| author | |
| committer | |
| log | 2456df5f4ebf9fef147d86736974e95403e2d40d |
| tree | f7d849a71434dceed2327e4b23a9592192e74ab8 |
| parent | 4d59f775289b50a9529e801a6998dcd181945efb |
20 files changed, 3330 insertions(+), 3331 deletions(-)
src-self-hosted/Compilation.zig+58-58| ... | @@ -16,7 +16,7 @@ const build_options = @import("build_options"); | ... | @@ -16,7 +16,7 @@ const build_options = @import("build_options"); |
| 16 | const LibCInstallation = @import("libc_installation.zig").LibCInstallation; | 16 | const LibCInstallation = @import("libc_installation.zig").LibCInstallation; |
| 17 | const glibc = @import("glibc.zig"); | 17 | const glibc = @import("glibc.zig"); |
| 18 | const fatal = @import("main.zig").fatal; | 18 | const fatal = @import("main.zig").fatal; |
| 19 | const ZigModule = @import("ZigModule.zig"); | 19 | const Module = @import("Module.zig"); |
| 20 | 20 | ||
| 21 | /// General-purpose allocator. Used for both temporary and long-term storage. | 21 | /// General-purpose allocator. Used for both temporary and long-term storage. |
| 22 | gpa: *Allocator, | 22 | gpa: *Allocator, |
| ... | @@ -75,7 +75,7 @@ crt_files: std.StringHashMapUnmanaged([]const u8) = .{}, | ... | @@ -75,7 +75,7 @@ crt_files: std.StringHashMapUnmanaged([]const u8) = .{}, |
| 75 | /// Keeping track of this possibly open resource so we can close it later. | 75 | /// Keeping track of this possibly open resource so we can close it later. |
| 76 | owned_link_dir: ?std.fs.Dir, | 76 | owned_link_dir: ?std.fs.Dir, |
| 77 | 77 | ||
| 78 | pub const InnerError = ZigModule.InnerError; | 78 | pub const InnerError = Module.InnerError; |
| 79 | 79 | ||
| 80 | /// For passing to a C compiler. | 80 | /// For passing to a C compiler. |
| 81 | pub const CSourceFile = struct { | 81 | pub const CSourceFile = struct { |
| ... | @@ -85,14 +85,14 @@ pub const CSourceFile = struct { | ... | @@ -85,14 +85,14 @@ pub const CSourceFile = struct { |
| 85 | 85 | ||
| 86 | const WorkItem = union(enum) { | 86 | const WorkItem = union(enum) { |
| 87 | /// Write the machine code for a Decl to the output file. | 87 | /// Write the machine code for a Decl to the output file. |
| 88 | codegen_decl: *ZigModule.Decl, | 88 | codegen_decl: *Module.Decl, |
| 89 | /// The Decl needs to be analyzed and possibly export itself. | 89 | /// The Decl needs to be analyzed and possibly export itself. |
| 90 | /// It may have already be analyzed, or it may have been determined | 90 | /// It may have already be analyzed, or it may have been determined |
| 91 | /// to be outdated; in this case perform semantic analysis again. | 91 | /// to be outdated; in this case perform semantic analysis again. |
| 92 | analyze_decl: *ZigModule.Decl, | 92 | analyze_decl: *Module.Decl, |
| 93 | /// The source file containing the Decl has been updated, and so the | 93 | /// The source file containing the Decl has been updated, and so the |
| 94 | /// Decl may need its line number information updated in the debug info. | 94 | /// Decl may need its line number information updated in the debug info. |
| 95 | update_line_number: *ZigModule.Decl, | 95 | update_line_number: *Module.Decl, |
| 96 | /// Invoke the Clang compiler to create an object file, which gets linked | 96 | /// Invoke the Clang compiler to create an object file, which gets linked |
| 97 | /// with the Compilation. | 97 | /// with the Compilation. |
| 98 | c_object: *CObject, | 98 | c_object: *CObject, |
| ... | @@ -402,7 +402,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { | ... | @@ -402,7 +402,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 402 | cache.hash.add(options.output_mode); | 402 | cache.hash.add(options.output_mode); |
| 403 | // TODO audit this and make sure everything is in it | 403 | // TODO audit this and make sure everything is in it |
| 404 | 404 | ||
| 405 | const zig_module: ?*ZigModule = if (options.root_pkg) |root_pkg| blk: { | 405 | const module: ?*Module = if (options.root_pkg) |root_pkg| blk: { |
| 406 | // Options that are specific to zig source files, that cannot be | 406 | // Options that are specific to zig source files, that cannot be |
| 407 | // modified between incremental updates. | 407 | // modified between incremental updates. |
| 408 | var hash = cache.hash; | 408 | var hash = cache.hash; |
| ... | @@ -442,11 +442,11 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { | ... | @@ -442,11 +442,11 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 442 | // this is where we would load it. We have open a handle to the directory where | 442 | // this is where we would load it. We have open a handle to the directory where |
| 443 | // the output either already is, or will be. | 443 | // the output either already is, or will be. |
| 444 | // However we currently do not have serialization of such metadata, so for now | 444 | // However we currently do not have serialization of such metadata, so for now |
| 445 | // we set up an empty ZigModule that does the entire compilation fresh. | 445 | // we set up an empty Module that does the entire compilation fresh. |
| 446 | 446 | ||
| 447 | const root_scope = rs: { | 447 | const root_scope = rs: { |
| 448 | if (mem.endsWith(u8, root_pkg.root_src_path, ".zig")) { | 448 | if (mem.endsWith(u8, root_pkg.root_src_path, ".zig")) { |
| 449 | const root_scope = try gpa.create(ZigModule.Scope.File); | 449 | const root_scope = try gpa.create(Module.Scope.File); |
| 450 | root_scope.* = .{ | 450 | root_scope.* = .{ |
| 451 | .sub_file_path = root_pkg.root_src_path, | 451 | .sub_file_path = root_pkg.root_src_path, |
| 452 | .source = .{ .unloaded = {} }, | 452 | .source = .{ .unloaded = {} }, |
| ... | @@ -459,7 +459,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { | ... | @@ -459,7 +459,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 459 | }; | 459 | }; |
| 460 | break :rs &root_scope.base; | 460 | break :rs &root_scope.base; |
| 461 | } else if (mem.endsWith(u8, root_pkg.root_src_path, ".zir")) { | 461 | } else if (mem.endsWith(u8, root_pkg.root_src_path, ".zir")) { |
| 462 | const root_scope = try gpa.create(ZigModule.Scope.ZIRModule); | 462 | const root_scope = try gpa.create(Module.Scope.ZIRModule); |
| 463 | root_scope.* = .{ | 463 | root_scope.* = .{ |
| 464 | .sub_file_path = root_pkg.root_src_path, | 464 | .sub_file_path = root_pkg.root_src_path, |
| 465 | .source = .{ .unloaded = {} }, | 465 | .source = .{ .unloaded = {} }, |
| ... | @@ -473,24 +473,24 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { | ... | @@ -473,24 +473,24 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 473 | } | 473 | } |
| 474 | }; | 474 | }; |
| 475 | 475 | ||
| 476 | const zig_module = try arena.create(ZigModule); | 476 | const module = try arena.create(Module); |
| 477 | zig_module.* = .{ | 477 | module.* = .{ |
| 478 | .gpa = gpa, | 478 | .gpa = gpa, |
| 479 | .comp = comp, | 479 | .comp = comp, |
| 480 | .root_pkg = root_pkg, | 480 | .root_pkg = root_pkg, |
| 481 | .root_scope = root_scope, | 481 | .root_scope = root_scope, |
| 482 | .zig_cache_artifact_directory = zig_cache_artifact_directory, | 482 | .zig_cache_artifact_directory = zig_cache_artifact_directory, |
| 483 | }; | 483 | }; |
| 484 | break :blk zig_module; | 484 | break :blk module; |
| 485 | } else null; | 485 | } else null; |
| 486 | errdefer if (zig_module) |zm| zm.deinit(); | 486 | errdefer if (module) |zm| zm.deinit(); |
| 487 | 487 | ||
| 488 | // For resource management purposes. | 488 | // For resource management purposes. |
| 489 | var owned_link_dir: ?std.fs.Dir = null; | 489 | var owned_link_dir: ?std.fs.Dir = null; |
| 490 | errdefer if (owned_link_dir) |*dir| dir.close(); | 490 | errdefer if (owned_link_dir) |*dir| dir.close(); |
| 491 | 491 | ||
| 492 | const bin_directory = emit_bin.directory orelse blk: { | 492 | const bin_directory = emit_bin.directory orelse blk: { |
| 493 | if (zig_module) |zm| break :blk zm.zig_cache_artifact_directory; | 493 | if (module) |zm| break :blk zm.zig_cache_artifact_directory; |
| 494 | 494 | ||
| 495 | const digest = cache.hash.peek(); | 495 | const digest = cache.hash.peek(); |
| 496 | const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); | 496 | const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); |
| ... | @@ -510,7 +510,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { | ... | @@ -510,7 +510,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 510 | .directory = bin_directory, | 510 | .directory = bin_directory, |
| 511 | .sub_path = emit_bin.basename, | 511 | .sub_path = emit_bin.basename, |
| 512 | .root_name = root_name, | 512 | .root_name = root_name, |
| 513 | .zig_module = zig_module, | 513 | .module = module, |
| 514 | .target = options.target, | 514 | .target = options.target, |
| 515 | .dynamic_linker = options.dynamic_linker, | 515 | .dynamic_linker = options.dynamic_linker, |
| 516 | .output_mode = options.output_mode, | 516 | .output_mode = options.output_mode, |
| ... | @@ -605,9 +605,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { | ... | @@ -605,9 +605,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 605 | } | 605 | } |
| 606 | 606 | ||
| 607 | pub fn destroy(self: *Compilation) void { | 607 | pub fn destroy(self: *Compilation) void { |
| 608 | const optional_zig_module = self.bin_file.options.zig_module; | 608 | const optional_module = self.bin_file.options.module; |
| 609 | self.bin_file.destroy(); | 609 | self.bin_file.destroy(); |
| 610 | if (optional_zig_module) |zig_module| zig_module.deinit(); | 610 | if (optional_module) |module| module.deinit(); |
| 611 | 611 | ||
| 612 | const gpa = self.gpa; | 612 | const gpa = self.gpa; |
| 613 | self.work_queue.deinit(); | 613 | self.work_queue.deinit(); |
| ... | @@ -655,23 +655,23 @@ pub fn update(self: *Compilation) !void { | ... | @@ -655,23 +655,23 @@ pub fn update(self: *Compilation) !void { |
| 655 | self.work_queue.writeItemAssumeCapacity(.{ .c_object = entry.key }); | 655 | self.work_queue.writeItemAssumeCapacity(.{ .c_object = entry.key }); |
| 656 | } | 656 | } |
| 657 | 657 | ||
| 658 | if (self.bin_file.options.zig_module) |zig_module| { | 658 | if (self.bin_file.options.module) |module| { |
| 659 | zig_module.generation += 1; | 659 | module.generation += 1; |
| 660 | 660 | ||
| 661 | // TODO Detect which source files changed. | 661 | // TODO Detect which source files changed. |
| 662 | // Until then we simulate a full cache miss. Source files could have been loaded for any reason; | 662 | // Until then we simulate a full cache miss. Source files could have been loaded for any reason; |
| 663 | // to force a refresh we unload now. | 663 | // to force a refresh we unload now. |
| 664 | if (zig_module.root_scope.cast(ZigModule.Scope.File)) |zig_file| { | 664 | if (module.root_scope.cast(Module.Scope.File)) |zig_file| { |
| 665 | zig_file.unload(zig_module.gpa); | 665 | zig_file.unload(module.gpa); |
| 666 | zig_module.analyzeContainer(&zig_file.root_container) catch |err| switch (err) { | 666 | module.analyzeContainer(&zig_file.root_container) catch |err| switch (err) { |
| 667 | error.AnalysisFail => { | 667 | error.AnalysisFail => { |
| 668 | assert(self.totalErrorCount() != 0); | 668 | assert(self.totalErrorCount() != 0); |
| 669 | }, | 669 | }, |
| 670 | else => |e| return e, | 670 | else => |e| return e, |
| 671 | }; | 671 | }; |
| 672 | } else if (zig_module.root_scope.cast(ZigModule.Scope.ZIRModule)) |zir_module| { | 672 | } else if (module.root_scope.cast(Module.Scope.ZIRModule)) |zir_module| { |
| 673 | zir_module.unload(zig_module.gpa); | 673 | zir_module.unload(module.gpa); |
| 674 | zig_module.analyzeRootZIRModule(zir_module) catch |err| switch (err) { | 674 | module.analyzeRootZIRModule(zir_module) catch |err| switch (err) { |
| 675 | error.AnalysisFail => { | 675 | error.AnalysisFail => { |
| 676 | assert(self.totalErrorCount() != 0); | 676 | assert(self.totalErrorCount() != 0); |
| 677 | }, | 677 | }, |
| ... | @@ -682,14 +682,14 @@ pub fn update(self: *Compilation) !void { | ... | @@ -682,14 +682,14 @@ pub fn update(self: *Compilation) !void { |
| 682 | 682 | ||
| 683 | try self.performAllTheWork(); | 683 | try self.performAllTheWork(); |
| 684 | 684 | ||
| 685 | if (self.bin_file.options.zig_module) |zig_module| { | 685 | if (self.bin_file.options.module) |module| { |
| 686 | // Process the deletion set. | 686 | // Process the deletion set. |
| 687 | while (zig_module.deletion_set.popOrNull()) |decl| { | 687 | while (module.deletion_set.popOrNull()) |decl| { |
| 688 | if (decl.dependants.items().len != 0) { | 688 | if (decl.dependants.items().len != 0) { |
| 689 | decl.deletion_flag = false; | 689 | decl.deletion_flag = false; |
| 690 | continue; | 690 | continue; |
| 691 | } | 691 | } |
| 692 | try zig_module.deleteDecl(decl); | 692 | try module.deleteDecl(decl); |
| 693 | } | 693 | } |
| 694 | } | 694 | } |
| 695 | 695 | ||
| ... | @@ -701,8 +701,8 @@ pub fn update(self: *Compilation) !void { | ... | @@ -701,8 +701,8 @@ pub fn update(self: *Compilation) !void { |
| 701 | // If there are any errors, we anticipate the source files being loaded | 701 | // If there are any errors, we anticipate the source files being loaded |
| 702 | // to report error messages. Otherwise we unload all source files to save memory. | 702 | // to report error messages. Otherwise we unload all source files to save memory. |
| 703 | if (self.totalErrorCount() == 0 and !self.keep_source_files_loaded) { | 703 | if (self.totalErrorCount() == 0 and !self.keep_source_files_loaded) { |
| 704 | if (self.bin_file.options.zig_module) |zig_module| { | 704 | if (self.bin_file.options.module) |module| { |
| 705 | zig_module.root_scope.unload(self.gpa); | 705 | module.root_scope.unload(self.gpa); |
| 706 | } | 706 | } |
| 707 | } | 707 | } |
| 708 | } | 708 | } |
| ... | @@ -722,10 +722,10 @@ pub fn makeBinFileWritable(self: *Compilation) !void { | ... | @@ -722,10 +722,10 @@ pub fn makeBinFileWritable(self: *Compilation) !void { |
| 722 | pub fn totalErrorCount(self: *Compilation) usize { | 722 | pub fn totalErrorCount(self: *Compilation) usize { |
| 723 | var total: usize = self.failed_c_objects.items().len; | 723 | var total: usize = self.failed_c_objects.items().len; |
| 724 | 724 | ||
| 725 | if (self.bin_file.options.zig_module) |zig_module| { | 725 | if (self.bin_file.options.module) |module| { |
| 726 | total += zig_module.failed_decls.items().len + | 726 | total += module.failed_decls.items().len + |
| 727 | zig_module.failed_exports.items().len + | 727 | module.failed_exports.items().len + |
| 728 | zig_module.failed_files.items().len; | 728 | module.failed_files.items().len; |
| 729 | } | 729 | } |
| 730 | 730 | ||
| 731 | // The "no entry point found" error only counts if there are no other errors. | 731 | // The "no entry point found" error only counts if there are no other errors. |
| ... | @@ -748,30 +748,30 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { | ... | @@ -748,30 +748,30 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { |
| 748 | const err_msg = entry.value; | 748 | const err_msg = entry.value; |
| 749 | try AllErrors.add(&arena, &errors, c_object.src_path, "", err_msg.*); | 749 | try AllErrors.add(&arena, &errors, c_object.src_path, "", err_msg.*); |
| 750 | } | 750 | } |
| 751 | if (self.bin_file.options.zig_module) |zig_module| { | 751 | if (self.bin_file.options.module) |module| { |
| 752 | for (zig_module.failed_files.items()) |entry| { | 752 | for (module.failed_files.items()) |entry| { |
| 753 | const scope = entry.key; | 753 | const scope = entry.key; |
| 754 | const err_msg = entry.value; | 754 | const err_msg = entry.value; |
| 755 | const source = try scope.getSource(zig_module); | 755 | const source = try scope.getSource(module); |
| 756 | try AllErrors.add(&arena, &errors, scope.subFilePath(), source, err_msg.*); | 756 | try AllErrors.add(&arena, &errors, scope.subFilePath(), source, err_msg.*); |
| 757 | } | 757 | } |
| 758 | for (zig_module.failed_decls.items()) |entry| { | 758 | for (module.failed_decls.items()) |entry| { |
| 759 | const decl = entry.key; | 759 | const decl = entry.key; |
| 760 | const err_msg = entry.value; | 760 | const err_msg = entry.value; |
| 761 | const source = try decl.scope.getSource(zig_module); | 761 | const source = try decl.scope.getSource(module); |
| 762 | try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); | 762 | try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); |
| 763 | } | 763 | } |
| 764 | for (zig_module.failed_exports.items()) |entry| { | 764 | for (module.failed_exports.items()) |entry| { |
| 765 | const decl = entry.key.owner_decl; | 765 | const decl = entry.key.owner_decl; |
| 766 | const err_msg = entry.value; | 766 | const err_msg = entry.value; |
| 767 | const source = try decl.scope.getSource(zig_module); | 767 | const source = try decl.scope.getSource(module); |
| 768 | try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); | 768 | try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); |
| 769 | } | 769 | } |
| 770 | } | 770 | } |
| 771 | 771 | ||
| 772 | if (errors.items.len == 0 and self.link_error_flags.no_entry_point_found) { | 772 | if (errors.items.len == 0 and self.link_error_flags.no_entry_point_found) { |
| 773 | const global_err_src_path = blk: { | 773 | const global_err_src_path = blk: { |
| 774 | if (self.bin_file.options.zig_module) |zig_module| break :blk zig_module.root_pkg.root_src_path; | 774 | if (self.bin_file.options.module) |module| break :blk module.root_pkg.root_src_path; |
| 775 | if (self.c_source_files.len != 0) break :blk self.c_source_files[0].src_path; | 775 | if (self.c_source_files.len != 0) break :blk self.c_source_files[0].src_path; |
| 776 | if (self.bin_file.options.objects.len != 0) break :blk self.bin_file.options.objects[0]; | 776 | if (self.bin_file.options.objects.len != 0) break :blk self.bin_file.options.objects[0]; |
| 777 | break :blk "(no file)"; | 777 | break :blk "(no file)"; |
| ... | @@ -807,10 +807,10 @@ pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void { | ... | @@ -807,10 +807,10 @@ pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void { |
| 807 | => continue, | 807 | => continue, |
| 808 | 808 | ||
| 809 | .complete, .codegen_failure_retryable => { | 809 | .complete, .codegen_failure_retryable => { |
| 810 | const zig_module = self.bin_file.options.zig_module.?; | 810 | const module = self.bin_file.options.module.?; |
| 811 | if (decl.typed_value.most_recent.typed_value.val.cast(Value.Payload.Function)) |payload| { | 811 | if (decl.typed_value.most_recent.typed_value.val.cast(Value.Payload.Function)) |payload| { |
| 812 | switch (payload.func.analysis) { | 812 | switch (payload.func.analysis) { |
| 813 | .queued => zig_module.analyzeFnBody(decl, payload.func) catch |err| switch (err) { | 813 | .queued => module.analyzeFnBody(decl, payload.func) catch |err| switch (err) { |
| 814 | error.AnalysisFail => { | 814 | error.AnalysisFail => { |
| 815 | assert(payload.func.analysis != .in_progress); | 815 | assert(payload.func.analysis != .in_progress); |
| 816 | continue; | 816 | continue; |
| ... | @@ -823,23 +823,23 @@ pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void { | ... | @@ -823,23 +823,23 @@ pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void { |
| 823 | } | 823 | } |
| 824 | // Here we tack on additional allocations to the Decl's arena. The allocations are | 824 | // Here we tack on additional allocations to the Decl's arena. The allocations are |
| 825 | // lifetime annotations in the ZIR. | 825 | // lifetime annotations in the ZIR. |
| 826 | var decl_arena = decl.typed_value.most_recent.arena.?.promote(zig_module.gpa); | 826 | var decl_arena = decl.typed_value.most_recent.arena.?.promote(module.gpa); |
| 827 | defer decl.typed_value.most_recent.arena.?.* = decl_arena.state; | 827 | defer decl.typed_value.most_recent.arena.?.* = decl_arena.state; |
| 828 | log.debug("analyze liveness of {}\n", .{decl.name}); | 828 | log.debug("analyze liveness of {}\n", .{decl.name}); |
| 829 | try liveness.analyze(zig_module.gpa, &decl_arena.allocator, payload.func.analysis.success); | 829 | try liveness.analyze(module.gpa, &decl_arena.allocator, payload.func.analysis.success); |
| 830 | } | 830 | } |
| 831 | 831 | ||
| 832 | assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits()); | 832 | assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits()); |
| 833 | 833 | ||
| 834 | self.bin_file.updateDecl(zig_module, decl) catch |err| switch (err) { | 834 | self.bin_file.updateDecl(module, decl) catch |err| switch (err) { |
| 835 | error.OutOfMemory => return error.OutOfMemory, | 835 | error.OutOfMemory => return error.OutOfMemory, |
| 836 | error.AnalysisFail => { | 836 | error.AnalysisFail => { |
| 837 | decl.analysis = .dependency_failure; | 837 | decl.analysis = .dependency_failure; |
| 838 | }, | 838 | }, |
| 839 | else => { | 839 | else => { |
| 840 | try zig_module.failed_decls.ensureCapacity(zig_module.gpa, zig_module.failed_decls.items().len + 1); | 840 | try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1); |
| 841 | zig_module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( | 841 | module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( |
| 842 | zig_module.gpa, | 842 | module.gpa, |
| 843 | decl.src(), | 843 | decl.src(), |
| 844 | "unable to codegen: {}", | 844 | "unable to codegen: {}", |
| 845 | .{@errorName(err)}, | 845 | .{@errorName(err)}, |
| ... | @@ -850,18 +850,18 @@ pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void { | ... | @@ -850,18 +850,18 @@ pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void { |
| 850 | }, | 850 | }, |
| 851 | }, | 851 | }, |
| 852 | .analyze_decl => |decl| { | 852 | .analyze_decl => |decl| { |
| 853 | const zig_module = self.bin_file.options.zig_module.?; | 853 | const module = self.bin_file.options.module.?; |
| 854 | zig_module.ensureDeclAnalyzed(decl) catch |err| switch (err) { | 854 | module.ensureDeclAnalyzed(decl) catch |err| switch (err) { |
| 855 | error.OutOfMemory => return error.OutOfMemory, | 855 | error.OutOfMemory => return error.OutOfMemory, |
| 856 | error.AnalysisFail => continue, | 856 | error.AnalysisFail => continue, |
| 857 | }; | 857 | }; |
| 858 | }, | 858 | }, |
| 859 | .update_line_number => |decl| { | 859 | .update_line_number => |decl| { |
| 860 | const zig_module = self.bin_file.options.zig_module.?; | 860 | const module = self.bin_file.options.module.?; |
| 861 | self.bin_file.updateDeclLineNumber(zig_module, decl) catch |err| { | 861 | self.bin_file.updateDeclLineNumber(module, decl) catch |err| { |
| 862 | try zig_module.failed_decls.ensureCapacity(zig_module.gpa, zig_module.failed_decls.items().len + 1); | 862 | try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1); |
| 863 | zig_module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( | 863 | module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( |
| 864 | zig_module.gpa, | 864 | module.gpa, |
| 865 | decl.src(), | 865 | decl.src(), |
| 866 | "unable to update line number: {}", | 866 | "unable to update line number: {}", |
| 867 | .{@errorName(err)}, | 867 | .{@errorName(err)}, |
| ... | @@ -949,7 +949,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject) !void { | ... | @@ -949,7 +949,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject) !void { |
| 949 | // Special case when doing build-obj for just one C file. When there are more than one object | 949 | // Special case when doing build-obj for just one C file. When there are more than one object |
| 950 | // file and building an object we need to link them together, but with just one it should go | 950 | // file and building an object we need to link them together, but with just one it should go |
| 951 | // directly to the output file. | 951 | // directly to the output file. |
| 952 | const direct_o = comp.c_source_files.len == 1 and comp.bin_file.options.zig_module == null and | 952 | const direct_o = comp.c_source_files.len == 1 and comp.bin_file.options.module == null and |
| 953 | comp.bin_file.options.output_mode == .Obj and comp.bin_file.options.objects.len == 0; | 953 | comp.bin_file.options.output_mode == .Obj and comp.bin_file.options.objects.len == 0; |
| 954 | const o_basename_noext = if (direct_o) | 954 | const o_basename_noext = if (direct_o) |
| 955 | comp.bin_file.options.root_name | 955 | comp.bin_file.options.root_name |
src-self-hosted/Module.zig created+3235| ... | @@ -0,0 +1,3235 @@ | ||
| 1 | const Module = @This(); | ||
| 2 | const std = @import("std"); | ||
| 3 | const Compilation = @import("Compilation.zig"); | ||
| 4 | const mem = std.mem; | ||
| 5 | const Allocator = std.mem.Allocator; | ||
| 6 | const ArrayListUnmanaged = std.ArrayListUnmanaged; | ||
| 7 | const Value = @import("value.zig").Value; | ||
| 8 | const Type = @import("type.zig").Type; | ||
| 9 | const TypedValue = @import("TypedValue.zig"); | ||
| 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 Package = @import("Package.zig"); | ||
| 16 | const link = @import("link.zig"); | ||
| 17 | const ir = @import("ir.zig"); | ||
| 18 | const zir = @import("zir.zig"); | ||
| 19 | const Inst = ir.Inst; | ||
| 20 | const Body = ir.Body; | ||
| 21 | const ast = std.zig.ast; | ||
| 22 | const trace = @import("tracy.zig").trace; | ||
| 23 | const astgen = @import("astgen.zig"); | ||
| 24 | const zir_sema = @import("zir_sema.zig"); | ||
| 25 | |||
| 26 | /// General-purpose allocator. Used for both temporary and long-term storage. | ||
| 27 | gpa: *Allocator, | ||
| 28 | comp: *Compilation, | ||
| 29 | |||
| 30 | /// Where our incremental compilation metadata serialization will go. | ||
| 31 | zig_cache_artifact_directory: Compilation.Directory, | ||
| 32 | /// Pointer to externally managed resource. `null` if there is no zig file being compiled. | ||
| 33 | root_pkg: *Package, | ||
| 34 | /// Module owns this resource. | ||
| 35 | /// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`. | ||
| 36 | root_scope: *Scope, | ||
| 37 | /// It's rare for a decl to be exported, so we save memory by having a sparse map of | ||
| 38 | /// Decl pointers to details about them being exported. | ||
| 39 | /// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table. | ||
| 40 | decl_exports: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{}, | ||
| 41 | /// We track which export is associated with the given symbol name for quick | ||
| 42 | /// detection of symbol collisions. | ||
| 43 | symbol_exports: std.StringArrayHashMapUnmanaged(*Export) = .{}, | ||
| 44 | /// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl | ||
| 45 | /// is modified. Note that the key of this table is not the Decl being exported, but the Decl that | ||
| 46 | /// is performing the export of another Decl. | ||
| 47 | /// This table owns the Export memory. | ||
| 48 | export_owners: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{}, | ||
| 49 | /// Maps fully qualified namespaced names to the Decl struct for them. | ||
| 50 | decl_table: std.ArrayHashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{}, | ||
| 51 | /// We optimize memory usage for a compilation with no compile errors by storing the | ||
| 52 | /// error messages and mapping outside of `Decl`. | ||
| 53 | /// The ErrorMsg memory is owned by the decl, using Module's general purpose allocator. | ||
| 54 | /// Note that a Decl can succeed but the Fn it represents can fail. In this case, | ||
| 55 | /// a Decl can have a failed_decls entry but have analysis status of success. | ||
| 56 | failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *Compilation.ErrorMsg) = .{}, | ||
| 57 | /// Using a map here for consistency with the other fields here. | ||
| 58 | /// The ErrorMsg memory is owned by the `Scope`, using Module's general purpose allocator. | ||
| 59 | failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *Compilation.ErrorMsg) = .{}, | ||
| 60 | /// Using a map here for consistency with the other fields here. | ||
| 61 | /// The ErrorMsg memory is owned by the `Export`, using Module's general purpose allocator. | ||
| 62 | failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *Compilation.ErrorMsg) = .{}, | ||
| 63 | |||
| 64 | next_anon_name_index: usize = 0, | ||
| 65 | |||
| 66 | /// Candidates for deletion. After a semantic analysis update completes, this list | ||
| 67 | /// contains Decls that need to be deleted if they end up having no references to them. | ||
| 68 | deletion_set: ArrayListUnmanaged(*Decl) = .{}, | ||
| 69 | |||
| 70 | /// Error tags and their values, tag names are duped with mod.gpa. | ||
| 71 | global_error_set: std.StringHashMapUnmanaged(u16) = .{}, | ||
| 72 | |||
| 73 | /// Incrementing integer used to compare against the corresponding Decl | ||
| 74 | /// field to determine whether a Decl's status applies to an ongoing update, or a | ||
| 75 | /// previous analysis. | ||
| 76 | generation: u32 = 0, | ||
| 77 | |||
| 78 | pub const Export = struct { | ||
| 79 | options: std.builtin.ExportOptions, | ||
| 80 | /// Byte offset into the file that contains the export directive. | ||
| 81 | src: usize, | ||
| 82 | /// Represents the position of the export, if any, in the output file. | ||
| 83 | link: link.File.Elf.Export, | ||
| 84 | /// The Decl that performs the export. Note that this is *not* the Decl being exported. | ||
| 85 | owner_decl: *Decl, | ||
| 86 | /// The Decl being exported. Note this is *not* the Decl performing the export. | ||
| 87 | exported_decl: *Decl, | ||
| 88 | status: enum { | ||
| 89 | in_progress, | ||
| 90 | failed, | ||
| 91 | /// Indicates that the failure was due to a temporary issue, such as an I/O error | ||
| 92 | /// when writing to the output file. Retrying the export may succeed. | ||
| 93 | failed_retryable, | ||
| 94 | complete, | ||
| 95 | }, | ||
| 96 | }; | ||
| 97 | |||
| 98 | pub const Decl = struct { | ||
| 99 | /// This name is relative to the containing namespace of the decl. It uses a null-termination | ||
| 100 | /// to save bytes, since there can be a lot of decls in a compilation. The null byte is not allowed | ||
| 101 | /// in symbol names, because executable file formats use null-terminated strings for symbol names. | ||
| 102 | /// All Decls have names, even values that are not bound to a zig namespace. This is necessary for | ||
| 103 | /// mapping them to an address in the output file. | ||
| 104 | /// Memory owned by this decl, using Module's allocator. | ||
| 105 | name: [*:0]const u8, | ||
| 106 | /// The direct parent container of the Decl. This is either a `Scope.Container` or `Scope.ZIRModule`. | ||
| 107 | /// Reference to externally owned memory. | ||
| 108 | scope: *Scope, | ||
| 109 | /// The AST Node decl index or ZIR Inst index that contains this declaration. | ||
| 110 | /// Must be recomputed when the corresponding source file is modified. | ||
| 111 | src_index: usize, | ||
| 112 | /// The most recent value of the Decl after a successful semantic analysis. | ||
| 113 | typed_value: union(enum) { | ||
| 114 | never_succeeded: void, | ||
| 115 | most_recent: TypedValue.Managed, | ||
| 116 | }, | ||
| 117 | /// Represents the "shallow" analysis status. For example, for decls that are functions, | ||
| 118 | /// the function type is analyzed with this set to `in_progress`, however, the semantic | ||
| 119 | /// analysis of the function body is performed with this value set to `success`. Functions | ||
| 120 | /// have their own analysis status field. | ||
| 121 | analysis: enum { | ||
| 122 | /// This Decl corresponds to an AST Node that has not been referenced yet, and therefore | ||
| 123 | /// because of Zig's lazy declaration analysis, it will remain unanalyzed until referenced. | ||
| 124 | unreferenced, | ||
| 125 | /// Semantic analysis for this Decl is running right now. This state detects dependency loops. | ||
| 126 | in_progress, | ||
| 127 | /// This Decl might be OK but it depends on another one which did not successfully complete | ||
| 128 | /// semantic analysis. | ||
| 129 | dependency_failure, | ||
| 130 | /// Semantic analysis failure. | ||
| 131 | /// There will be a corresponding ErrorMsg in Module.failed_decls. | ||
| 132 | sema_failure, | ||
| 133 | /// There will be a corresponding ErrorMsg in Module.failed_decls. | ||
| 134 | /// This indicates the failure was something like running out of disk space, | ||
| 135 | /// and attempting semantic analysis again may succeed. | ||
| 136 | sema_failure_retryable, | ||
| 137 | /// There will be a corresponding ErrorMsg in Module.failed_decls. | ||
| 138 | codegen_failure, | ||
| 139 | /// There will be a corresponding ErrorMsg in Module.failed_decls. | ||
| 140 | /// This indicates the failure was something like running out of disk space, | ||
| 141 | /// and attempting codegen again may succeed. | ||
| 142 | codegen_failure_retryable, | ||
| 143 | /// Everything is done. During an update, this Decl may be out of date, depending | ||
| 144 | /// on its dependencies. The `generation` field can be used to determine if this | ||
| 145 | /// completion status occurred before or after a given update. | ||
| 146 | complete, | ||
| 147 | /// A Module update is in progress, and this Decl has been flagged as being known | ||
| 148 | /// to require re-analysis. | ||
| 149 | outdated, | ||
| 150 | }, | ||
| 151 | /// This flag is set when this Decl is added to a check_for_deletion set, and cleared | ||
| 152 | /// when removed. | ||
| 153 | deletion_flag: bool, | ||
| 154 | /// Whether the corresponding AST decl has a `pub` keyword. | ||
| 155 | is_pub: bool, | ||
| 156 | |||
| 157 | /// An integer that can be checked against the corresponding incrementing | ||
| 158 | /// generation field of Module. This is used to determine whether `complete` status | ||
| 159 | /// represents pre- or post- re-analysis. | ||
| 160 | generation: u32, | ||
| 161 | |||
| 162 | /// Represents the position of the code in the output file. | ||
| 163 | /// This is populated regardless of semantic analysis and code generation. | ||
| 164 | link: link.File.LinkBlock, | ||
| 165 | |||
| 166 | /// Represents the function in the linked output file, if the `Decl` is a function. | ||
| 167 | /// This is stored here and not in `Fn` because `Decl` survives across updates but | ||
| 168 | /// `Fn` does not. | ||
| 169 | /// TODO Look into making `Fn` a longer lived structure and moving this field there | ||
| 170 | /// to save on memory usage. | ||
| 171 | fn_link: link.File.LinkFn, | ||
| 172 | |||
| 173 | contents_hash: std.zig.SrcHash, | ||
| 174 | |||
| 175 | /// The shallow set of other decls whose typed_value could possibly change if this Decl's | ||
| 176 | /// typed_value is modified. | ||
| 177 | dependants: DepsTable = .{}, | ||
| 178 | /// The shallow set of other decls whose typed_value changing indicates that this Decl's | ||
| 179 | /// typed_value may need to be regenerated. | ||
| 180 | dependencies: DepsTable = .{}, | ||
| 181 | |||
| 182 | /// The reason this is not `std.AutoArrayHashMapUnmanaged` is a workaround for | ||
| 183 | /// stage1 compiler giving me: `error: struct 'Module.Decl' depends on itself` | ||
| 184 | pub const DepsTable = std.ArrayHashMapUnmanaged(*Decl, void, std.array_hash_map.getAutoHashFn(*Decl), std.array_hash_map.getAutoEqlFn(*Decl), false); | ||
| 185 | |||
| 186 | pub fn destroy(self: *Decl, gpa: *Allocator) void { | ||
| 187 | gpa.free(mem.spanZ(self.name)); | ||
| 188 | if (self.typedValueManaged()) |tvm| { | ||
| 189 | tvm.deinit(gpa); | ||
| 190 | } | ||
| 191 | self.dependants.deinit(gpa); | ||
| 192 | self.dependencies.deinit(gpa); | ||
| 193 | gpa.destroy(self); | ||
| 194 | } | ||
| 195 | |||
| 196 | pub fn src(self: Decl) usize { | ||
| 197 | switch (self.scope.tag) { | ||
| 198 | .container => { | ||
| 199 | const container = @fieldParentPtr(Scope.Container, "base", self.scope); | ||
| 200 | const tree = container.file_scope.contents.tree; | ||
| 201 | // TODO Container should have it's own decls() | ||
| 202 | const decl_node = tree.root_node.decls()[self.src_index]; | ||
| 203 | return tree.token_locs[decl_node.firstToken()].start; | ||
| 204 | }, | ||
| 205 | .zir_module => { | ||
| 206 | const zir_module = @fieldParentPtr(Scope.ZIRModule, "base", self.scope); | ||
| 207 | const module = zir_module.contents.module; | ||
| 208 | const src_decl = module.decls[self.src_index]; | ||
| 209 | return src_decl.inst.src; | ||
| 210 | }, | ||
| 211 | .file, .block => unreachable, | ||
| 212 | .gen_zir => unreachable, | ||
| 213 | .local_val => unreachable, | ||
| 214 | .local_ptr => unreachable, | ||
| 215 | .decl => unreachable, | ||
| 216 | } | ||
| 217 | } | ||
| 218 | |||
| 219 | pub fn fullyQualifiedNameHash(self: Decl) Scope.NameHash { | ||
| 220 | return self.scope.fullyQualifiedNameHash(mem.spanZ(self.name)); | ||
| 221 | } | ||
| 222 | |||
| 223 | pub fn typedValue(self: *Decl) error{AnalysisFail}!TypedValue { | ||
| 224 | const tvm = self.typedValueManaged() orelse return error.AnalysisFail; | ||
| 225 | return tvm.typed_value; | ||
| 226 | } | ||
| 227 | |||
| 228 | pub fn value(self: *Decl) error{AnalysisFail}!Value { | ||
| 229 | return (try self.typedValue()).val; | ||
| 230 | } | ||
| 231 | |||
| 232 | pub fn dump(self: *Decl) void { | ||
| 233 | const loc = std.zig.findLineColumn(self.scope.source.bytes, self.src); | ||
| 234 | std.debug.print("{}:{}:{} name={} status={}", .{ | ||
| 235 | self.scope.sub_file_path, | ||
| 236 | loc.line + 1, | ||
| 237 | loc.column + 1, | ||
| 238 | mem.spanZ(self.name), | ||
| 239 | @tagName(self.analysis), | ||
| 240 | }); | ||
| 241 | if (self.typedValueManaged()) |tvm| { | ||
| 242 | std.debug.print(" ty={} val={}", .{ tvm.typed_value.ty, tvm.typed_value.val }); | ||
| 243 | } | ||
| 244 | std.debug.print("\n", .{}); | ||
| 245 | } | ||
| 246 | |||
| 247 | pub fn typedValueManaged(self: *Decl) ?*TypedValue.Managed { | ||
| 248 | switch (self.typed_value) { | ||
| 249 | .most_recent => |*x| return x, | ||
| 250 | .never_succeeded => return null, | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 254 | fn removeDependant(self: *Decl, other: *Decl) void { | ||
| 255 | self.dependants.removeAssertDiscard(other); | ||
| 256 | } | ||
| 257 | |||
| 258 | fn removeDependency(self: *Decl, other: *Decl) void { | ||
| 259 | self.dependencies.removeAssertDiscard(other); | ||
| 260 | } | ||
| 261 | }; | ||
| 262 | |||
| 263 | /// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator. | ||
| 264 | pub const Fn = struct { | ||
| 265 | /// This memory owned by the Decl's TypedValue.Managed arena allocator. | ||
| 266 | analysis: union(enum) { | ||
| 267 | queued: *ZIR, | ||
| 268 | in_progress, | ||
| 269 | /// There will be a corresponding ErrorMsg in Module.failed_decls | ||
| 270 | sema_failure, | ||
| 271 | /// This Fn might be OK but it depends on another Decl which did not successfully complete | ||
| 272 | /// semantic analysis. | ||
| 273 | dependency_failure, | ||
| 274 | success: Body, | ||
| 275 | }, | ||
| 276 | owner_decl: *Decl, | ||
| 277 | |||
| 278 | /// This memory is temporary and points to stack memory for the duration | ||
| 279 | /// of Fn analysis. | ||
| 280 | pub const Analysis = struct { | ||
| 281 | inner_block: Scope.Block, | ||
| 282 | }; | ||
| 283 | |||
| 284 | /// Contains un-analyzed ZIR instructions generated from Zig source AST. | ||
| 285 | pub const ZIR = struct { | ||
| 286 | body: zir.Module.Body, | ||
| 287 | arena: std.heap.ArenaAllocator.State, | ||
| 288 | }; | ||
| 289 | |||
| 290 | /// For debugging purposes. | ||
| 291 | pub fn dump(self: *Fn, mod: Module) void { | ||
| 292 | std.debug.print("Module.Function(name={}) ", .{self.owner_decl.name}); | ||
| 293 | switch (self.analysis) { | ||
| 294 | .queued => { | ||
| 295 | std.debug.print("queued\n", .{}); | ||
| 296 | }, | ||
| 297 | .in_progress => { | ||
| 298 | std.debug.print("in_progress\n", .{}); | ||
| 299 | }, | ||
| 300 | else => { | ||
| 301 | std.debug.print("\n", .{}); | ||
| 302 | zir.dumpFn(mod, self); | ||
| 303 | }, | ||
| 304 | } | ||
| 305 | } | ||
| 306 | }; | ||
| 307 | |||
| 308 | pub const Var = struct { | ||
| 309 | /// if is_extern == true this is undefined | ||
| 310 | init: Value, | ||
| 311 | owner_decl: *Decl, | ||
| 312 | |||
| 313 | is_extern: bool, | ||
| 314 | is_mutable: bool, | ||
| 315 | is_threadlocal: bool, | ||
| 316 | }; | ||
| 317 | |||
| 318 | pub const Scope = struct { | ||
| 319 | tag: Tag, | ||
| 320 | |||
| 321 | pub const NameHash = [16]u8; | ||
| 322 | |||
| 323 | pub fn cast(base: *Scope, comptime T: type) ?*T { | ||
| 324 | if (base.tag != T.base_tag) | ||
| 325 | return null; | ||
| 326 | |||
| 327 | return @fieldParentPtr(T, "base", base); | ||
| 328 | } | ||
| 329 | |||
| 330 | /// Asserts the scope has a parent which is a DeclAnalysis and | ||
| 331 | /// returns the arena Allocator. | ||
| 332 | pub fn arena(self: *Scope) *Allocator { | ||
| 333 | switch (self.tag) { | ||
| 334 | .block => return self.cast(Block).?.arena, | ||
| 335 | .decl => return &self.cast(DeclAnalysis).?.arena.allocator, | ||
| 336 | .gen_zir => return self.cast(GenZIR).?.arena, | ||
| 337 | .local_val => return self.cast(LocalVal).?.gen_zir.arena, | ||
| 338 | .local_ptr => return self.cast(LocalPtr).?.gen_zir.arena, | ||
| 339 | .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator, | ||
| 340 | .file => unreachable, | ||
| 341 | .container => unreachable, | ||
| 342 | } | ||
| 343 | } | ||
| 344 | |||
| 345 | /// If the scope has a parent which is a `DeclAnalysis`, | ||
| 346 | /// returns the `Decl`, otherwise returns `null`. | ||
| 347 | pub fn decl(self: *Scope) ?*Decl { | ||
| 348 | return switch (self.tag) { | ||
| 349 | .block => self.cast(Block).?.decl, | ||
| 350 | .gen_zir => self.cast(GenZIR).?.decl, | ||
| 351 | .local_val => self.cast(LocalVal).?.gen_zir.decl, | ||
| 352 | .local_ptr => self.cast(LocalPtr).?.gen_zir.decl, | ||
| 353 | .decl => self.cast(DeclAnalysis).?.decl, | ||
| 354 | .zir_module => null, | ||
| 355 | .file => null, | ||
| 356 | .container => null, | ||
| 357 | }; | ||
| 358 | } | ||
| 359 | |||
| 360 | /// Asserts the scope has a parent which is a ZIRModule or Container and | ||
| 361 | /// returns it. | ||
| 362 | pub fn namespace(self: *Scope) *Scope { | ||
| 363 | switch (self.tag) { | ||
| 364 | .block => return self.cast(Block).?.decl.scope, | ||
| 365 | .gen_zir => return self.cast(GenZIR).?.decl.scope, | ||
| 366 | .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope, | ||
| 367 | .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope, | ||
| 368 | .decl => return self.cast(DeclAnalysis).?.decl.scope, | ||
| 369 | .file => return &self.cast(File).?.root_container.base, | ||
| 370 | .zir_module, .container => return self, | ||
| 371 | } | ||
| 372 | } | ||
| 373 | |||
| 374 | /// Must generate unique bytes with no collisions with other decls. | ||
| 375 | /// The point of hashing here is only to limit the number of bytes of | ||
| 376 | /// the unique identifier to a fixed size (16 bytes). | ||
| 377 | pub fn fullyQualifiedNameHash(self: *Scope, name: []const u8) NameHash { | ||
| 378 | switch (self.tag) { | ||
| 379 | .block => unreachable, | ||
| 380 | .gen_zir => unreachable, | ||
| 381 | .local_val => unreachable, | ||
| 382 | .local_ptr => unreachable, | ||
| 383 | .decl => unreachable, | ||
| 384 | .file => unreachable, | ||
| 385 | .zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name), | ||
| 386 | .container => return self.cast(Container).?.fullyQualifiedNameHash(name), | ||
| 387 | } | ||
| 388 | } | ||
| 389 | |||
| 390 | /// Asserts the scope is a child of a File and has an AST tree and returns the tree. | ||
| 391 | pub fn tree(self: *Scope) *ast.Tree { | ||
| 392 | switch (self.tag) { | ||
| 393 | .file => return self.cast(File).?.contents.tree, | ||
| 394 | .zir_module => unreachable, | ||
| 395 | .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(Container).?.file_scope.contents.tree, | ||
| 396 | .block => return self.cast(Block).?.decl.scope.cast(Container).?.file_scope.contents.tree, | ||
| 397 | .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(Container).?.file_scope.contents.tree, | ||
| 398 | .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree, | ||
| 399 | .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree, | ||
| 400 | .container => return self.cast(Container).?.file_scope.contents.tree, | ||
| 401 | } | ||
| 402 | } | ||
| 403 | |||
| 404 | /// Asserts the scope is a child of a `GenZIR` and returns it. | ||
| 405 | pub fn getGenZIR(self: *Scope) *GenZIR { | ||
| 406 | return switch (self.tag) { | ||
| 407 | .block => unreachable, | ||
| 408 | .gen_zir => self.cast(GenZIR).?, | ||
| 409 | .local_val => return self.cast(LocalVal).?.gen_zir, | ||
| 410 | .local_ptr => return self.cast(LocalPtr).?.gen_zir, | ||
| 411 | .decl => unreachable, | ||
| 412 | .zir_module => unreachable, | ||
| 413 | .file => unreachable, | ||
| 414 | .container => unreachable, | ||
| 415 | }; | ||
| 416 | } | ||
| 417 | |||
| 418 | /// Asserts the scope has a parent which is a ZIRModule, Contaienr or File and | ||
| 419 | /// returns the sub_file_path field. | ||
| 420 | pub fn subFilePath(base: *Scope) []const u8 { | ||
| 421 | switch (base.tag) { | ||
| 422 | .container => return @fieldParentPtr(Container, "base", base).file_scope.sub_file_path, | ||
| 423 | .file => return @fieldParentPtr(File, "base", base).sub_file_path, | ||
| 424 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path, | ||
| 425 | .block => unreachable, | ||
| 426 | .gen_zir => unreachable, | ||
| 427 | .local_val => unreachable, | ||
| 428 | .local_ptr => unreachable, | ||
| 429 | .decl => unreachable, | ||
| 430 | } | ||
| 431 | } | ||
| 432 | |||
| 433 | pub fn unload(base: *Scope, gpa: *Allocator) void { | ||
| 434 | switch (base.tag) { | ||
| 435 | .file => return @fieldParentPtr(File, "base", base).unload(gpa), | ||
| 436 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).unload(gpa), | ||
| 437 | .block => unreachable, | ||
| 438 | .gen_zir => unreachable, | ||
| 439 | .local_val => unreachable, | ||
| 440 | .local_ptr => unreachable, | ||
| 441 | .decl => unreachable, | ||
| 442 | .container => unreachable, | ||
| 443 | } | ||
| 444 | } | ||
| 445 | |||
| 446 | pub fn getSource(base: *Scope, module: *Module) ![:0]const u8 { | ||
| 447 | switch (base.tag) { | ||
| 448 | .container => return @fieldParentPtr(Container, "base", base).file_scope.getSource(module), | ||
| 449 | .file => return @fieldParentPtr(File, "base", base).getSource(module), | ||
| 450 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module), | ||
| 451 | .gen_zir => unreachable, | ||
| 452 | .local_val => unreachable, | ||
| 453 | .local_ptr => unreachable, | ||
| 454 | .block => unreachable, | ||
| 455 | .decl => unreachable, | ||
| 456 | } | ||
| 457 | } | ||
| 458 | |||
| 459 | /// Asserts the scope is a namespace Scope and removes the Decl from the namespace. | ||
| 460 | pub fn removeDecl(base: *Scope, child: *Decl) void { | ||
| 461 | switch (base.tag) { | ||
| 462 | .container => return @fieldParentPtr(Container, "base", base).removeDecl(child), | ||
| 463 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child), | ||
| 464 | .file => unreachable, | ||
| 465 | .block => unreachable, | ||
| 466 | .gen_zir => unreachable, | ||
| 467 | .local_val => unreachable, | ||
| 468 | .local_ptr => unreachable, | ||
| 469 | .decl => unreachable, | ||
| 470 | } | ||
| 471 | } | ||
| 472 | |||
| 473 | /// Asserts the scope is a File or ZIRModule and deinitializes it, then deallocates it. | ||
| 474 | pub fn destroy(base: *Scope, gpa: *Allocator) void { | ||
| 475 | switch (base.tag) { | ||
| 476 | .file => { | ||
| 477 | const scope_file = @fieldParentPtr(File, "base", base); | ||
| 478 | scope_file.deinit(gpa); | ||
| 479 | gpa.destroy(scope_file); | ||
| 480 | }, | ||
| 481 | .zir_module => { | ||
| 482 | const scope_zir_module = @fieldParentPtr(ZIRModule, "base", base); | ||
| 483 | scope_zir_module.deinit(gpa); | ||
| 484 | gpa.destroy(scope_zir_module); | ||
| 485 | }, | ||
| 486 | .block => unreachable, | ||
| 487 | .gen_zir => unreachable, | ||
| 488 | .local_val => unreachable, | ||
| 489 | .local_ptr => unreachable, | ||
| 490 | .decl => unreachable, | ||
| 491 | .container => unreachable, | ||
| 492 | } | ||
| 493 | } | ||
| 494 | |||
| 495 | fn name_hash_hash(x: NameHash) u32 { | ||
| 496 | return @truncate(u32, @bitCast(u128, x)); | ||
| 497 | } | ||
| 498 | |||
| 499 | fn name_hash_eql(a: NameHash, b: NameHash) bool { | ||
| 500 | return @bitCast(u128, a) == @bitCast(u128, b); | ||
| 501 | } | ||
| 502 | |||
| 503 | pub const Tag = enum { | ||
| 504 | /// .zir source code. | ||
| 505 | zir_module, | ||
| 506 | /// .zig source code. | ||
| 507 | file, | ||
| 508 | /// struct, enum or union, every .file contains one of these. | ||
| 509 | container, | ||
| 510 | block, | ||
| 511 | decl, | ||
| 512 | gen_zir, | ||
| 513 | local_val, | ||
| 514 | local_ptr, | ||
| 515 | }; | ||
| 516 | |||
| 517 | pub const Container = struct { | ||
| 518 | pub const base_tag: Tag = .container; | ||
| 519 | base: Scope = Scope{ .tag = base_tag }, | ||
| 520 | |||
| 521 | file_scope: *Scope.File, | ||
| 522 | |||
| 523 | /// Direct children of the file. | ||
| 524 | decls: std.AutoArrayHashMapUnmanaged(*Decl, void), | ||
| 525 | |||
| 526 | // TODO implement container types and put this in a status union | ||
| 527 | // ty: Type | ||
| 528 | |||
| 529 | pub fn deinit(self: *Container, gpa: *Allocator) void { | ||
| 530 | self.decls.deinit(gpa); | ||
| 531 | self.* = undefined; | ||
| 532 | } | ||
| 533 | |||
| 534 | pub fn removeDecl(self: *Container, child: *Decl) void { | ||
| 535 | _ = self.decls.remove(child); | ||
| 536 | } | ||
| 537 | |||
| 538 | pub fn fullyQualifiedNameHash(self: *Container, name: []const u8) NameHash { | ||
| 539 | // TODO container scope qualified names. | ||
| 540 | return std.zig.hashSrc(name); | ||
| 541 | } | ||
| 542 | }; | ||
| 543 | |||
| 544 | pub const File = struct { | ||
| 545 | pub const base_tag: Tag = .file; | ||
| 546 | base: Scope = Scope{ .tag = base_tag }, | ||
| 547 | |||
| 548 | /// Relative to the owning package's root_src_dir. | ||
| 549 | /// Reference to external memory, not owned by File. | ||
| 550 | sub_file_path: []const u8, | ||
| 551 | source: union(enum) { | ||
| 552 | unloaded: void, | ||
| 553 | bytes: [:0]const u8, | ||
| 554 | }, | ||
| 555 | contents: union { | ||
| 556 | not_available: void, | ||
| 557 | tree: *ast.Tree, | ||
| 558 | }, | ||
| 559 | status: enum { | ||
| 560 | never_loaded, | ||
| 561 | unloaded_success, | ||
| 562 | unloaded_parse_failure, | ||
| 563 | loaded_success, | ||
| 564 | }, | ||
| 565 | |||
| 566 | root_container: Container, | ||
| 567 | |||
| 568 | pub fn unload(self: *File, gpa: *Allocator) void { | ||
| 569 | switch (self.status) { | ||
| 570 | .never_loaded, | ||
| 571 | .unloaded_parse_failure, | ||
| 572 | .unloaded_success, | ||
| 573 | => {}, | ||
| 574 | |||
| 575 | .loaded_success => { | ||
| 576 | self.contents.tree.deinit(); | ||
| 577 | self.status = .unloaded_success; | ||
| 578 | }, | ||
| 579 | } | ||
| 580 | switch (self.source) { | ||
| 581 | .bytes => |bytes| { | ||
| 582 | gpa.free(bytes); | ||
| 583 | self.source = .{ .unloaded = {} }; | ||
| 584 | }, | ||
| 585 | .unloaded => {}, | ||
| 586 | } | ||
| 587 | } | ||
| 588 | |||
| 589 | pub fn deinit(self: *File, gpa: *Allocator) void { | ||
| 590 | self.root_container.deinit(gpa); | ||
| 591 | self.unload(gpa); | ||
| 592 | self.* = undefined; | ||
| 593 | } | ||
| 594 | |||
| 595 | pub fn dumpSrc(self: *File, src: usize) void { | ||
| 596 | const loc = std.zig.findLineColumn(self.source.bytes, src); | ||
| 597 | std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 }); | ||
| 598 | } | ||
| 599 | |||
| 600 | pub fn getSource(self: *File, module: *Module) ![:0]const u8 { | ||
| 601 | switch (self.source) { | ||
| 602 | .unloaded => { | ||
| 603 | const source = try module.root_pkg.root_src_directory.handle.readFileAllocOptions( | ||
| 604 | module.gpa, | ||
| 605 | self.sub_file_path, | ||
| 606 | std.math.maxInt(u32), | ||
| 607 | null, | ||
| 608 | 1, | ||
| 609 | 0, | ||
| 610 | ); | ||
| 611 | self.source = .{ .bytes = source }; | ||
| 612 | return source; | ||
| 613 | }, | ||
| 614 | .bytes => |bytes| return bytes, | ||
| 615 | } | ||
| 616 | } | ||
| 617 | }; | ||
| 618 | |||
| 619 | pub const ZIRModule = struct { | ||
| 620 | pub const base_tag: Tag = .zir_module; | ||
| 621 | base: Scope = Scope{ .tag = base_tag }, | ||
| 622 | /// Relative to the owning package's root_src_dir. | ||
| 623 | /// Reference to external memory, not owned by ZIRModule. | ||
| 624 | sub_file_path: []const u8, | ||
| 625 | source: union(enum) { | ||
| 626 | unloaded: void, | ||
| 627 | bytes: [:0]const u8, | ||
| 628 | }, | ||
| 629 | contents: union { | ||
| 630 | not_available: void, | ||
| 631 | module: *zir.Module, | ||
| 632 | }, | ||
| 633 | status: enum { | ||
| 634 | never_loaded, | ||
| 635 | unloaded_success, | ||
| 636 | unloaded_parse_failure, | ||
| 637 | unloaded_sema_failure, | ||
| 638 | |||
| 639 | loaded_sema_failure, | ||
| 640 | loaded_success, | ||
| 641 | }, | ||
| 642 | |||
| 643 | /// Even though .zir files only have 1 module, this set is still needed | ||
| 644 | /// because of anonymous Decls, which can exist in the global set, but | ||
| 645 | /// not this one. | ||
| 646 | decls: ArrayListUnmanaged(*Decl), | ||
| 647 | |||
| 648 | pub fn unload(self: *ZIRModule, gpa: *Allocator) void { | ||
| 649 | switch (self.status) { | ||
| 650 | .never_loaded, | ||
| 651 | .unloaded_parse_failure, | ||
| 652 | .unloaded_sema_failure, | ||
| 653 | .unloaded_success, | ||
| 654 | => {}, | ||
| 655 | |||
| 656 | .loaded_success => { | ||
| 657 | self.contents.module.deinit(gpa); | ||
| 658 | gpa.destroy(self.contents.module); | ||
| 659 | self.contents = .{ .not_available = {} }; | ||
| 660 | self.status = .unloaded_success; | ||
| 661 | }, | ||
| 662 | .loaded_sema_failure => { | ||
| 663 | self.contents.module.deinit(gpa); | ||
| 664 | gpa.destroy(self.contents.module); | ||
| 665 | self.contents = .{ .not_available = {} }; | ||
| 666 | self.status = .unloaded_sema_failure; | ||
| 667 | }, | ||
| 668 | } | ||
| 669 | switch (self.source) { | ||
| 670 | .bytes => |bytes| { | ||
| 671 | gpa.free(bytes); | ||
| 672 | self.source = .{ .unloaded = {} }; | ||
| 673 | }, | ||
| 674 | .unloaded => {}, | ||
| 675 | } | ||
| 676 | } | ||
| 677 | |||
| 678 | pub fn deinit(self: *ZIRModule, gpa: *Allocator) void { | ||
| 679 | self.decls.deinit(gpa); | ||
| 680 | self.unload(gpa); | ||
| 681 | self.* = undefined; | ||
| 682 | } | ||
| 683 | |||
| 684 | pub fn removeDecl(self: *ZIRModule, child: *Decl) void { | ||
| 685 | for (self.decls.items) |item, i| { | ||
| 686 | if (item == child) { | ||
| 687 | _ = self.decls.swapRemove(i); | ||
| 688 | return; | ||
| 689 | } | ||
| 690 | } | ||
| 691 | } | ||
| 692 | |||
| 693 | pub fn dumpSrc(self: *ZIRModule, src: usize) void { | ||
| 694 | const loc = std.zig.findLineColumn(self.source.bytes, src); | ||
| 695 | std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 }); | ||
| 696 | } | ||
| 697 | |||
| 698 | pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 { | ||
| 699 | switch (self.source) { | ||
| 700 | .unloaded => { | ||
| 701 | const source = try module.root_pkg.root_src_directory.handle.readFileAllocOptions( | ||
| 702 | module.gpa, | ||
| 703 | self.sub_file_path, | ||
| 704 | std.math.maxInt(u32), | ||
| 705 | null, | ||
| 706 | 1, | ||
| 707 | 0, | ||
| 708 | ); | ||
| 709 | self.source = .{ .bytes = source }; | ||
| 710 | return source; | ||
| 711 | }, | ||
| 712 | .bytes => |bytes| return bytes, | ||
| 713 | } | ||
| 714 | } | ||
| 715 | |||
| 716 | pub fn fullyQualifiedNameHash(self: *ZIRModule, name: []const u8) NameHash { | ||
| 717 | // ZIR modules only have 1 file with all decls global in the same namespace. | ||
| 718 | return std.zig.hashSrc(name); | ||
| 719 | } | ||
| 720 | }; | ||
| 721 | |||
| 722 | /// This is a temporary structure, references to it are valid only | ||
| 723 | /// during semantic analysis of the block. | ||
| 724 | pub const Block = struct { | ||
| 725 | pub const base_tag: Tag = .block; | ||
| 726 | base: Scope = Scope{ .tag = base_tag }, | ||
| 727 | parent: ?*Block, | ||
| 728 | func: ?*Fn, | ||
| 729 | decl: *Decl, | ||
| 730 | instructions: ArrayListUnmanaged(*Inst), | ||
| 731 | /// Points to the arena allocator of DeclAnalysis | ||
| 732 | arena: *Allocator, | ||
| 733 | label: ?Label = null, | ||
| 734 | is_comptime: bool, | ||
| 735 | |||
| 736 | pub const Label = struct { | ||
| 737 | zir_block: *zir.Inst.Block, | ||
| 738 | results: ArrayListUnmanaged(*Inst), | ||
| 739 | block_inst: *Inst.Block, | ||
| 740 | }; | ||
| 741 | }; | ||
| 742 | |||
| 743 | /// This is a temporary structure, references to it are valid only | ||
| 744 | /// during semantic analysis of the decl. | ||
| 745 | pub const DeclAnalysis = struct { | ||
| 746 | pub const base_tag: Tag = .decl; | ||
| 747 | base: Scope = Scope{ .tag = base_tag }, | ||
| 748 | decl: *Decl, | ||
| 749 | arena: std.heap.ArenaAllocator, | ||
| 750 | }; | ||
| 751 | |||
| 752 | /// This is a temporary structure, references to it are valid only | ||
| 753 | /// during semantic analysis of the decl. | ||
| 754 | pub const GenZIR = struct { | ||
| 755 | pub const base_tag: Tag = .gen_zir; | ||
| 756 | base: Scope = Scope{ .tag = base_tag }, | ||
| 757 | /// Parents can be: `GenZIR`, `ZIRModule`, `File` | ||
| 758 | parent: *Scope, | ||
| 759 | decl: *Decl, | ||
| 760 | arena: *Allocator, | ||
| 761 | /// The first N instructions in a function body ZIR are arg instructions. | ||
| 762 | instructions: std.ArrayListUnmanaged(*zir.Inst) = .{}, | ||
| 763 | label: ?Label = null, | ||
| 764 | |||
| 765 | pub const Label = struct { | ||
| 766 | token: ast.TokenIndex, | ||
| 767 | block_inst: *zir.Inst.Block, | ||
| 768 | result_loc: astgen.ResultLoc, | ||
| 769 | }; | ||
| 770 | }; | ||
| 771 | |||
| 772 | /// This is always a `const` local and importantly the `inst` is a value type, not a pointer. | ||
| 773 | /// This structure lives as long as the AST generation of the Block | ||
| 774 | /// node that contains the variable. | ||
| 775 | pub const LocalVal = struct { | ||
| 776 | pub const base_tag: Tag = .local_val; | ||
| 777 | base: Scope = Scope{ .tag = base_tag }, | ||
| 778 | /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`. | ||
| 779 | parent: *Scope, | ||
| 780 | gen_zir: *GenZIR, | ||
| 781 | name: []const u8, | ||
| 782 | inst: *zir.Inst, | ||
| 783 | }; | ||
| 784 | |||
| 785 | /// This could be a `const` or `var` local. It has a pointer instead of a value. | ||
| 786 | /// This structure lives as long as the AST generation of the Block | ||
| 787 | /// node that contains the variable. | ||
| 788 | pub const LocalPtr = struct { | ||
| 789 | pub const base_tag: Tag = .local_ptr; | ||
| 790 | base: Scope = Scope{ .tag = base_tag }, | ||
| 791 | /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`. | ||
| 792 | parent: *Scope, | ||
| 793 | gen_zir: *GenZIR, | ||
| 794 | name: []const u8, | ||
| 795 | ptr: *zir.Inst, | ||
| 796 | }; | ||
| 797 | }; | ||
| 798 | |||
| 799 | pub const InnerError = error{ OutOfMemory, AnalysisFail }; | ||
| 800 | |||
| 801 | pub fn deinit(self: *Module) void { | ||
| 802 | const gpa = self.gpa; | ||
| 803 | |||
| 804 | self.zig_cache_artifact_directory.handle.close(); | ||
| 805 | |||
| 806 | self.deletion_set.deinit(gpa); | ||
| 807 | |||
| 808 | for (self.decl_table.items()) |entry| { | ||
| 809 | entry.value.destroy(gpa); | ||
| 810 | } | ||
| 811 | self.decl_table.deinit(gpa); | ||
| 812 | |||
| 813 | for (self.failed_decls.items()) |entry| { | ||
| 814 | entry.value.destroy(gpa); | ||
| 815 | } | ||
| 816 | self.failed_decls.deinit(gpa); | ||
| 817 | |||
| 818 | for (self.failed_files.items()) |entry| { | ||
| 819 | entry.value.destroy(gpa); | ||
| 820 | } | ||
| 821 | self.failed_files.deinit(gpa); | ||
| 822 | |||
| 823 | for (self.failed_exports.items()) |entry| { | ||
| 824 | entry.value.destroy(gpa); | ||
| 825 | } | ||
| 826 | self.failed_exports.deinit(gpa); | ||
| 827 | |||
| 828 | for (self.decl_exports.items()) |entry| { | ||
| 829 | const export_list = entry.value; | ||
| 830 | gpa.free(export_list); | ||
| 831 | } | ||
| 832 | self.decl_exports.deinit(gpa); | ||
| 833 | |||
| 834 | for (self.export_owners.items()) |entry| { | ||
| 835 | freeExportList(gpa, entry.value); | ||
| 836 | } | ||
| 837 | self.export_owners.deinit(gpa); | ||
| 838 | |||
| 839 | self.symbol_exports.deinit(gpa); | ||
| 840 | self.root_scope.destroy(gpa); | ||
| 841 | |||
| 842 | var it = self.global_error_set.iterator(); | ||
| 843 | while (it.next()) |entry| { | ||
| 844 | gpa.free(entry.key); | ||
| 845 | } | ||
| 846 | self.global_error_set.deinit(gpa); | ||
| 847 | } | ||
| 848 | |||
| 849 | fn freeExportList(gpa: *Allocator, export_list: []*Export) void { | ||
| 850 | for (export_list) |exp| { | ||
| 851 | gpa.free(exp.options.name); | ||
| 852 | gpa.destroy(exp); | ||
| 853 | } | ||
| 854 | gpa.free(export_list); | ||
| 855 | } | ||
| 856 | |||
| 857 | pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { | ||
| 858 | const tracy = trace(@src()); | ||
| 859 | defer tracy.end(); | ||
| 860 | |||
| 861 | const subsequent_analysis = switch (decl.analysis) { | ||
| 862 | .in_progress => unreachable, | ||
| 863 | |||
| 864 | .sema_failure, | ||
| 865 | .sema_failure_retryable, | ||
| 866 | .codegen_failure, | ||
| 867 | .dependency_failure, | ||
| 868 | .codegen_failure_retryable, | ||
| 869 | => return error.AnalysisFail, | ||
| 870 | |||
| 871 | .complete => return, | ||
| 872 | |||
| 873 | .outdated => blk: { | ||
| 874 | log.debug("re-analyzing {}\n", .{decl.name}); | ||
| 875 | |||
| 876 | // The exports this Decl performs will be re-discovered, so we remove them here | ||
| 877 | // prior to re-analysis. | ||
| 878 | self.deleteDeclExports(decl); | ||
| 879 | // Dependencies will be re-discovered, so we remove them here prior to re-analysis. | ||
| 880 | for (decl.dependencies.items()) |entry| { | ||
| 881 | const dep = entry.key; | ||
| 882 | dep.removeDependant(decl); | ||
| 883 | if (dep.dependants.items().len == 0 and !dep.deletion_flag) { | ||
| 884 | // We don't perform a deletion here, because this Decl or another one | ||
| 885 | // may end up referencing it before the update is complete. | ||
| 886 | dep.deletion_flag = true; | ||
| 887 | try self.deletion_set.append(self.gpa, dep); | ||
| 888 | } | ||
| 889 | } | ||
| 890 | decl.dependencies.clearRetainingCapacity(); | ||
| 891 | |||
| 892 | break :blk true; | ||
| 893 | }, | ||
| 894 | |||
| 895 | .unreferenced => false, | ||
| 896 | }; | ||
| 897 | |||
| 898 | const type_changed = if (self.root_scope.cast(Scope.ZIRModule)) |zir_module| | ||
| 899 | try zir_sema.analyzeZirDecl(self, decl, zir_module.contents.module.decls[decl.src_index]) | ||
| 900 | else | ||
| 901 | self.astGenAndAnalyzeDecl(decl) catch |err| switch (err) { | ||
| 902 | error.OutOfMemory => return error.OutOfMemory, | ||
| 903 | error.AnalysisFail => return error.AnalysisFail, | ||
| 904 | else => { | ||
| 905 | try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1); | ||
| 906 | self.failed_decls.putAssumeCapacityNoClobber(decl, try Compilation.ErrorMsg.create( | ||
| 907 | self.gpa, | ||
| 908 | decl.src(), | ||
| 909 | "unable to analyze: {}", | ||
| 910 | .{@errorName(err)}, | ||
| 911 | )); | ||
| 912 | decl.analysis = .sema_failure_retryable; | ||
| 913 | return error.AnalysisFail; | ||
| 914 | }, | ||
| 915 | }; | ||
| 916 | |||
| 917 | if (subsequent_analysis) { | ||
| 918 | // We may need to chase the dependants and re-analyze them. | ||
| 919 | // However, if the decl is a function, and the type is the same, we do not need to. | ||
| 920 | if (type_changed or decl.typed_value.most_recent.typed_value.val.tag() != .function) { | ||
| 921 | for (decl.dependants.items()) |entry| { | ||
| 922 | const dep = entry.key; | ||
| 923 | switch (dep.analysis) { | ||
| 924 | .unreferenced => unreachable, | ||
| 925 | .in_progress => unreachable, | ||
| 926 | .outdated => continue, // already queued for update | ||
| 927 | |||
| 928 | .dependency_failure, | ||
| 929 | .sema_failure, | ||
| 930 | .sema_failure_retryable, | ||
| 931 | .codegen_failure, | ||
| 932 | .codegen_failure_retryable, | ||
| 933 | .complete, | ||
| 934 | => if (dep.generation != self.generation) { | ||
| 935 | try self.markOutdatedDecl(dep); | ||
| 936 | }, | ||
| 937 | } | ||
| 938 | } | ||
| 939 | } | ||
| 940 | } | ||
| 941 | } | ||
| 942 | |||
| 943 | fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { | ||
| 944 | const tracy = trace(@src()); | ||
| 945 | defer tracy.end(); | ||
| 946 | |||
| 947 | const container_scope = decl.scope.cast(Scope.Container).?; | ||
| 948 | const tree = try self.getAstTree(container_scope); | ||
| 949 | const ast_node = tree.root_node.decls()[decl.src_index]; | ||
| 950 | switch (ast_node.tag) { | ||
| 951 | .FnProto => { | ||
| 952 | const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", ast_node); | ||
| 953 | |||
| 954 | decl.analysis = .in_progress; | ||
| 955 | |||
| 956 | // This arena allocator's memory is discarded at the end of this function. It is used | ||
| 957 | // to determine the type of the function, and hence the type of the decl, which is needed | ||
| 958 | // to complete the Decl analysis. | ||
| 959 | var fn_type_scope_arena = std.heap.ArenaAllocator.init(self.gpa); | ||
| 960 | defer fn_type_scope_arena.deinit(); | ||
| 961 | var fn_type_scope: Scope.GenZIR = .{ | ||
| 962 | .decl = decl, | ||
| 963 | .arena = &fn_type_scope_arena.allocator, | ||
| 964 | .parent = decl.scope, | ||
| 965 | }; | ||
| 966 | defer fn_type_scope.instructions.deinit(self.gpa); | ||
| 967 | |||
| 968 | decl.is_pub = fn_proto.getVisibToken() != null; | ||
| 969 | const body_node = fn_proto.getBodyNode() orelse | ||
| 970 | return self.failTok(&fn_type_scope.base, fn_proto.fn_token, "TODO implement extern functions", .{}); | ||
| 971 | |||
| 972 | const param_decls = fn_proto.params(); | ||
| 973 | const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len); | ||
| 974 | |||
| 975 | const fn_src = tree.token_locs[fn_proto.fn_token].start; | ||
| 976 | const type_type = try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{ | ||
| 977 | .ty = Type.initTag(.type), | ||
| 978 | .val = Value.initTag(.type_type), | ||
| 979 | }); | ||
| 980 | const type_type_rl: astgen.ResultLoc = .{ .ty = type_type }; | ||
| 981 | for (param_decls) |param_decl, i| { | ||
| 982 | const param_type_node = switch (param_decl.param_type) { | ||
| 983 | .any_type => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement anytype parameter", .{}), | ||
| 984 | .type_expr => |node| node, | ||
| 985 | }; | ||
| 986 | param_types[i] = try astgen.expr(self, &fn_type_scope.base, type_type_rl, param_type_node); | ||
| 987 | } | ||
| 988 | if (fn_proto.getVarArgsToken()) |var_args_token| { | ||
| 989 | return self.failTok(&fn_type_scope.base, var_args_token, "TODO implement var args", .{}); | ||
| 990 | } | ||
| 991 | if (fn_proto.getLibName()) |lib_name| { | ||
| 992 | return self.failNode(&fn_type_scope.base, lib_name, "TODO implement function library name", .{}); | ||
| 993 | } | ||
| 994 | if (fn_proto.getAlignExpr()) |align_expr| { | ||
| 995 | return self.failNode(&fn_type_scope.base, align_expr, "TODO implement function align expression", .{}); | ||
| 996 | } | ||
| 997 | if (fn_proto.getSectionExpr()) |sect_expr| { | ||
| 998 | return self.failNode(&fn_type_scope.base, sect_expr, "TODO implement function section expression", .{}); | ||
| 999 | } | ||
| 1000 | if (fn_proto.getCallconvExpr()) |callconv_expr| { | ||
| 1001 | return self.failNode( | ||
| 1002 | &fn_type_scope.base, | ||
| 1003 | callconv_expr, | ||
| 1004 | "TODO implement function calling convention expression", | ||
| 1005 | .{}, | ||
| 1006 | ); | ||
| 1007 | } | ||
| 1008 | const return_type_expr = switch (fn_proto.return_type) { | ||
| 1009 | .Explicit => |node| node, | ||
| 1010 | .InferErrorSet => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement inferred error sets", .{}), | ||
| 1011 | .Invalid => |tok| return self.failTok(&fn_type_scope.base, tok, "unable to parse return type", .{}), | ||
| 1012 | }; | ||
| 1013 | |||
| 1014 | const return_type_inst = try astgen.expr(self, &fn_type_scope.base, type_type_rl, return_type_expr); | ||
| 1015 | const fn_type_inst = try astgen.addZIRInst(self, &fn_type_scope.base, fn_src, zir.Inst.FnType, .{ | ||
| 1016 | .return_type = return_type_inst, | ||
| 1017 | .param_types = param_types, | ||
| 1018 | }, .{}); | ||
| 1019 | |||
| 1020 | // We need the memory for the Type to go into the arena for the Decl | ||
| 1021 | var decl_arena = std.heap.ArenaAllocator.init(self.gpa); | ||
| 1022 | errdefer decl_arena.deinit(); | ||
| 1023 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); | ||
| 1024 | |||
| 1025 | var block_scope: Scope.Block = .{ | ||
| 1026 | .parent = null, | ||
| 1027 | .func = null, | ||
| 1028 | .decl = decl, | ||
| 1029 | .instructions = .{}, | ||
| 1030 | .arena = &decl_arena.allocator, | ||
| 1031 | .is_comptime = false, | ||
| 1032 | }; | ||
| 1033 | defer block_scope.instructions.deinit(self.gpa); | ||
| 1034 | |||
| 1035 | const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{ | ||
| 1036 | .instructions = fn_type_scope.instructions.items, | ||
| 1037 | }); | ||
| 1038 | const new_func = try decl_arena.allocator.create(Fn); | ||
| 1039 | const fn_payload = try decl_arena.allocator.create(Value.Payload.Function); | ||
| 1040 | |||
| 1041 | const fn_zir = blk: { | ||
| 1042 | // This scope's arena memory is discarded after the ZIR generation | ||
| 1043 | // pass completes, and semantic analysis of it completes. | ||
| 1044 | var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa); | ||
| 1045 | errdefer gen_scope_arena.deinit(); | ||
| 1046 | var gen_scope: Scope.GenZIR = .{ | ||
| 1047 | .decl = decl, | ||
| 1048 | .arena = &gen_scope_arena.allocator, | ||
| 1049 | .parent = decl.scope, | ||
| 1050 | }; | ||
| 1051 | defer gen_scope.instructions.deinit(self.gpa); | ||
| 1052 | |||
| 1053 | // We need an instruction for each parameter, and they must be first in the body. | ||
| 1054 | try gen_scope.instructions.resize(self.gpa, fn_proto.params_len); | ||
| 1055 | var params_scope = &gen_scope.base; | ||
| 1056 | for (fn_proto.params()) |param, i| { | ||
| 1057 | const name_token = param.name_token.?; | ||
| 1058 | const src = tree.token_locs[name_token].start; | ||
| 1059 | const param_name = tree.tokenSlice(name_token); // TODO: call identifierTokenString | ||
| 1060 | const arg = try gen_scope_arena.allocator.create(zir.Inst.Arg); | ||
| 1061 | arg.* = .{ | ||
| 1062 | .base = .{ | ||
| 1063 | .tag = .arg, | ||
| 1064 | .src = src, | ||
| 1065 | }, | ||
| 1066 | .positionals = .{ | ||
| 1067 | .name = param_name, | ||
| 1068 | }, | ||
| 1069 | .kw_args = .{}, | ||
| 1070 | }; | ||
| 1071 | gen_scope.instructions.items[i] = &arg.base; | ||
| 1072 | const sub_scope = try gen_scope_arena.allocator.create(Scope.LocalVal); | ||
| 1073 | sub_scope.* = .{ | ||
| 1074 | .parent = params_scope, | ||
| 1075 | .gen_zir = &gen_scope, | ||
| 1076 | .name = param_name, | ||
| 1077 | .inst = &arg.base, | ||
| 1078 | }; | ||
| 1079 | params_scope = &sub_scope.base; | ||
| 1080 | } | ||
| 1081 | |||
| 1082 | const body_block = body_node.cast(ast.Node.Block).?; | ||
| 1083 | |||
| 1084 | try astgen.blockExpr(self, params_scope, body_block); | ||
| 1085 | |||
| 1086 | if (gen_scope.instructions.items.len == 0 or | ||
| 1087 | !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()) | ||
| 1088 | { | ||
| 1089 | const src = tree.token_locs[body_block.rbrace].start; | ||
| 1090 | _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid); | ||
| 1091 | } | ||
| 1092 | |||
| 1093 | const fn_zir = try gen_scope_arena.allocator.create(Fn.ZIR); | ||
| 1094 | fn_zir.* = .{ | ||
| 1095 | .body = .{ | ||
| 1096 | .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items), | ||
| 1097 | }, | ||
| 1098 | .arena = gen_scope_arena.state, | ||
| 1099 | }; | ||
| 1100 | break :blk fn_zir; | ||
| 1101 | }; | ||
| 1102 | |||
| 1103 | new_func.* = .{ | ||
| 1104 | .analysis = .{ .queued = fn_zir }, | ||
| 1105 | .owner_decl = decl, | ||
| 1106 | }; | ||
| 1107 | fn_payload.* = .{ .func = new_func }; | ||
| 1108 | |||
| 1109 | var prev_type_has_bits = false; | ||
| 1110 | var type_changed = true; | ||
| 1111 | |||
| 1112 | if (decl.typedValueManaged()) |tvm| { | ||
| 1113 | prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits(); | ||
| 1114 | type_changed = !tvm.typed_value.ty.eql(fn_type); | ||
| 1115 | |||
| 1116 | tvm.deinit(self.gpa); | ||
| 1117 | } | ||
| 1118 | |||
| 1119 | decl_arena_state.* = decl_arena.state; | ||
| 1120 | decl.typed_value = .{ | ||
| 1121 | .most_recent = .{ | ||
| 1122 | .typed_value = .{ | ||
| 1123 | .ty = fn_type, | ||
| 1124 | .val = Value.initPayload(&fn_payload.base), | ||
| 1125 | }, | ||
| 1126 | .arena = decl_arena_state, | ||
| 1127 | }, | ||
| 1128 | }; | ||
| 1129 | decl.analysis = .complete; | ||
| 1130 | decl.generation = self.generation; | ||
| 1131 | |||
| 1132 | if (fn_type.hasCodeGenBits()) { | ||
| 1133 | // We don't fully codegen the decl until later, but we do need to reserve a global | ||
| 1134 | // offset table index for it. This allows us to codegen decls out of dependency order, | ||
| 1135 | // increasing how many computations can be done in parallel. | ||
| 1136 | try self.comp.bin_file.allocateDeclIndexes(decl); | ||
| 1137 | try self.comp.work_queue.writeItem(.{ .codegen_decl = decl }); | ||
| 1138 | } else if (prev_type_has_bits) { | ||
| 1139 | self.comp.bin_file.freeDecl(decl); | ||
| 1140 | } | ||
| 1141 | |||
| 1142 | if (fn_proto.getExternExportInlineToken()) |maybe_export_token| { | ||
| 1143 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { | ||
| 1144 | const export_src = tree.token_locs[maybe_export_token].start; | ||
| 1145 | const name_loc = tree.token_locs[fn_proto.getNameToken().?]; | ||
| 1146 | const name = tree.tokenSliceLoc(name_loc); | ||
| 1147 | // The scope needs to have the decl in it. | ||
| 1148 | try self.analyzeExport(&block_scope.base, export_src, name, decl); | ||
| 1149 | } | ||
| 1150 | } | ||
| 1151 | return type_changed; | ||
| 1152 | }, | ||
| 1153 | .VarDecl => { | ||
| 1154 | const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", ast_node); | ||
| 1155 | |||
| 1156 | decl.analysis = .in_progress; | ||
| 1157 | |||
| 1158 | // We need the memory for the Type to go into the arena for the Decl | ||
| 1159 | var decl_arena = std.heap.ArenaAllocator.init(self.gpa); | ||
| 1160 | errdefer decl_arena.deinit(); | ||
| 1161 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); | ||
| 1162 | |||
| 1163 | var block_scope: Scope.Block = .{ | ||
| 1164 | .parent = null, | ||
| 1165 | .func = null, | ||
| 1166 | .decl = decl, | ||
| 1167 | .instructions = .{}, | ||
| 1168 | .arena = &decl_arena.allocator, | ||
| 1169 | .is_comptime = true, | ||
| 1170 | }; | ||
| 1171 | defer block_scope.instructions.deinit(self.gpa); | ||
| 1172 | |||
| 1173 | decl.is_pub = var_decl.getVisibToken() != null; | ||
| 1174 | const is_extern = blk: { | ||
| 1175 | const maybe_extern_token = var_decl.getExternExportToken() orelse | ||
| 1176 | break :blk false; | ||
| 1177 | if (tree.token_ids[maybe_extern_token] != .Keyword_extern) break :blk false; | ||
| 1178 | if (var_decl.getInitNode()) |some| { | ||
| 1179 | return self.failNode(&block_scope.base, some, "extern variables have no initializers", .{}); | ||
| 1180 | } | ||
| 1181 | break :blk true; | ||
| 1182 | }; | ||
| 1183 | if (var_decl.getLibName()) |lib_name| { | ||
| 1184 | assert(is_extern); | ||
| 1185 | return self.failNode(&block_scope.base, lib_name, "TODO implement function library name", .{}); | ||
| 1186 | } | ||
| 1187 | const is_mutable = tree.token_ids[var_decl.mut_token] == .Keyword_var; | ||
| 1188 | const is_threadlocal = if (var_decl.getThreadLocalToken()) |some| blk: { | ||
| 1189 | if (!is_mutable) { | ||
| 1190 | return self.failTok(&block_scope.base, some, "threadlocal variable cannot be constant", .{}); | ||
| 1191 | } | ||
| 1192 | break :blk true; | ||
| 1193 | } else false; | ||
| 1194 | assert(var_decl.getComptimeToken() == null); | ||
| 1195 | if (var_decl.getAlignNode()) |align_expr| { | ||
| 1196 | return self.failNode(&block_scope.base, align_expr, "TODO implement function align expression", .{}); | ||
| 1197 | } | ||
| 1198 | if (var_decl.getSectionNode()) |sect_expr| { | ||
| 1199 | return self.failNode(&block_scope.base, sect_expr, "TODO implement function section expression", .{}); | ||
| 1200 | } | ||
| 1201 | |||
| 1202 | const var_info: struct { ty: Type, val: ?Value } = if (var_decl.getInitNode()) |init_node| vi: { | ||
| 1203 | var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa); | ||
| 1204 | defer gen_scope_arena.deinit(); | ||
| 1205 | var gen_scope: Scope.GenZIR = .{ | ||
| 1206 | .decl = decl, | ||
| 1207 | .arena = &gen_scope_arena.allocator, | ||
| 1208 | .parent = decl.scope, | ||
| 1209 | }; | ||
| 1210 | defer gen_scope.instructions.deinit(self.gpa); | ||
| 1211 | |||
| 1212 | const init_result_loc: astgen.ResultLoc = if (var_decl.getTypeNode()) |type_node| rl: { | ||
| 1213 | const src = tree.token_locs[type_node.firstToken()].start; | ||
| 1214 | const type_type = try astgen.addZIRInstConst(self, &gen_scope.base, src, .{ | ||
| 1215 | .ty = Type.initTag(.type), | ||
| 1216 | .val = Value.initTag(.type_type), | ||
| 1217 | }); | ||
| 1218 | const var_type = try astgen.expr(self, &gen_scope.base, .{ .ty = type_type }, type_node); | ||
| 1219 | break :rl .{ .ty = var_type }; | ||
| 1220 | } else .none; | ||
| 1221 | |||
| 1222 | const src = tree.token_locs[init_node.firstToken()].start; | ||
| 1223 | const init_inst = try astgen.expr(self, &gen_scope.base, init_result_loc, init_node); | ||
| 1224 | |||
| 1225 | var inner_block: Scope.Block = .{ | ||
| 1226 | .parent = null, | ||
| 1227 | .func = null, | ||
| 1228 | .decl = decl, | ||
| 1229 | .instructions = .{}, | ||
| 1230 | .arena = &gen_scope_arena.allocator, | ||
| 1231 | .is_comptime = true, | ||
| 1232 | }; | ||
| 1233 | defer inner_block.instructions.deinit(self.gpa); | ||
| 1234 | try zir_sema.analyzeBody(self, &inner_block.base, .{ .instructions = gen_scope.instructions.items }); | ||
| 1235 | |||
| 1236 | // The result location guarantees the type coercion. | ||
| 1237 | const analyzed_init_inst = init_inst.analyzed_inst.?; | ||
| 1238 | // The is_comptime in the Scope.Block guarantees the result is comptime-known. | ||
| 1239 | const val = analyzed_init_inst.value().?; | ||
| 1240 | |||
| 1241 | const ty = try analyzed_init_inst.ty.copy(block_scope.arena); | ||
| 1242 | break :vi .{ | ||
| 1243 | .ty = ty, | ||
| 1244 | .val = try val.copy(block_scope.arena), | ||
| 1245 | }; | ||
| 1246 | } else if (!is_extern) { | ||
| 1247 | return self.failTok(&block_scope.base, var_decl.firstToken(), "variables must be initialized", .{}); | ||
| 1248 | } else if (var_decl.getTypeNode()) |type_node| vi: { | ||
| 1249 | // Temporary arena for the zir instructions. | ||
| 1250 | var type_scope_arena = std.heap.ArenaAllocator.init(self.gpa); | ||
| 1251 | defer type_scope_arena.deinit(); | ||
| 1252 | var type_scope: Scope.GenZIR = .{ | ||
| 1253 | .decl = decl, | ||
| 1254 | .arena = &type_scope_arena.allocator, | ||
| 1255 | .parent = decl.scope, | ||
| 1256 | }; | ||
| 1257 | defer type_scope.instructions.deinit(self.gpa); | ||
| 1258 | |||
| 1259 | const src = tree.token_locs[type_node.firstToken()].start; | ||
| 1260 | const type_type = try astgen.addZIRInstConst(self, &type_scope.base, src, .{ | ||
| 1261 | .ty = Type.initTag(.type), | ||
| 1262 | .val = Value.initTag(.type_type), | ||
| 1263 | }); | ||
| 1264 | const var_type = try astgen.expr(self, &type_scope.base, .{ .ty = type_type }, type_node); | ||
| 1265 | const ty = try zir_sema.analyzeBodyValueAsType(self, &block_scope, var_type, .{ | ||
| 1266 | .instructions = type_scope.instructions.items, | ||
| 1267 | }); | ||
| 1268 | break :vi .{ | ||
| 1269 | .ty = ty, | ||
| 1270 | .val = null, | ||
| 1271 | }; | ||
| 1272 | } else { | ||
| 1273 | return self.failTok(&block_scope.base, var_decl.firstToken(), "unable to infer variable type", .{}); | ||
| 1274 | }; | ||
| 1275 | |||
| 1276 | if (is_mutable and !var_info.ty.isValidVarType(is_extern)) { | ||
| 1277 | return self.failTok(&block_scope.base, var_decl.firstToken(), "variable of type '{}' must be const", .{var_info.ty}); | ||
| 1278 | } | ||
| 1279 | |||
| 1280 | var type_changed = true; | ||
| 1281 | if (decl.typedValueManaged()) |tvm| { | ||
| 1282 | type_changed = !tvm.typed_value.ty.eql(var_info.ty); | ||
| 1283 | |||
| 1284 | tvm.deinit(self.gpa); | ||
| 1285 | } | ||
| 1286 | |||
| 1287 | const new_variable = try decl_arena.allocator.create(Var); | ||
| 1288 | const var_payload = try decl_arena.allocator.create(Value.Payload.Variable); | ||
| 1289 | new_variable.* = .{ | ||
| 1290 | .owner_decl = decl, | ||
| 1291 | .init = var_info.val orelse undefined, | ||
| 1292 | .is_extern = is_extern, | ||
| 1293 | .is_mutable = is_mutable, | ||
| 1294 | .is_threadlocal = is_threadlocal, | ||
| 1295 | }; | ||
| 1296 | var_payload.* = .{ .variable = new_variable }; | ||
| 1297 | |||
| 1298 | decl_arena_state.* = decl_arena.state; | ||
| 1299 | decl.typed_value = .{ | ||
| 1300 | .most_recent = .{ | ||
| 1301 | .typed_value = .{ | ||
| 1302 | .ty = var_info.ty, | ||
| 1303 | .val = Value.initPayload(&var_payload.base), | ||
| 1304 | }, | ||
| 1305 | .arena = decl_arena_state, | ||
| 1306 | }, | ||
| 1307 | }; | ||
| 1308 | decl.analysis = .complete; | ||
| 1309 | decl.generation = self.generation; | ||
| 1310 | |||
| 1311 | if (var_decl.getExternExportToken()) |maybe_export_token| { | ||
| 1312 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { | ||
| 1313 | const export_src = tree.token_locs[maybe_export_token].start; | ||
| 1314 | const name_loc = tree.token_locs[var_decl.name_token]; | ||
| 1315 | const name = tree.tokenSliceLoc(name_loc); | ||
| 1316 | // The scope needs to have the decl in it. | ||
| 1317 | try self.analyzeExport(&block_scope.base, export_src, name, decl); | ||
| 1318 | } | ||
| 1319 | } | ||
| 1320 | return type_changed; | ||
| 1321 | }, | ||
| 1322 | .Comptime => { | ||
| 1323 | const comptime_decl = @fieldParentPtr(ast.Node.Comptime, "base", ast_node); | ||
| 1324 | |||
| 1325 | decl.analysis = .in_progress; | ||
| 1326 | |||
| 1327 | // A comptime decl does not store any value so we can just deinit this arena after analysis is done. | ||
| 1328 | var analysis_arena = std.heap.ArenaAllocator.init(self.gpa); | ||
| 1329 | defer analysis_arena.deinit(); | ||
| 1330 | var gen_scope: Scope.GenZIR = .{ | ||
| 1331 | .decl = decl, | ||
| 1332 | .arena = &analysis_arena.allocator, | ||
| 1333 | .parent = decl.scope, | ||
| 1334 | }; | ||
| 1335 | defer gen_scope.instructions.deinit(self.gpa); | ||
| 1336 | |||
| 1337 | _ = try astgen.comptimeExpr(self, &gen_scope.base, .none, comptime_decl.expr); | ||
| 1338 | |||
| 1339 | var block_scope: Scope.Block = .{ | ||
| 1340 | .parent = null, | ||
| 1341 | .func = null, | ||
| 1342 | .decl = decl, | ||
| 1343 | .instructions = .{}, | ||
| 1344 | .arena = &analysis_arena.allocator, | ||
| 1345 | .is_comptime = true, | ||
| 1346 | }; | ||
| 1347 | defer block_scope.instructions.deinit(self.gpa); | ||
| 1348 | |||
| 1349 | _ = try zir_sema.analyzeBody(self, &block_scope.base, .{ | ||
| 1350 | .instructions = gen_scope.instructions.items, | ||
| 1351 | }); | ||
| 1352 | |||
| 1353 | decl.analysis = .complete; | ||
| 1354 | decl.generation = self.generation; | ||
| 1355 | return true; | ||
| 1356 | }, | ||
| 1357 | .Use => @panic("TODO usingnamespace decl"), | ||
| 1358 | else => unreachable, | ||
| 1359 | } | ||
| 1360 | } | ||
| 1361 | |||
| 1362 | fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void { | ||
| 1363 | try depender.dependencies.ensureCapacity(self.gpa, depender.dependencies.items().len + 1); | ||
| 1364 | try dependee.dependants.ensureCapacity(self.gpa, dependee.dependants.items().len + 1); | ||
| 1365 | |||
| 1366 | depender.dependencies.putAssumeCapacity(dependee, {}); | ||
| 1367 | dependee.dependants.putAssumeCapacity(depender, {}); | ||
| 1368 | } | ||
| 1369 | |||
| 1370 | fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module { | ||
| 1371 | switch (root_scope.status) { | ||
| 1372 | .never_loaded, .unloaded_success => { | ||
| 1373 | try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1); | ||
| 1374 | |||
| 1375 | const source = try root_scope.getSource(self); | ||
| 1376 | |||
| 1377 | var keep_zir_module = false; | ||
| 1378 | const zir_module = try self.gpa.create(zir.Module); | ||
| 1379 | defer if (!keep_zir_module) self.gpa.destroy(zir_module); | ||
| 1380 | |||
| 1381 | zir_module.* = try zir.parse(self.gpa, source); | ||
| 1382 | defer if (!keep_zir_module) zir_module.deinit(self.gpa); | ||
| 1383 | |||
| 1384 | if (zir_module.error_msg) |src_err_msg| { | ||
| 1385 | self.failed_files.putAssumeCapacityNoClobber( | ||
| 1386 | &root_scope.base, | ||
| 1387 | try Compilation.ErrorMsg.create(self.gpa, src_err_msg.byte_offset, "{}", .{src_err_msg.msg}), | ||
| 1388 | ); | ||
| 1389 | root_scope.status = .unloaded_parse_failure; | ||
| 1390 | return error.AnalysisFail; | ||
| 1391 | } | ||
| 1392 | |||
| 1393 | root_scope.status = .loaded_success; | ||
| 1394 | root_scope.contents = .{ .module = zir_module }; | ||
| 1395 | keep_zir_module = true; | ||
| 1396 | |||
| 1397 | return zir_module; | ||
| 1398 | }, | ||
| 1399 | |||
| 1400 | .unloaded_parse_failure, | ||
| 1401 | .unloaded_sema_failure, | ||
| 1402 | => return error.AnalysisFail, | ||
| 1403 | |||
| 1404 | .loaded_success, .loaded_sema_failure => return root_scope.contents.module, | ||
| 1405 | } | ||
| 1406 | } | ||
| 1407 | |||
| 1408 | fn getAstTree(self: *Module, container_scope: *Scope.Container) !*ast.Tree { | ||
| 1409 | const tracy = trace(@src()); | ||
| 1410 | defer tracy.end(); | ||
| 1411 | |||
| 1412 | const root_scope = container_scope.file_scope; | ||
| 1413 | |||
| 1414 | switch (root_scope.status) { | ||
| 1415 | .never_loaded, .unloaded_success => { | ||
| 1416 | try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1); | ||
| 1417 | |||
| 1418 | const source = try root_scope.getSource(self); | ||
| 1419 | |||
| 1420 | var keep_tree = false; | ||
| 1421 | const tree = try std.zig.parse(self.gpa, source); | ||
| 1422 | defer if (!keep_tree) tree.deinit(); | ||
| 1423 | |||
| 1424 | if (tree.errors.len != 0) { | ||
| 1425 | const parse_err = tree.errors[0]; | ||
| 1426 | |||
| 1427 | var msg = std.ArrayList(u8).init(self.gpa); | ||
| 1428 | defer msg.deinit(); | ||
| 1429 | |||
| 1430 | try parse_err.render(tree.token_ids, msg.outStream()); | ||
| 1431 | const err_msg = try self.gpa.create(Compilation.ErrorMsg); | ||
| 1432 | err_msg.* = .{ | ||
| 1433 | .msg = msg.toOwnedSlice(), | ||
| 1434 | .byte_offset = tree.token_locs[parse_err.loc()].start, | ||
| 1435 | }; | ||
| 1436 | |||
| 1437 | self.failed_files.putAssumeCapacityNoClobber(&root_scope.base, err_msg); | ||
| 1438 | root_scope.status = .unloaded_parse_failure; | ||
| 1439 | return error.AnalysisFail; | ||
| 1440 | } | ||
| 1441 | |||
| 1442 | root_scope.status = .loaded_success; | ||
| 1443 | root_scope.contents = .{ .tree = tree }; | ||
| 1444 | keep_tree = true; | ||
| 1445 | |||
| 1446 | return tree; | ||
| 1447 | }, | ||
| 1448 | |||
| 1449 | .unloaded_parse_failure => return error.AnalysisFail, | ||
| 1450 | |||
| 1451 | .loaded_success => return root_scope.contents.tree, | ||
| 1452 | } | ||
| 1453 | } | ||
| 1454 | |||
| 1455 | pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void { | ||
| 1456 | const tracy = trace(@src()); | ||
| 1457 | defer tracy.end(); | ||
| 1458 | |||
| 1459 | // We may be analyzing it for the first time, or this may be | ||
| 1460 | // an incremental update. This code handles both cases. | ||
| 1461 | const tree = try self.getAstTree(container_scope); | ||
| 1462 | const decls = tree.root_node.decls(); | ||
| 1463 | |||
| 1464 | try self.comp.work_queue.ensureUnusedCapacity(decls.len); | ||
| 1465 | try container_scope.decls.ensureCapacity(self.gpa, decls.len); | ||
| 1466 | |||
| 1467 | // Keep track of the decls that we expect to see in this file so that | ||
| 1468 | // we know which ones have been deleted. | ||
| 1469 | var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa); | ||
| 1470 | defer deleted_decls.deinit(); | ||
| 1471 | try deleted_decls.ensureCapacity(container_scope.decls.items().len); | ||
| 1472 | for (container_scope.decls.items()) |entry| { | ||
| 1473 | deleted_decls.putAssumeCapacityNoClobber(entry.key, {}); | ||
| 1474 | } | ||
| 1475 | |||
| 1476 | for (decls) |src_decl, decl_i| { | ||
| 1477 | if (src_decl.cast(ast.Node.FnProto)) |fn_proto| { | ||
| 1478 | // We will create a Decl for it regardless of analysis status. | ||
| 1479 | const name_tok = fn_proto.getNameToken() orelse { | ||
| 1480 | @panic("TODO missing function name"); | ||
| 1481 | }; | ||
| 1482 | |||
| 1483 | const name_loc = tree.token_locs[name_tok]; | ||
| 1484 | const name = tree.tokenSliceLoc(name_loc); | ||
| 1485 | const name_hash = container_scope.fullyQualifiedNameHash(name); | ||
| 1486 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl)); | ||
| 1487 | if (self.decl_table.get(name_hash)) |decl| { | ||
| 1488 | // Update the AST Node index of the decl, even if its contents are unchanged, it may | ||
| 1489 | // have been re-ordered. | ||
| 1490 | decl.src_index = decl_i; | ||
| 1491 | if (deleted_decls.remove(decl) == null) { | ||
| 1492 | decl.analysis = .sema_failure; | ||
| 1493 | const err_msg = try Compilation.ErrorMsg.create(self.gpa, tree.token_locs[name_tok].start, "redefinition of '{}'", .{decl.name}); | ||
| 1494 | errdefer err_msg.destroy(self.gpa); | ||
| 1495 | try self.failed_decls.putNoClobber(self.gpa, decl, err_msg); | ||
| 1496 | } else { | ||
| 1497 | if (!srcHashEql(decl.contents_hash, contents_hash)) { | ||
| 1498 | try self.markOutdatedDecl(decl); | ||
| 1499 | decl.contents_hash = contents_hash; | ||
| 1500 | } else switch (self.comp.bin_file.tag) { | ||
| 1501 | .coff => { | ||
| 1502 | // TODO Implement for COFF | ||
| 1503 | }, | ||
| 1504 | .elf => if (decl.fn_link.elf.len != 0) { | ||
| 1505 | // TODO Look into detecting when this would be unnecessary by storing enough state | ||
| 1506 | // in `Decl` to notice that the line number did not change. | ||
| 1507 | self.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); | ||
| 1508 | }, | ||
| 1509 | .macho => { | ||
| 1510 | // TODO Implement for MachO | ||
| 1511 | }, | ||
| 1512 | .c, .wasm => {}, | ||
| 1513 | } | ||
| 1514 | } | ||
| 1515 | } else { | ||
| 1516 | const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); | ||
| 1517 | container_scope.decls.putAssumeCapacity(new_decl, {}); | ||
| 1518 | if (fn_proto.getExternExportInlineToken()) |maybe_export_token| { | ||
| 1519 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { | ||
| 1520 | self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); | ||
| 1521 | } | ||
| 1522 | } | ||
| 1523 | } | ||
| 1524 | } else if (src_decl.castTag(.VarDecl)) |var_decl| { | ||
| 1525 | const name_loc = tree.token_locs[var_decl.name_token]; | ||
| 1526 | const name = tree.tokenSliceLoc(name_loc); | ||
| 1527 | const name_hash = container_scope.fullyQualifiedNameHash(name); | ||
| 1528 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl)); | ||
| 1529 | if (self.decl_table.get(name_hash)) |decl| { | ||
| 1530 | // Update the AST Node index of the decl, even if its contents are unchanged, it may | ||
| 1531 | // have been re-ordered. | ||
| 1532 | decl.src_index = decl_i; | ||
| 1533 | if (deleted_decls.remove(decl) == null) { | ||
| 1534 | decl.analysis = .sema_failure; | ||
| 1535 | const err_msg = try Compilation.ErrorMsg.create(self.gpa, name_loc.start, "redefinition of '{}'", .{decl.name}); | ||
| 1536 | errdefer err_msg.destroy(self.gpa); | ||
| 1537 | try self.failed_decls.putNoClobber(self.gpa, decl, err_msg); | ||
| 1538 | } else if (!srcHashEql(decl.contents_hash, contents_hash)) { | ||
| 1539 | try self.markOutdatedDecl(decl); | ||
| 1540 | decl.contents_hash = contents_hash; | ||
| 1541 | } | ||
| 1542 | } else { | ||
| 1543 | const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); | ||
| 1544 | container_scope.decls.putAssumeCapacity(new_decl, {}); | ||
| 1545 | if (var_decl.getExternExportToken()) |maybe_export_token| { | ||
| 1546 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { | ||
| 1547 | self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); | ||
| 1548 | } | ||
| 1549 | } | ||
| 1550 | } | ||
| 1551 | } else if (src_decl.castTag(.Comptime)) |comptime_node| { | ||
| 1552 | const name_index = self.getNextAnonNameIndex(); | ||
| 1553 | const name = try std.fmt.allocPrint(self.gpa, "__comptime_{}", .{name_index}); | ||
| 1554 | defer self.gpa.free(name); | ||
| 1555 | |||
| 1556 | const name_hash = container_scope.fullyQualifiedNameHash(name); | ||
| 1557 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl)); | ||
| 1558 | |||
| 1559 | const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); | ||
| 1560 | container_scope.decls.putAssumeCapacity(new_decl, {}); | ||
| 1561 | self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); | ||
| 1562 | } else if (src_decl.castTag(.ContainerField)) |container_field| { | ||
| 1563 | log.err("TODO: analyze container field", .{}); | ||
| 1564 | } else if (src_decl.castTag(.TestDecl)) |test_decl| { | ||
| 1565 | log.err("TODO: analyze test decl", .{}); | ||
| 1566 | } else if (src_decl.castTag(.Use)) |use_decl| { | ||
| 1567 | log.err("TODO: analyze usingnamespace decl", .{}); | ||
| 1568 | } else { | ||
| 1569 | unreachable; | ||
| 1570 | } | ||
| 1571 | } | ||
| 1572 | // Handle explicitly deleted decls from the source code. Not to be confused | ||
| 1573 | // with when we delete decls because they are no longer referenced. | ||
| 1574 | for (deleted_decls.items()) |entry| { | ||
| 1575 | log.debug("noticed '{}' deleted from source\n", .{entry.key.name}); | ||
| 1576 | try self.deleteDecl(entry.key); | ||
| 1577 | } | ||
| 1578 | } | ||
| 1579 | |||
| 1580 | pub fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void { | ||
| 1581 | // We may be analyzing it for the first time, or this may be | ||
| 1582 | // an incremental update. This code handles both cases. | ||
| 1583 | const src_module = try self.getSrcModule(root_scope); | ||
| 1584 | |||
| 1585 | try self.comp.work_queue.ensureUnusedCapacity(src_module.decls.len); | ||
| 1586 | try root_scope.decls.ensureCapacity(self.gpa, src_module.decls.len); | ||
| 1587 | |||
| 1588 | var exports_to_resolve = std.ArrayList(*zir.Decl).init(self.gpa); | ||
| 1589 | defer exports_to_resolve.deinit(); | ||
| 1590 | |||
| 1591 | // Keep track of the decls that we expect to see in this file so that | ||
| 1592 | // we know which ones have been deleted. | ||
| 1593 | var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa); | ||
| 1594 | defer deleted_decls.deinit(); | ||
| 1595 | try deleted_decls.ensureCapacity(self.decl_table.items().len); | ||
| 1596 | for (self.decl_table.items()) |entry| { | ||
| 1597 | deleted_decls.putAssumeCapacityNoClobber(entry.value, {}); | ||
| 1598 | } | ||
| 1599 | |||
| 1600 | for (src_module.decls) |src_decl, decl_i| { | ||
| 1601 | const name_hash = root_scope.fullyQualifiedNameHash(src_decl.name); | ||
| 1602 | if (self.decl_table.get(name_hash)) |decl| { | ||
| 1603 | deleted_decls.removeAssertDiscard(decl); | ||
| 1604 | if (!srcHashEql(src_decl.contents_hash, decl.contents_hash)) { | ||
| 1605 | try self.markOutdatedDecl(decl); | ||
| 1606 | decl.contents_hash = src_decl.contents_hash; | ||
| 1607 | } | ||
| 1608 | } else { | ||
| 1609 | const new_decl = try self.createNewDecl( | ||
| 1610 | &root_scope.base, | ||
| 1611 | src_decl.name, | ||
| 1612 | decl_i, | ||
| 1613 | name_hash, | ||
| 1614 | src_decl.contents_hash, | ||
| 1615 | ); | ||
| 1616 | root_scope.decls.appendAssumeCapacity(new_decl); | ||
| 1617 | if (src_decl.inst.cast(zir.Inst.Export)) |export_inst| { | ||
| 1618 | try exports_to_resolve.append(src_decl); | ||
| 1619 | } | ||
| 1620 | } | ||
| 1621 | } | ||
| 1622 | for (exports_to_resolve.items) |export_decl| { | ||
| 1623 | _ = try zir_sema.resolveZirDecl(self, &root_scope.base, export_decl); | ||
| 1624 | } | ||
| 1625 | // Handle explicitly deleted decls from the source code. Not to be confused | ||
| 1626 | // with when we delete decls because they are no longer referenced. | ||
| 1627 | for (deleted_decls.items()) |entry| { | ||
| 1628 | log.debug("noticed '{}' deleted from source\n", .{entry.key.name}); | ||
| 1629 | try self.deleteDecl(entry.key); | ||
| 1630 | } | ||
| 1631 | } | ||
| 1632 | |||
| 1633 | pub fn deleteDecl(self: *Module, decl: *Decl) !void { | ||
| 1634 | try self.deletion_set.ensureCapacity(self.gpa, self.deletion_set.items.len + decl.dependencies.items().len); | ||
| 1635 | |||
| 1636 | // Remove from the namespace it resides in. In the case of an anonymous Decl it will | ||
| 1637 | // not be present in the set, and this does nothing. | ||
| 1638 | decl.scope.removeDecl(decl); | ||
| 1639 | |||
| 1640 | log.debug("deleting decl '{}'\n", .{decl.name}); | ||
| 1641 | const name_hash = decl.fullyQualifiedNameHash(); | ||
| 1642 | self.decl_table.removeAssertDiscard(name_hash); | ||
| 1643 | // Remove itself from its dependencies, because we are about to destroy the decl pointer. | ||
| 1644 | for (decl.dependencies.items()) |entry| { | ||
| 1645 | const dep = entry.key; | ||
| 1646 | dep.removeDependant(decl); | ||
| 1647 | if (dep.dependants.items().len == 0 and !dep.deletion_flag) { | ||
| 1648 | // We don't recursively perform a deletion here, because during the update, | ||
| 1649 | // another reference to it may turn up. | ||
| 1650 | dep.deletion_flag = true; | ||
| 1651 | self.deletion_set.appendAssumeCapacity(dep); | ||
| 1652 | } | ||
| 1653 | } | ||
| 1654 | // Anything that depends on this deleted decl certainly needs to be re-analyzed. | ||
| 1655 | for (decl.dependants.items()) |entry| { | ||
| 1656 | const dep = entry.key; | ||
| 1657 | dep.removeDependency(decl); | ||
| 1658 | if (dep.analysis != .outdated) { | ||
| 1659 | // TODO Move this failure possibility to the top of the function. | ||
| 1660 | try self.markOutdatedDecl(dep); | ||
| 1661 | } | ||
| 1662 | } | ||
| 1663 | if (self.failed_decls.remove(decl)) |entry| { | ||
| 1664 | entry.value.destroy(self.gpa); | ||
| 1665 | } | ||
| 1666 | self.deleteDeclExports(decl); | ||
| 1667 | self.comp.bin_file.freeDecl(decl); | ||
| 1668 | decl.destroy(self.gpa); | ||
| 1669 | } | ||
| 1670 | |||
| 1671 | /// Delete all the Export objects that are caused by this Decl. Re-analysis of | ||
| 1672 | /// this Decl will cause them to be re-created (or not). | ||
| 1673 | fn deleteDeclExports(self: *Module, decl: *Decl) void { | ||
| 1674 | const kv = self.export_owners.remove(decl) orelse return; | ||
| 1675 | |||
| 1676 | for (kv.value) |exp| { | ||
| 1677 | if (self.decl_exports.getEntry(exp.exported_decl)) |decl_exports_kv| { | ||
| 1678 | // Remove exports with owner_decl matching the regenerating decl. | ||
| 1679 | const list = decl_exports_kv.value; | ||
| 1680 | var i: usize = 0; | ||
| 1681 | var new_len = list.len; | ||
| 1682 | while (i < new_len) { | ||
| 1683 | if (list[i].owner_decl == decl) { | ||
| 1684 | mem.copyBackwards(*Export, list[i..], list[i + 1 .. new_len]); | ||
| 1685 | new_len -= 1; | ||
| 1686 | } else { | ||
| 1687 | i += 1; | ||
| 1688 | } | ||
| 1689 | } | ||
| 1690 | decl_exports_kv.value = self.gpa.shrink(list, new_len); | ||
| 1691 | if (new_len == 0) { | ||
| 1692 | self.decl_exports.removeAssertDiscard(exp.exported_decl); | ||
| 1693 | } | ||
| 1694 | } | ||
| 1695 | if (self.comp.bin_file.cast(link.File.Elf)) |elf| { | ||
| 1696 | elf.deleteExport(exp.link); | ||
| 1697 | } | ||
| 1698 | if (self.failed_exports.remove(exp)) |entry| { | ||
| 1699 | entry.value.destroy(self.gpa); | ||
| 1700 | } | ||
| 1701 | _ = self.symbol_exports.remove(exp.options.name); | ||
| 1702 | self.gpa.free(exp.options.name); | ||
| 1703 | self.gpa.destroy(exp); | ||
| 1704 | } | ||
| 1705 | self.gpa.free(kv.value); | ||
| 1706 | } | ||
| 1707 | |||
| 1708 | pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { | ||
| 1709 | const tracy = trace(@src()); | ||
| 1710 | defer tracy.end(); | ||
| 1711 | |||
| 1712 | // Use the Decl's arena for function memory. | ||
| 1713 | var arena = decl.typed_value.most_recent.arena.?.promote(self.gpa); | ||
| 1714 | defer decl.typed_value.most_recent.arena.?.* = arena.state; | ||
| 1715 | var inner_block: Scope.Block = .{ | ||
| 1716 | .parent = null, | ||
| 1717 | .func = func, | ||
| 1718 | .decl = decl, | ||
| 1719 | .instructions = .{}, | ||
| 1720 | .arena = &arena.allocator, | ||
| 1721 | .is_comptime = false, | ||
| 1722 | }; | ||
| 1723 | defer inner_block.instructions.deinit(self.gpa); | ||
| 1724 | |||
| 1725 | const fn_zir = func.analysis.queued; | ||
| 1726 | defer fn_zir.arena.promote(self.gpa).deinit(); | ||
| 1727 | func.analysis = .{ .in_progress = {} }; | ||
| 1728 | log.debug("set {} to in_progress\n", .{decl.name}); | ||
| 1729 | |||
| 1730 | try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body); | ||
| 1731 | |||
| 1732 | const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items); | ||
| 1733 | func.analysis = .{ .success = .{ .instructions = instructions } }; | ||
| 1734 | log.debug("set {} to success\n", .{decl.name}); | ||
| 1735 | } | ||
| 1736 | |||
| 1737 | fn markOutdatedDecl(self: *Module, decl: *Decl) !void { | ||
| 1738 | log.debug("mark {} outdated\n", .{decl.name}); | ||
| 1739 | try self.comp.work_queue.writeItem(.{ .analyze_decl = decl }); | ||
| 1740 | if (self.failed_decls.remove(decl)) |entry| { | ||
| 1741 | entry.value.destroy(self.gpa); | ||
| 1742 | } | ||
| 1743 | decl.analysis = .outdated; | ||
| 1744 | } | ||
| 1745 | |||
| 1746 | fn allocateNewDecl( | ||
| 1747 | self: *Module, | ||
| 1748 | scope: *Scope, | ||
| 1749 | src_index: usize, | ||
| 1750 | contents_hash: std.zig.SrcHash, | ||
| 1751 | ) !*Decl { | ||
| 1752 | const new_decl = try self.gpa.create(Decl); | ||
| 1753 | new_decl.* = .{ | ||
| 1754 | .name = "", | ||
| 1755 | .scope = scope.namespace(), | ||
| 1756 | .src_index = src_index, | ||
| 1757 | .typed_value = .{ .never_succeeded = {} }, | ||
| 1758 | .analysis = .unreferenced, | ||
| 1759 | .deletion_flag = false, | ||
| 1760 | .contents_hash = contents_hash, | ||
| 1761 | .link = switch (self.comp.bin_file.tag) { | ||
| 1762 | .coff => .{ .coff = link.File.Coff.TextBlock.empty }, | ||
| 1763 | .elf => .{ .elf = link.File.Elf.TextBlock.empty }, | ||
| 1764 | .macho => .{ .macho = link.File.MachO.TextBlock.empty }, | ||
| 1765 | .c => .{ .c = {} }, | ||
| 1766 | .wasm => .{ .wasm = {} }, | ||
| 1767 | }, | ||
| 1768 | .fn_link = switch (self.comp.bin_file.tag) { | ||
| 1769 | .coff => .{ .coff = {} }, | ||
| 1770 | .elf => .{ .elf = link.File.Elf.SrcFn.empty }, | ||
| 1771 | .macho => .{ .macho = link.File.MachO.SrcFn.empty }, | ||
| 1772 | .c => .{ .c = {} }, | ||
| 1773 | .wasm => .{ .wasm = null }, | ||
| 1774 | }, | ||
| 1775 | .generation = 0, | ||
| 1776 | .is_pub = false, | ||
| 1777 | }; | ||
| 1778 | return new_decl; | ||
| 1779 | } | ||
| 1780 | |||
| 1781 | fn createNewDecl( | ||
| 1782 | self: *Module, | ||
| 1783 | scope: *Scope, | ||
| 1784 | decl_name: []const u8, | ||
| 1785 | src_index: usize, | ||
| 1786 | name_hash: Scope.NameHash, | ||
| 1787 | contents_hash: std.zig.SrcHash, | ||
| 1788 | ) !*Decl { | ||
| 1789 | try self.decl_table.ensureCapacity(self.gpa, self.decl_table.items().len + 1); | ||
| 1790 | const new_decl = try self.allocateNewDecl(scope, src_index, contents_hash); | ||
| 1791 | errdefer self.gpa.destroy(new_decl); | ||
| 1792 | new_decl.name = try mem.dupeZ(self.gpa, u8, decl_name); | ||
| 1793 | self.decl_table.putAssumeCapacityNoClobber(name_hash, new_decl); | ||
| 1794 | return new_decl; | ||
| 1795 | } | ||
| 1796 | |||
| 1797 | /// Get error value for error tag `name`. | ||
| 1798 | pub fn getErrorValue(self: *Module, name: []const u8) !std.StringHashMapUnmanaged(u16).Entry { | ||
| 1799 | const gop = try self.global_error_set.getOrPut(self.gpa, name); | ||
| 1800 | if (gop.found_existing) | ||
| 1801 | return gop.entry.*; | ||
| 1802 | errdefer self.global_error_set.removeAssertDiscard(name); | ||
| 1803 | |||
| 1804 | gop.entry.key = try self.gpa.dupe(u8, name); | ||
| 1805 | gop.entry.value = @intCast(u16, self.global_error_set.count() - 1); | ||
| 1806 | return gop.entry.*; | ||
| 1807 | } | ||
| 1808 | |||
| 1809 | pub fn requireFunctionBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block { | ||
| 1810 | return scope.cast(Scope.Block) orelse | ||
| 1811 | return self.fail(scope, src, "instruction illegal outside function body", .{}); | ||
| 1812 | } | ||
| 1813 | |||
| 1814 | pub fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block { | ||
| 1815 | const block = try self.requireFunctionBlock(scope, src); | ||
| 1816 | if (block.is_comptime) { | ||
| 1817 | return self.fail(scope, src, "unable to resolve comptime value", .{}); | ||
| 1818 | } | ||
| 1819 | return block; | ||
| 1820 | } | ||
| 1821 | |||
| 1822 | pub fn resolveConstValue(self: *Module, scope: *Scope, base: *Inst) !Value { | ||
| 1823 | return (try self.resolveDefinedValue(scope, base)) orelse | ||
| 1824 | return self.fail(scope, base.src, "unable to resolve comptime value", .{}); | ||
| 1825 | } | ||
| 1826 | |||
| 1827 | pub fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value { | ||
| 1828 | if (base.value()) |val| { | ||
| 1829 | if (val.isUndef()) { | ||
| 1830 | return self.fail(scope, base.src, "use of undefined value here causes undefined behavior", .{}); | ||
| 1831 | } | ||
| 1832 | return val; | ||
| 1833 | } | ||
| 1834 | return null; | ||
| 1835 | } | ||
| 1836 | |||
| 1837 | pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, borrowed_symbol_name: []const u8, exported_decl: *Decl) !void { | ||
| 1838 | try self.ensureDeclAnalyzed(exported_decl); | ||
| 1839 | const typed_value = exported_decl.typed_value.most_recent.typed_value; | ||
| 1840 | switch (typed_value.ty.zigTypeTag()) { | ||
| 1841 | .Fn => {}, | ||
| 1842 | else => return self.fail(scope, src, "unable to export type '{}'", .{typed_value.ty}), | ||
| 1843 | } | ||
| 1844 | |||
| 1845 | try self.decl_exports.ensureCapacity(self.gpa, self.decl_exports.items().len + 1); | ||
| 1846 | try self.export_owners.ensureCapacity(self.gpa, self.export_owners.items().len + 1); | ||
| 1847 | |||
| 1848 | const new_export = try self.gpa.create(Export); | ||
| 1849 | errdefer self.gpa.destroy(new_export); | ||
| 1850 | |||
| 1851 | const symbol_name = try self.gpa.dupe(u8, borrowed_symbol_name); | ||
| 1852 | errdefer self.gpa.free(symbol_name); | ||
| 1853 | |||
| 1854 | const owner_decl = scope.decl().?; | ||
| 1855 | |||
| 1856 | new_export.* = .{ | ||
| 1857 | .options = .{ .name = symbol_name }, | ||
| 1858 | .src = src, | ||
| 1859 | .link = .{}, | ||
| 1860 | .owner_decl = owner_decl, | ||
| 1861 | .exported_decl = exported_decl, | ||
| 1862 | .status = .in_progress, | ||
| 1863 | }; | ||
| 1864 | |||
| 1865 | // Add to export_owners table. | ||
| 1866 | const eo_gop = self.export_owners.getOrPutAssumeCapacity(owner_decl); | ||
| 1867 | if (!eo_gop.found_existing) { | ||
| 1868 | eo_gop.entry.value = &[0]*Export{}; | ||
| 1869 | } | ||
| 1870 | eo_gop.entry.value = try self.gpa.realloc(eo_gop.entry.value, eo_gop.entry.value.len + 1); | ||
| 1871 | eo_gop.entry.value[eo_gop.entry.value.len - 1] = new_export; | ||
| 1872 | errdefer eo_gop.entry.value = self.gpa.shrink(eo_gop.entry.value, eo_gop.entry.value.len - 1); | ||
| 1873 | |||
| 1874 | // Add to exported_decl table. | ||
| 1875 | const de_gop = self.decl_exports.getOrPutAssumeCapacity(exported_decl); | ||
| 1876 | if (!de_gop.found_existing) { | ||
| 1877 | de_gop.entry.value = &[0]*Export{}; | ||
| 1878 | } | ||
| 1879 | de_gop.entry.value = try self.gpa.realloc(de_gop.entry.value, de_gop.entry.value.len + 1); | ||
| 1880 | de_gop.entry.value[de_gop.entry.value.len - 1] = new_export; | ||
| 1881 | errdefer de_gop.entry.value = self.gpa.shrink(de_gop.entry.value, de_gop.entry.value.len - 1); | ||
| 1882 | |||
| 1883 | if (self.symbol_exports.get(symbol_name)) |_| { | ||
| 1884 | try self.failed_exports.ensureCapacity(self.gpa, self.failed_exports.items().len + 1); | ||
| 1885 | self.failed_exports.putAssumeCapacityNoClobber(new_export, try Compilation.ErrorMsg.create( | ||
| 1886 | self.gpa, | ||
| 1887 | src, | ||
| 1888 | "exported symbol collision: {}", | ||
| 1889 | .{symbol_name}, | ||
| 1890 | )); | ||
| 1891 | // TODO: add a note | ||
| 1892 | new_export.status = .failed; | ||
| 1893 | return; | ||
| 1894 | } | ||
| 1895 | |||
| 1896 | try self.symbol_exports.putNoClobber(self.gpa, symbol_name, new_export); | ||
| 1897 | self.comp.bin_file.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) { | ||
| 1898 | error.OutOfMemory => return error.OutOfMemory, | ||
| 1899 | else => { | ||
| 1900 | try self.failed_exports.ensureCapacity(self.gpa, self.failed_exports.items().len + 1); | ||
| 1901 | self.failed_exports.putAssumeCapacityNoClobber(new_export, try Compilation.ErrorMsg.create( | ||
| 1902 | self.gpa, | ||
| 1903 | src, | ||
| 1904 | "unable to export: {}", | ||
| 1905 | .{@errorName(err)}, | ||
| 1906 | )); | ||
| 1907 | new_export.status = .failed_retryable; | ||
| 1908 | }, | ||
| 1909 | }; | ||
| 1910 | } | ||
| 1911 | |||
| 1912 | pub fn addNoOp( | ||
| 1913 | self: *Module, | ||
| 1914 | block: *Scope.Block, | ||
| 1915 | src: usize, | ||
| 1916 | ty: Type, | ||
| 1917 | comptime tag: Inst.Tag, | ||
| 1918 | ) !*Inst { | ||
| 1919 | const inst = try block.arena.create(tag.Type()); | ||
| 1920 | inst.* = .{ | ||
| 1921 | .base = .{ | ||
| 1922 | .tag = tag, | ||
| 1923 | .ty = ty, | ||
| 1924 | .src = src, | ||
| 1925 | }, | ||
| 1926 | }; | ||
| 1927 | try block.instructions.append(self.gpa, &inst.base); | ||
| 1928 | return &inst.base; | ||
| 1929 | } | ||
| 1930 | |||
| 1931 | pub fn addUnOp( | ||
| 1932 | self: *Module, | ||
| 1933 | block: *Scope.Block, | ||
| 1934 | src: usize, | ||
| 1935 | ty: Type, | ||
| 1936 | tag: Inst.Tag, | ||
| 1937 | operand: *Inst, | ||
| 1938 | ) !*Inst { | ||
| 1939 | const inst = try block.arena.create(Inst.UnOp); | ||
| 1940 | inst.* = .{ | ||
| 1941 | .base = .{ | ||
| 1942 | .tag = tag, | ||
| 1943 | .ty = ty, | ||
| 1944 | .src = src, | ||
| 1945 | }, | ||
| 1946 | .operand = operand, | ||
| 1947 | }; | ||
| 1948 | try block.instructions.append(self.gpa, &inst.base); | ||
| 1949 | return &inst.base; | ||
| 1950 | } | ||
| 1951 | |||
| 1952 | pub fn addBinOp( | ||
| 1953 | self: *Module, | ||
| 1954 | block: *Scope.Block, | ||
| 1955 | src: usize, | ||
| 1956 | ty: Type, | ||
| 1957 | tag: Inst.Tag, | ||
| 1958 | lhs: *Inst, | ||
| 1959 | rhs: *Inst, | ||
| 1960 | ) !*Inst { | ||
| 1961 | const inst = try block.arena.create(Inst.BinOp); | ||
| 1962 | inst.* = .{ | ||
| 1963 | .base = .{ | ||
| 1964 | .tag = tag, | ||
| 1965 | .ty = ty, | ||
| 1966 | .src = src, | ||
| 1967 | }, | ||
| 1968 | .lhs = lhs, | ||
| 1969 | .rhs = rhs, | ||
| 1970 | }; | ||
| 1971 | try block.instructions.append(self.gpa, &inst.base); | ||
| 1972 | return &inst.base; | ||
| 1973 | } | ||
| 1974 | |||
| 1975 | pub fn addArg(self: *Module, block: *Scope.Block, src: usize, ty: Type, name: [*:0]const u8) !*Inst { | ||
| 1976 | const inst = try block.arena.create(Inst.Arg); | ||
| 1977 | inst.* = .{ | ||
| 1978 | .base = .{ | ||
| 1979 | .tag = .arg, | ||
| 1980 | .ty = ty, | ||
| 1981 | .src = src, | ||
| 1982 | }, | ||
| 1983 | .name = name, | ||
| 1984 | }; | ||
| 1985 | try block.instructions.append(self.gpa, &inst.base); | ||
| 1986 | return &inst.base; | ||
| 1987 | } | ||
| 1988 | |||
| 1989 | pub fn addBr( | ||
| 1990 | self: *Module, | ||
| 1991 | scope_block: *Scope.Block, | ||
| 1992 | src: usize, | ||
| 1993 | target_block: *Inst.Block, | ||
| 1994 | operand: *Inst, | ||
| 1995 | ) !*Inst { | ||
| 1996 | const inst = try scope_block.arena.create(Inst.Br); | ||
| 1997 | inst.* = .{ | ||
| 1998 | .base = .{ | ||
| 1999 | .tag = .br, | ||
| 2000 | .ty = Type.initTag(.noreturn), | ||
| 2001 | .src = src, | ||
| 2002 | }, | ||
| 2003 | .operand = operand, | ||
| 2004 | .block = target_block, | ||
| 2005 | }; | ||
| 2006 | try scope_block.instructions.append(self.gpa, &inst.base); | ||
| 2007 | return &inst.base; | ||
| 2008 | } | ||
| 2009 | |||
| 2010 | pub fn addCondBr( | ||
| 2011 | self: *Module, | ||
| 2012 | block: *Scope.Block, | ||
| 2013 | src: usize, | ||
| 2014 | condition: *Inst, | ||
| 2015 | then_body: ir.Body, | ||
| 2016 | else_body: ir.Body, | ||
| 2017 | ) !*Inst { | ||
| 2018 | const inst = try block.arena.create(Inst.CondBr); | ||
| 2019 | inst.* = .{ | ||
| 2020 | .base = .{ | ||
| 2021 | .tag = .condbr, | ||
| 2022 | .ty = Type.initTag(.noreturn), | ||
| 2023 | .src = src, | ||
| 2024 | }, | ||
| 2025 | .condition = condition, | ||
| 2026 | .then_body = then_body, | ||
| 2027 | .else_body = else_body, | ||
| 2028 | }; | ||
| 2029 | try block.instructions.append(self.gpa, &inst.base); | ||
| 2030 | return &inst.base; | ||
| 2031 | } | ||
| 2032 | |||
| 2033 | pub fn addCall( | ||
| 2034 | self: *Module, | ||
| 2035 | block: *Scope.Block, | ||
| 2036 | src: usize, | ||
| 2037 | ty: Type, | ||
| 2038 | func: *Inst, | ||
| 2039 | args: []const *Inst, | ||
| 2040 | ) !*Inst { | ||
| 2041 | const inst = try block.arena.create(Inst.Call); | ||
| 2042 | inst.* = .{ | ||
| 2043 | .base = .{ | ||
| 2044 | .tag = .call, | ||
| 2045 | .ty = ty, | ||
| 2046 | .src = src, | ||
| 2047 | }, | ||
| 2048 | .func = func, | ||
| 2049 | .args = args, | ||
| 2050 | }; | ||
| 2051 | try block.instructions.append(self.gpa, &inst.base); | ||
| 2052 | return &inst.base; | ||
| 2053 | } | ||
| 2054 | |||
| 2055 | pub fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst { | ||
| 2056 | const const_inst = try scope.arena().create(Inst.Constant); | ||
| 2057 | const_inst.* = .{ | ||
| 2058 | .base = .{ | ||
| 2059 | .tag = Inst.Constant.base_tag, | ||
| 2060 | .ty = typed_value.ty, | ||
| 2061 | .src = src, | ||
| 2062 | }, | ||
| 2063 | .val = typed_value.val, | ||
| 2064 | }; | ||
| 2065 | return &const_inst.base; | ||
| 2066 | } | ||
| 2067 | |||
| 2068 | pub fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst { | ||
| 2069 | return self.constInst(scope, src, .{ | ||
| 2070 | .ty = Type.initTag(.type), | ||
| 2071 | .val = try ty.toValue(scope.arena()), | ||
| 2072 | }); | ||
| 2073 | } | ||
| 2074 | |||
| 2075 | pub fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst { | ||
| 2076 | return self.constInst(scope, src, .{ | ||
| 2077 | .ty = Type.initTag(.void), | ||
| 2078 | .val = Value.initTag(.void_value), | ||
| 2079 | }); | ||
| 2080 | } | ||
| 2081 | |||
| 2082 | pub fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst { | ||
| 2083 | return self.constInst(scope, src, .{ | ||
| 2084 | .ty = Type.initTag(.noreturn), | ||
| 2085 | .val = Value.initTag(.unreachable_value), | ||
| 2086 | }); | ||
| 2087 | } | ||
| 2088 | |||
| 2089 | pub fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst { | ||
| 2090 | return self.constInst(scope, src, .{ | ||
| 2091 | .ty = ty, | ||
| 2092 | .val = Value.initTag(.undef), | ||
| 2093 | }); | ||
| 2094 | } | ||
| 2095 | |||
| 2096 | pub fn constBool(self: *Module, scope: *Scope, src: usize, v: bool) !*Inst { | ||
| 2097 | return self.constInst(scope, src, .{ | ||
| 2098 | .ty = Type.initTag(.bool), | ||
| 2099 | .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)], | ||
| 2100 | }); | ||
| 2101 | } | ||
| 2102 | |||
| 2103 | pub fn constIntUnsigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: u64) !*Inst { | ||
| 2104 | const int_payload = try scope.arena().create(Value.Payload.Int_u64); | ||
| 2105 | int_payload.* = .{ .int = int }; | ||
| 2106 | |||
| 2107 | return self.constInst(scope, src, .{ | ||
| 2108 | .ty = ty, | ||
| 2109 | .val = Value.initPayload(&int_payload.base), | ||
| 2110 | }); | ||
| 2111 | } | ||
| 2112 | |||
| 2113 | pub fn constIntSigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: i64) !*Inst { | ||
| 2114 | const int_payload = try scope.arena().create(Value.Payload.Int_i64); | ||
| 2115 | int_payload.* = .{ .int = int }; | ||
| 2116 | |||
| 2117 | return self.constInst(scope, src, .{ | ||
| 2118 | .ty = ty, | ||
| 2119 | .val = Value.initPayload(&int_payload.base), | ||
| 2120 | }); | ||
| 2121 | } | ||
| 2122 | |||
| 2123 | pub fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigIntConst) !*Inst { | ||
| 2124 | const val_payload = if (big_int.positive) blk: { | ||
| 2125 | if (big_int.to(u64)) |x| { | ||
| 2126 | return self.constIntUnsigned(scope, src, ty, x); | ||
| 2127 | } else |err| switch (err) { | ||
| 2128 | error.NegativeIntoUnsigned => unreachable, | ||
| 2129 | error.TargetTooSmall => {}, // handled below | ||
| 2130 | } | ||
| 2131 | const big_int_payload = try scope.arena().create(Value.Payload.IntBigPositive); | ||
| 2132 | big_int_payload.* = .{ .limbs = big_int.limbs }; | ||
| 2133 | break :blk &big_int_payload.base; | ||
| 2134 | } else blk: { | ||
| 2135 | if (big_int.to(i64)) |x| { | ||
| 2136 | return self.constIntSigned(scope, src, ty, x); | ||
| 2137 | } else |err| switch (err) { | ||
| 2138 | error.NegativeIntoUnsigned => unreachable, | ||
| 2139 | error.TargetTooSmall => {}, // handled below | ||
| 2140 | } | ||
| 2141 | const big_int_payload = try scope.arena().create(Value.Payload.IntBigNegative); | ||
| 2142 | big_int_payload.* = .{ .limbs = big_int.limbs }; | ||
| 2143 | break :blk &big_int_payload.base; | ||
| 2144 | }; | ||
| 2145 | |||
| 2146 | return self.constInst(scope, src, .{ | ||
| 2147 | .ty = ty, | ||
| 2148 | .val = Value.initPayload(val_payload), | ||
| 2149 | }); | ||
| 2150 | } | ||
| 2151 | |||
| 2152 | pub fn createAnonymousDecl( | ||
| 2153 | self: *Module, | ||
| 2154 | scope: *Scope, | ||
| 2155 | decl_arena: *std.heap.ArenaAllocator, | ||
| 2156 | typed_value: TypedValue, | ||
| 2157 | ) !*Decl { | ||
| 2158 | const name_index = self.getNextAnonNameIndex(); | ||
| 2159 | const scope_decl = scope.decl().?; | ||
| 2160 | const name = try std.fmt.allocPrint(self.gpa, "{}__anon_{}", .{ scope_decl.name, name_index }); | ||
| 2161 | defer self.gpa.free(name); | ||
| 2162 | const name_hash = scope.namespace().fullyQualifiedNameHash(name); | ||
| 2163 | const src_hash: std.zig.SrcHash = undefined; | ||
| 2164 | const new_decl = try self.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash); | ||
| 2165 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); | ||
| 2166 | |||
| 2167 | decl_arena_state.* = decl_arena.state; | ||
| 2168 | new_decl.typed_value = .{ | ||
| 2169 | .most_recent = .{ | ||
| 2170 | .typed_value = typed_value, | ||
| 2171 | .arena = decl_arena_state, | ||
| 2172 | }, | ||
| 2173 | }; | ||
| 2174 | new_decl.analysis = .complete; | ||
| 2175 | new_decl.generation = self.generation; | ||
| 2176 | |||
| 2177 | // TODO: This generates the Decl into the machine code file if it is of a type that is non-zero size. | ||
| 2178 | // We should be able to further improve the compiler to not omit Decls which are only referenced at | ||
| 2179 | // compile-time and not runtime. | ||
| 2180 | if (typed_value.ty.hasCodeGenBits()) { | ||
| 2181 | try self.comp.bin_file.allocateDeclIndexes(new_decl); | ||
| 2182 | try self.comp.work_queue.writeItem(.{ .codegen_decl = new_decl }); | ||
| 2183 | } | ||
| 2184 | |||
| 2185 | return new_decl; | ||
| 2186 | } | ||
| 2187 | |||
| 2188 | fn getNextAnonNameIndex(self: *Module) usize { | ||
| 2189 | return @atomicRmw(usize, &self.next_anon_name_index, .Add, 1, .Monotonic); | ||
| 2190 | } | ||
| 2191 | |||
| 2192 | pub fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*Decl { | ||
| 2193 | const namespace = scope.namespace(); | ||
| 2194 | const name_hash = namespace.fullyQualifiedNameHash(ident_name); | ||
| 2195 | return self.decl_table.get(name_hash); | ||
| 2196 | } | ||
| 2197 | |||
| 2198 | pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst { | ||
| 2199 | const scope_decl = scope.decl().?; | ||
| 2200 | try self.declareDeclDependency(scope_decl, decl); | ||
| 2201 | self.ensureDeclAnalyzed(decl) catch |err| { | ||
| 2202 | if (scope.cast(Scope.Block)) |block| { | ||
| 2203 | if (block.func) |func| { | ||
| 2204 | func.analysis = .dependency_failure; | ||
| 2205 | } else { | ||
| 2206 | block.decl.analysis = .dependency_failure; | ||
| 2207 | } | ||
| 2208 | } else { | ||
| 2209 | scope_decl.analysis = .dependency_failure; | ||
| 2210 | } | ||
| 2211 | return err; | ||
| 2212 | }; | ||
| 2213 | |||
| 2214 | const decl_tv = try decl.typedValue(); | ||
| 2215 | if (decl_tv.val.tag() == .variable) { | ||
| 2216 | return self.analyzeVarRef(scope, src, decl_tv); | ||
| 2217 | } | ||
| 2218 | const ty = try self.simplePtrType(scope, src, decl_tv.ty, false, .One); | ||
| 2219 | const val_payload = try scope.arena().create(Value.Payload.DeclRef); | ||
| 2220 | val_payload.* = .{ .decl = decl }; | ||
| 2221 | |||
| 2222 | return self.constInst(scope, src, .{ | ||
| 2223 | .ty = ty, | ||
| 2224 | .val = Value.initPayload(&val_payload.base), | ||
| 2225 | }); | ||
| 2226 | } | ||
| 2227 | |||
| 2228 | fn analyzeVarRef(self: *Module, scope: *Scope, src: usize, tv: TypedValue) InnerError!*Inst { | ||
| 2229 | const variable = tv.val.cast(Value.Payload.Variable).?.variable; | ||
| 2230 | |||
| 2231 | const ty = try self.simplePtrType(scope, src, tv.ty, variable.is_mutable, .One); | ||
| 2232 | if (!variable.is_mutable and !variable.is_extern) { | ||
| 2233 | const val_payload = try scope.arena().create(Value.Payload.RefVal); | ||
| 2234 | val_payload.* = .{ .val = variable.init }; | ||
| 2235 | return self.constInst(scope, src, .{ | ||
| 2236 | .ty = ty, | ||
| 2237 | .val = Value.initPayload(&val_payload.base), | ||
| 2238 | }); | ||
| 2239 | } | ||
| 2240 | |||
| 2241 | const b = try self.requireRuntimeBlock(scope, src); | ||
| 2242 | const inst = try b.arena.create(Inst.VarPtr); | ||
| 2243 | inst.* = .{ | ||
| 2244 | .base = .{ | ||
| 2245 | .tag = .varptr, | ||
| 2246 | .ty = ty, | ||
| 2247 | .src = src, | ||
| 2248 | }, | ||
| 2249 | .variable = variable, | ||
| 2250 | }; | ||
| 2251 | try b.instructions.append(self.gpa, &inst.base); | ||
| 2252 | return &inst.base; | ||
| 2253 | } | ||
| 2254 | |||
| 2255 | pub fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_src: usize) InnerError!*Inst { | ||
| 2256 | const elem_ty = switch (ptr.ty.zigTypeTag()) { | ||
| 2257 | .Pointer => ptr.ty.elemType(), | ||
| 2258 | else => return self.fail(scope, ptr_src, "expected pointer, found '{}'", .{ptr.ty}), | ||
| 2259 | }; | ||
| 2260 | if (ptr.value()) |val| { | ||
| 2261 | return self.constInst(scope, src, .{ | ||
| 2262 | .ty = elem_ty, | ||
| 2263 | .val = try val.pointerDeref(scope.arena()), | ||
| 2264 | }); | ||
| 2265 | } | ||
| 2266 | |||
| 2267 | const b = try self.requireRuntimeBlock(scope, src); | ||
| 2268 | return self.addUnOp(b, src, elem_ty, .load, ptr); | ||
| 2269 | } | ||
| 2270 | |||
| 2271 | pub fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name: []const u8) InnerError!*Inst { | ||
| 2272 | const decl = self.lookupDeclName(scope, decl_name) orelse | ||
| 2273 | return self.fail(scope, src, "decl '{}' not found", .{decl_name}); | ||
| 2274 | return self.analyzeDeclRef(scope, src, decl); | ||
| 2275 | } | ||
| 2276 | |||
| 2277 | pub fn wantSafety(self: *Module, scope: *Scope) bool { | ||
| 2278 | // TODO take into account scope's safety overrides | ||
| 2279 | return switch (self.optimizeMode()) { | ||
| 2280 | .Debug => true, | ||
| 2281 | .ReleaseSafe => true, | ||
| 2282 | .ReleaseFast => false, | ||
| 2283 | .ReleaseSmall => false, | ||
| 2284 | }; | ||
| 2285 | } | ||
| 2286 | |||
| 2287 | pub fn analyzeIsNull( | ||
| 2288 | self: *Module, | ||
| 2289 | scope: *Scope, | ||
| 2290 | src: usize, | ||
| 2291 | operand: *Inst, | ||
| 2292 | invert_logic: bool, | ||
| 2293 | ) InnerError!*Inst { | ||
| 2294 | if (operand.value()) |opt_val| { | ||
| 2295 | const is_null = opt_val.isNull(); | ||
| 2296 | const bool_value = if (invert_logic) !is_null else is_null; | ||
| 2297 | return self.constBool(scope, src, bool_value); | ||
| 2298 | } | ||
| 2299 | const b = try self.requireRuntimeBlock(scope, src); | ||
| 2300 | const inst_tag: Inst.Tag = if (invert_logic) .isnonnull else .isnull; | ||
| 2301 | return self.addUnOp(b, src, Type.initTag(.bool), inst_tag, operand); | ||
| 2302 | } | ||
| 2303 | |||
| 2304 | pub fn analyzeIsErr(self: *Module, scope: *Scope, src: usize, operand: *Inst) InnerError!*Inst { | ||
| 2305 | return self.fail(scope, src, "TODO implement analysis of iserr", .{}); | ||
| 2306 | } | ||
| 2307 | |||
| 2308 | pub fn analyzeSlice(self: *Module, scope: *Scope, src: usize, array_ptr: *Inst, start: *Inst, end_opt: ?*Inst, sentinel_opt: ?*Inst) InnerError!*Inst { | ||
| 2309 | const ptr_child = switch (array_ptr.ty.zigTypeTag()) { | ||
| 2310 | .Pointer => array_ptr.ty.elemType(), | ||
| 2311 | else => return self.fail(scope, src, "expected pointer, found '{}'", .{array_ptr.ty}), | ||
| 2312 | }; | ||
| 2313 | |||
| 2314 | var array_type = ptr_child; | ||
| 2315 | const elem_type = switch (ptr_child.zigTypeTag()) { | ||
| 2316 | .Array => ptr_child.elemType(), | ||
| 2317 | .Pointer => blk: { | ||
| 2318 | if (ptr_child.isSinglePointer()) { | ||
| 2319 | if (ptr_child.elemType().zigTypeTag() == .Array) { | ||
| 2320 | array_type = ptr_child.elemType(); | ||
| 2321 | break :blk ptr_child.elemType().elemType(); | ||
| 2322 | } | ||
| 2323 | |||
| 2324 | return self.fail(scope, src, "slice of single-item pointer", .{}); | ||
| 2325 | } | ||
| 2326 | break :blk ptr_child.elemType(); | ||
| 2327 | }, | ||
| 2328 | else => return self.fail(scope, src, "slice of non-array type '{}'", .{ptr_child}), | ||
| 2329 | }; | ||
| 2330 | |||
| 2331 | const slice_sentinel = if (sentinel_opt) |sentinel| blk: { | ||
| 2332 | const casted = try self.coerce(scope, elem_type, sentinel); | ||
| 2333 | break :blk try self.resolveConstValue(scope, casted); | ||
| 2334 | } else null; | ||
| 2335 | |||
| 2336 | var return_ptr_size: std.builtin.TypeInfo.Pointer.Size = .Slice; | ||
| 2337 | var return_elem_type = elem_type; | ||
| 2338 | if (end_opt) |end| { | ||
| 2339 | if (end.value()) |end_val| { | ||
| 2340 | if (start.value()) |start_val| { | ||
| 2341 | const start_u64 = start_val.toUnsignedInt(); | ||
| 2342 | const end_u64 = end_val.toUnsignedInt(); | ||
| 2343 | if (start_u64 > end_u64) { | ||
| 2344 | return self.fail(scope, src, "out of bounds slice", .{}); | ||
| 2345 | } | ||
| 2346 | |||
| 2347 | const len = end_u64 - start_u64; | ||
| 2348 | const array_sentinel = if (array_type.zigTypeTag() == .Array and end_u64 == array_type.arrayLen()) | ||
| 2349 | array_type.sentinel() | ||
| 2350 | else | ||
| 2351 | slice_sentinel; | ||
| 2352 | return_elem_type = try self.arrayType(scope, len, array_sentinel, elem_type); | ||
| 2353 | return_ptr_size = .One; | ||
| 2354 | } | ||
| 2355 | } | ||
| 2356 | } | ||
| 2357 | const return_type = try self.ptrType( | ||
| 2358 | scope, | ||
| 2359 | src, | ||
| 2360 | return_elem_type, | ||
| 2361 | if (end_opt == null) slice_sentinel else null, | ||
| 2362 | 0, // TODO alignment | ||
| 2363 | 0, | ||
| 2364 | 0, | ||
| 2365 | !ptr_child.isConstPtr(), | ||
| 2366 | ptr_child.isAllowzeroPtr(), | ||
| 2367 | ptr_child.isVolatilePtr(), | ||
| 2368 | return_ptr_size, | ||
| 2369 | ); | ||
| 2370 | |||
| 2371 | return self.fail(scope, src, "TODO implement analysis of slice", .{}); | ||
| 2372 | } | ||
| 2373 | |||
| 2374 | /// Asserts that lhs and rhs types are both numeric. | ||
| 2375 | pub fn cmpNumeric( | ||
| 2376 | self: *Module, | ||
| 2377 | scope: *Scope, | ||
| 2378 | src: usize, | ||
| 2379 | lhs: *Inst, | ||
| 2380 | rhs: *Inst, | ||
| 2381 | op: std.math.CompareOperator, | ||
| 2382 | ) !*Inst { | ||
| 2383 | assert(lhs.ty.isNumeric()); | ||
| 2384 | assert(rhs.ty.isNumeric()); | ||
| 2385 | |||
| 2386 | const lhs_ty_tag = lhs.ty.zigTypeTag(); | ||
| 2387 | const rhs_ty_tag = rhs.ty.zigTypeTag(); | ||
| 2388 | |||
| 2389 | if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) { | ||
| 2390 | if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) { | ||
| 2391 | return self.fail(scope, src, "vector length mismatch: {} and {}", .{ | ||
| 2392 | lhs.ty.arrayLen(), | ||
| 2393 | rhs.ty.arrayLen(), | ||
| 2394 | }); | ||
| 2395 | } | ||
| 2396 | return self.fail(scope, src, "TODO implement support for vectors in cmpNumeric", .{}); | ||
| 2397 | } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) { | ||
| 2398 | return self.fail(scope, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{ | ||
| 2399 | lhs.ty, | ||
| 2400 | rhs.ty, | ||
| 2401 | }); | ||
| 2402 | } | ||
| 2403 | |||
| 2404 | if (lhs.value()) |lhs_val| { | ||
| 2405 | if (rhs.value()) |rhs_val| { | ||
| 2406 | return self.constBool(scope, src, Value.compare(lhs_val, op, rhs_val)); | ||
| 2407 | } | ||
| 2408 | } | ||
| 2409 | |||
| 2410 | // TODO handle comparisons against lazy zero values | ||
| 2411 | // Some values can be compared against zero without being runtime known or without forcing | ||
| 2412 | // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to | ||
| 2413 | // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout | ||
| 2414 | // of this function if we don't need to. | ||
| 2415 | |||
| 2416 | // It must be a runtime comparison. | ||
| 2417 | const b = try self.requireRuntimeBlock(scope, src); | ||
| 2418 | // For floats, emit a float comparison instruction. | ||
| 2419 | const lhs_is_float = switch (lhs_ty_tag) { | ||
| 2420 | .Float, .ComptimeFloat => true, | ||
| 2421 | else => false, | ||
| 2422 | }; | ||
| 2423 | const rhs_is_float = switch (rhs_ty_tag) { | ||
| 2424 | .Float, .ComptimeFloat => true, | ||
| 2425 | else => false, | ||
| 2426 | }; | ||
| 2427 | if (lhs_is_float and rhs_is_float) { | ||
| 2428 | // Implicit cast the smaller one to the larger one. | ||
| 2429 | const dest_type = x: { | ||
| 2430 | if (lhs_ty_tag == .ComptimeFloat) { | ||
| 2431 | break :x rhs.ty; | ||
| 2432 | } else if (rhs_ty_tag == .ComptimeFloat) { | ||
| 2433 | break :x lhs.ty; | ||
| 2434 | } | ||
| 2435 | if (lhs.ty.floatBits(self.getTarget()) >= rhs.ty.floatBits(self.getTarget())) { | ||
| 2436 | break :x lhs.ty; | ||
| 2437 | } else { | ||
| 2438 | break :x rhs.ty; | ||
| 2439 | } | ||
| 2440 | }; | ||
| 2441 | const casted_lhs = try self.coerce(scope, dest_type, lhs); | ||
| 2442 | const casted_rhs = try self.coerce(scope, dest_type, rhs); | ||
| 2443 | return self.addBinOp(b, src, dest_type, Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs); | ||
| 2444 | } | ||
| 2445 | // For mixed unsigned integer sizes, implicit cast both operands to the larger integer. | ||
| 2446 | // For mixed signed and unsigned integers, implicit cast both operands to a signed | ||
| 2447 | // integer with + 1 bit. | ||
| 2448 | // For mixed floats and integers, extract the integer part from the float, cast that to | ||
| 2449 | // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float, | ||
| 2450 | // add/subtract 1. | ||
| 2451 | const lhs_is_signed = if (lhs.value()) |lhs_val| | ||
| 2452 | lhs_val.compareWithZero(.lt) | ||
| 2453 | else | ||
| 2454 | (lhs.ty.isFloat() or lhs.ty.isSignedInt()); | ||
| 2455 | const rhs_is_signed = if (rhs.value()) |rhs_val| | ||
| 2456 | rhs_val.compareWithZero(.lt) | ||
| 2457 | else | ||
| 2458 | (rhs.ty.isFloat() or rhs.ty.isSignedInt()); | ||
| 2459 | const dest_int_is_signed = lhs_is_signed or rhs_is_signed; | ||
| 2460 | |||
| 2461 | var dest_float_type: ?Type = null; | ||
| 2462 | |||
| 2463 | var lhs_bits: usize = undefined; | ||
| 2464 | if (lhs.value()) |lhs_val| { | ||
| 2465 | if (lhs_val.isUndef()) | ||
| 2466 | return self.constUndef(scope, src, Type.initTag(.bool)); | ||
| 2467 | const is_unsigned = if (lhs_is_float) x: { | ||
| 2468 | var bigint_space: Value.BigIntSpace = undefined; | ||
| 2469 | var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(self.gpa); | ||
| 2470 | defer bigint.deinit(); | ||
| 2471 | const zcmp = lhs_val.orderAgainstZero(); | ||
| 2472 | if (lhs_val.floatHasFraction()) { | ||
| 2473 | switch (op) { | ||
| 2474 | .eq => return self.constBool(scope, src, false), | ||
| 2475 | .neq => return self.constBool(scope, src, true), | ||
| 2476 | else => {}, | ||
| 2477 | } | ||
| 2478 | if (zcmp == .lt) { | ||
| 2479 | try bigint.addScalar(bigint.toConst(), -1); | ||
| 2480 | } else { | ||
| 2481 | try bigint.addScalar(bigint.toConst(), 1); | ||
| 2482 | } | ||
| 2483 | } | ||
| 2484 | lhs_bits = bigint.toConst().bitCountTwosComp(); | ||
| 2485 | break :x (zcmp != .lt); | ||
| 2486 | } else x: { | ||
| 2487 | lhs_bits = lhs_val.intBitCountTwosComp(); | ||
| 2488 | break :x (lhs_val.orderAgainstZero() != .lt); | ||
| 2489 | }; | ||
| 2490 | lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed); | ||
| 2491 | } else if (lhs_is_float) { | ||
| 2492 | dest_float_type = lhs.ty; | ||
| 2493 | } else { | ||
| 2494 | const int_info = lhs.ty.intInfo(self.getTarget()); | ||
| 2495 | lhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed); | ||
| 2496 | } | ||
| 2497 | |||
| 2498 | var rhs_bits: usize = undefined; | ||
| 2499 | if (rhs.value()) |rhs_val| { | ||
| 2500 | if (rhs_val.isUndef()) | ||
| 2501 | return self.constUndef(scope, src, Type.initTag(.bool)); | ||
| 2502 | const is_unsigned = if (rhs_is_float) x: { | ||
| 2503 | var bigint_space: Value.BigIntSpace = undefined; | ||
| 2504 | var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(self.gpa); | ||
| 2505 | defer bigint.deinit(); | ||
| 2506 | const zcmp = rhs_val.orderAgainstZero(); | ||
| 2507 | if (rhs_val.floatHasFraction()) { | ||
| 2508 | switch (op) { | ||
| 2509 | .eq => return self.constBool(scope, src, false), | ||
| 2510 | .neq => return self.constBool(scope, src, true), | ||
| 2511 | else => {}, | ||
| 2512 | } | ||
| 2513 | if (zcmp == .lt) { | ||
| 2514 | try bigint.addScalar(bigint.toConst(), -1); | ||
| 2515 | } else { | ||
| 2516 | try bigint.addScalar(bigint.toConst(), 1); | ||
| 2517 | } | ||
| 2518 | } | ||
| 2519 | rhs_bits = bigint.toConst().bitCountTwosComp(); | ||
| 2520 | break :x (zcmp != .lt); | ||
| 2521 | } else x: { | ||
| 2522 | rhs_bits = rhs_val.intBitCountTwosComp(); | ||
| 2523 | break :x (rhs_val.orderAgainstZero() != .lt); | ||
| 2524 | }; | ||
| 2525 | rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed); | ||
| 2526 | } else if (rhs_is_float) { | ||
| 2527 | dest_float_type = rhs.ty; | ||
| 2528 | } else { | ||
| 2529 | const int_info = rhs.ty.intInfo(self.getTarget()); | ||
| 2530 | rhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed); | ||
| 2531 | } | ||
| 2532 | |||
| 2533 | const dest_type = if (dest_float_type) |ft| ft else blk: { | ||
| 2534 | const max_bits = std.math.max(lhs_bits, rhs_bits); | ||
| 2535 | const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) { | ||
| 2536 | error.Overflow => return self.fail(scope, src, "{} exceeds maximum integer bit count", .{max_bits}), | ||
| 2537 | }; | ||
| 2538 | break :blk try self.makeIntType(scope, dest_int_is_signed, casted_bits); | ||
| 2539 | }; | ||
| 2540 | const casted_lhs = try self.coerce(scope, dest_type, lhs); | ||
| 2541 | const casted_rhs = try self.coerce(scope, dest_type, rhs); | ||
| 2542 | |||
| 2543 | return self.addBinOp(b, src, Type.initTag(.bool), Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs); | ||
| 2544 | } | ||
| 2545 | |||
| 2546 | fn wrapOptional(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { | ||
| 2547 | if (inst.value()) |val| { | ||
| 2548 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 2549 | } | ||
| 2550 | |||
| 2551 | const b = try self.requireRuntimeBlock(scope, inst.src); | ||
| 2552 | return self.addUnOp(b, inst.src, dest_type, .wrap_optional, inst); | ||
| 2553 | } | ||
| 2554 | |||
| 2555 | fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type { | ||
| 2556 | if (signed) { | ||
| 2557 | const int_payload = try scope.arena().create(Type.Payload.IntSigned); | ||
| 2558 | int_payload.* = .{ .bits = bits }; | ||
| 2559 | return Type.initPayload(&int_payload.base); | ||
| 2560 | } else { | ||
| 2561 | const int_payload = try scope.arena().create(Type.Payload.IntUnsigned); | ||
| 2562 | int_payload.* = .{ .bits = bits }; | ||
| 2563 | return Type.initPayload(&int_payload.base); | ||
| 2564 | } | ||
| 2565 | } | ||
| 2566 | |||
| 2567 | pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type { | ||
| 2568 | if (instructions.len == 0) | ||
| 2569 | return Type.initTag(.noreturn); | ||
| 2570 | |||
| 2571 | if (instructions.len == 1) | ||
| 2572 | return instructions[0].ty; | ||
| 2573 | |||
| 2574 | var prev_inst = instructions[0]; | ||
| 2575 | for (instructions[1..]) |next_inst| { | ||
| 2576 | if (next_inst.ty.eql(prev_inst.ty)) | ||
| 2577 | continue; | ||
| 2578 | if (next_inst.ty.zigTypeTag() == .NoReturn) | ||
| 2579 | continue; | ||
| 2580 | if (prev_inst.ty.zigTypeTag() == .NoReturn) { | ||
| 2581 | prev_inst = next_inst; | ||
| 2582 | continue; | ||
| 2583 | } | ||
| 2584 | if (next_inst.ty.zigTypeTag() == .Undefined) | ||
| 2585 | continue; | ||
| 2586 | if (prev_inst.ty.zigTypeTag() == .Undefined) { | ||
| 2587 | prev_inst = next_inst; | ||
| 2588 | continue; | ||
| 2589 | } | ||
| 2590 | if (prev_inst.ty.isInt() and | ||
| 2591 | next_inst.ty.isInt() and | ||
| 2592 | prev_inst.ty.isSignedInt() == next_inst.ty.isSignedInt()) | ||
| 2593 | { | ||
| 2594 | if (prev_inst.ty.intInfo(self.getTarget()).bits < next_inst.ty.intInfo(self.getTarget()).bits) { | ||
| 2595 | prev_inst = next_inst; | ||
| 2596 | } | ||
| 2597 | continue; | ||
| 2598 | } | ||
| 2599 | if (prev_inst.ty.isFloat() and next_inst.ty.isFloat()) { | ||
| 2600 | if (prev_inst.ty.floatBits(self.getTarget()) < next_inst.ty.floatBits(self.getTarget())) { | ||
| 2601 | prev_inst = next_inst; | ||
| 2602 | } | ||
| 2603 | continue; | ||
| 2604 | } | ||
| 2605 | |||
| 2606 | // TODO error notes pointing out each type | ||
| 2607 | return self.fail(scope, next_inst.src, "incompatible types: '{}' and '{}'", .{ prev_inst.ty, next_inst.ty }); | ||
| 2608 | } | ||
| 2609 | |||
| 2610 | return prev_inst.ty; | ||
| 2611 | } | ||
| 2612 | |||
| 2613 | pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { | ||
| 2614 | // If the types are the same, we can return the operand. | ||
| 2615 | if (dest_type.eql(inst.ty)) | ||
| 2616 | return inst; | ||
| 2617 | |||
| 2618 | const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty); | ||
| 2619 | if (in_memory_result == .ok) { | ||
| 2620 | return self.bitcast(scope, dest_type, inst); | ||
| 2621 | } | ||
| 2622 | |||
| 2623 | // undefined to anything | ||
| 2624 | if (inst.value()) |val| { | ||
| 2625 | if (val.isUndef() or inst.ty.zigTypeTag() == .Undefined) { | ||
| 2626 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 2627 | } | ||
| 2628 | } | ||
| 2629 | assert(inst.ty.zigTypeTag() != .Undefined); | ||
| 2630 | |||
| 2631 | // null to ?T | ||
| 2632 | if (dest_type.zigTypeTag() == .Optional and inst.ty.zigTypeTag() == .Null) { | ||
| 2633 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = Value.initTag(.null_value) }); | ||
| 2634 | } | ||
| 2635 | |||
| 2636 | // T to ?T | ||
| 2637 | if (dest_type.zigTypeTag() == .Optional) { | ||
| 2638 | var buf: Type.Payload.PointerSimple = undefined; | ||
| 2639 | const child_type = dest_type.optionalChild(&buf); | ||
| 2640 | if (child_type.eql(inst.ty)) { | ||
| 2641 | return self.wrapOptional(scope, dest_type, inst); | ||
| 2642 | } else if (try self.coerceNum(scope, child_type, inst)) |some| { | ||
| 2643 | return self.wrapOptional(scope, dest_type, some); | ||
| 2644 | } | ||
| 2645 | } | ||
| 2646 | |||
| 2647 | // *[N]T to []T | ||
| 2648 | if (inst.ty.isSinglePointer() and dest_type.isSlice() and | ||
| 2649 | (!inst.ty.isConstPtr() or dest_type.isConstPtr())) | ||
| 2650 | { | ||
| 2651 | const array_type = inst.ty.elemType(); | ||
| 2652 | const dst_elem_type = dest_type.elemType(); | ||
| 2653 | if (array_type.zigTypeTag() == .Array and | ||
| 2654 | coerceInMemoryAllowed(dst_elem_type, array_type.elemType()) == .ok) | ||
| 2655 | { | ||
| 2656 | return self.coerceArrayPtrToSlice(scope, dest_type, inst); | ||
| 2657 | } | ||
| 2658 | } | ||
| 2659 | |||
| 2660 | // comptime known number to other number | ||
| 2661 | if (try self.coerceNum(scope, dest_type, inst)) |some| | ||
| 2662 | return some; | ||
| 2663 | |||
| 2664 | // integer widening | ||
| 2665 | if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) { | ||
| 2666 | assert(inst.value() == null); // handled above | ||
| 2667 | |||
| 2668 | const src_info = inst.ty.intInfo(self.getTarget()); | ||
| 2669 | const dst_info = dest_type.intInfo(self.getTarget()); | ||
| 2670 | if ((src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) or | ||
| 2671 | // small enough unsigned ints can get casted to large enough signed ints | ||
| 2672 | (src_info.signed and !dst_info.signed and dst_info.bits > src_info.bits)) | ||
| 2673 | { | ||
| 2674 | const b = try self.requireRuntimeBlock(scope, inst.src); | ||
| 2675 | return self.addUnOp(b, inst.src, dest_type, .intcast, inst); | ||
| 2676 | } | ||
| 2677 | } | ||
| 2678 | |||
| 2679 | // float widening | ||
| 2680 | if (inst.ty.zigTypeTag() == .Float and dest_type.zigTypeTag() == .Float) { | ||
| 2681 | assert(inst.value() == null); // handled above | ||
| 2682 | |||
| 2683 | const src_bits = inst.ty.floatBits(self.getTarget()); | ||
| 2684 | const dst_bits = dest_type.floatBits(self.getTarget()); | ||
| 2685 | if (dst_bits >= src_bits) { | ||
| 2686 | const b = try self.requireRuntimeBlock(scope, inst.src); | ||
| 2687 | return self.addUnOp(b, inst.src, dest_type, .floatcast, inst); | ||
| 2688 | } | ||
| 2689 | } | ||
| 2690 | |||
| 2691 | return self.fail(scope, inst.src, "expected {}, found {}", .{ dest_type, inst.ty }); | ||
| 2692 | } | ||
| 2693 | |||
| 2694 | pub fn coerceNum(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !?*Inst { | ||
| 2695 | const val = inst.value() orelse return null; | ||
| 2696 | const src_zig_tag = inst.ty.zigTypeTag(); | ||
| 2697 | const dst_zig_tag = dest_type.zigTypeTag(); | ||
| 2698 | |||
| 2699 | if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) { | ||
| 2700 | if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { | ||
| 2701 | if (val.floatHasFraction()) { | ||
| 2702 | return self.fail(scope, inst.src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst.ty }); | ||
| 2703 | } | ||
| 2704 | return self.fail(scope, inst.src, "TODO float to int", .{}); | ||
| 2705 | } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { | ||
| 2706 | if (!val.intFitsInType(dest_type, self.getTarget())) { | ||
| 2707 | return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val }); | ||
| 2708 | } | ||
| 2709 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 2710 | } | ||
| 2711 | } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) { | ||
| 2712 | if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { | ||
| 2713 | const res = val.floatCast(scope.arena(), dest_type, self.getTarget()) catch |err| switch (err) { | ||
| 2714 | error.Overflow => return self.fail( | ||
| 2715 | scope, | ||
| 2716 | inst.src, | ||
| 2717 | "cast of value {} to type '{}' loses information", | ||
| 2718 | .{ val, dest_type }, | ||
| 2719 | ), | ||
| 2720 | error.OutOfMemory => return error.OutOfMemory, | ||
| 2721 | }; | ||
| 2722 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = res }); | ||
| 2723 | } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { | ||
| 2724 | return self.fail(scope, inst.src, "TODO int to float", .{}); | ||
| 2725 | } | ||
| 2726 | } | ||
| 2727 | return null; | ||
| 2728 | } | ||
| 2729 | |||
| 2730 | pub fn storePtr(self: *Module, scope: *Scope, src: usize, ptr: *Inst, uncasted_value: *Inst) !*Inst { | ||
| 2731 | if (ptr.ty.isConstPtr()) | ||
| 2732 | return self.fail(scope, src, "cannot assign to constant", .{}); | ||
| 2733 | |||
| 2734 | const elem_ty = ptr.ty.elemType(); | ||
| 2735 | const value = try self.coerce(scope, elem_ty, uncasted_value); | ||
| 2736 | if (elem_ty.onePossibleValue() != null) | ||
| 2737 | return self.constVoid(scope, src); | ||
| 2738 | |||
| 2739 | // TODO handle comptime pointer writes | ||
| 2740 | // TODO handle if the element type requires comptime | ||
| 2741 | |||
| 2742 | const b = try self.requireRuntimeBlock(scope, src); | ||
| 2743 | return self.addBinOp(b, src, Type.initTag(.void), .store, ptr, value); | ||
| 2744 | } | ||
| 2745 | |||
| 2746 | pub fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { | ||
| 2747 | if (inst.value()) |val| { | ||
| 2748 | // Keep the comptime Value representation; take the new type. | ||
| 2749 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 2750 | } | ||
| 2751 | // TODO validate the type size and other compile errors | ||
| 2752 | const b = try self.requireRuntimeBlock(scope, inst.src); | ||
| 2753 | return self.addUnOp(b, inst.src, dest_type, .bitcast, inst); | ||
| 2754 | } | ||
| 2755 | |||
| 2756 | fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { | ||
| 2757 | if (inst.value()) |val| { | ||
| 2758 | // The comptime Value representation is compatible with both types. | ||
| 2759 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 2760 | } | ||
| 2761 | return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{}); | ||
| 2762 | } | ||
| 2763 | |||
| 2764 | pub fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError { | ||
| 2765 | @setCold(true); | ||
| 2766 | const err_msg = try Compilation.ErrorMsg.create(self.gpa, src, format, args); | ||
| 2767 | return self.failWithOwnedErrorMsg(scope, src, err_msg); | ||
| 2768 | } | ||
| 2769 | |||
| 2770 | pub fn failTok( | ||
| 2771 | self: *Module, | ||
| 2772 | scope: *Scope, | ||
| 2773 | token_index: ast.TokenIndex, | ||
| 2774 | comptime format: []const u8, | ||
| 2775 | args: anytype, | ||
| 2776 | ) InnerError { | ||
| 2777 | @setCold(true); | ||
| 2778 | const src = scope.tree().token_locs[token_index].start; | ||
| 2779 | return self.fail(scope, src, format, args); | ||
| 2780 | } | ||
| 2781 | |||
| 2782 | pub fn failNode( | ||
| 2783 | self: *Module, | ||
| 2784 | scope: *Scope, | ||
| 2785 | ast_node: *ast.Node, | ||
| 2786 | comptime format: []const u8, | ||
| 2787 | args: anytype, | ||
| 2788 | ) InnerError { | ||
| 2789 | @setCold(true); | ||
| 2790 | const src = scope.tree().token_locs[ast_node.firstToken()].start; | ||
| 2791 | return self.fail(scope, src, format, args); | ||
| 2792 | } | ||
| 2793 | |||
| 2794 | fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Compilation.ErrorMsg) InnerError { | ||
| 2795 | { | ||
| 2796 | errdefer err_msg.destroy(self.gpa); | ||
| 2797 | try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1); | ||
| 2798 | try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1); | ||
| 2799 | } | ||
| 2800 | switch (scope.tag) { | ||
| 2801 | .decl => { | ||
| 2802 | const decl = scope.cast(Scope.DeclAnalysis).?.decl; | ||
| 2803 | decl.analysis = .sema_failure; | ||
| 2804 | decl.generation = self.generation; | ||
| 2805 | self.failed_decls.putAssumeCapacityNoClobber(decl, err_msg); | ||
| 2806 | }, | ||
| 2807 | .block => { | ||
| 2808 | const block = scope.cast(Scope.Block).?; | ||
| 2809 | if (block.func) |func| { | ||
| 2810 | func.analysis = .sema_failure; | ||
| 2811 | } else { | ||
| 2812 | block.decl.analysis = .sema_failure; | ||
| 2813 | block.decl.generation = self.generation; | ||
| 2814 | } | ||
| 2815 | self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg); | ||
| 2816 | }, | ||
| 2817 | .gen_zir => { | ||
| 2818 | const gen_zir = scope.cast(Scope.GenZIR).?; | ||
| 2819 | gen_zir.decl.analysis = .sema_failure; | ||
| 2820 | gen_zir.decl.generation = self.generation; | ||
| 2821 | self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg); | ||
| 2822 | }, | ||
| 2823 | .local_val => { | ||
| 2824 | const gen_zir = scope.cast(Scope.LocalVal).?.gen_zir; | ||
| 2825 | gen_zir.decl.analysis = .sema_failure; | ||
| 2826 | gen_zir.decl.generation = self.generation; | ||
| 2827 | self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg); | ||
| 2828 | }, | ||
| 2829 | .local_ptr => { | ||
| 2830 | const gen_zir = scope.cast(Scope.LocalPtr).?.gen_zir; | ||
| 2831 | gen_zir.decl.analysis = .sema_failure; | ||
| 2832 | gen_zir.decl.generation = self.generation; | ||
| 2833 | self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg); | ||
| 2834 | }, | ||
| 2835 | .zir_module => { | ||
| 2836 | const zir_module = scope.cast(Scope.ZIRModule).?; | ||
| 2837 | zir_module.status = .loaded_sema_failure; | ||
| 2838 | self.failed_files.putAssumeCapacityNoClobber(scope, err_msg); | ||
| 2839 | }, | ||
| 2840 | .file => unreachable, | ||
| 2841 | .container => unreachable, | ||
| 2842 | } | ||
| 2843 | return error.AnalysisFail; | ||
| 2844 | } | ||
| 2845 | |||
| 2846 | const InMemoryCoercionResult = enum { | ||
| 2847 | ok, | ||
| 2848 | no_match, | ||
| 2849 | }; | ||
| 2850 | |||
| 2851 | fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult { | ||
| 2852 | if (dest_type.eql(src_type)) | ||
| 2853 | return .ok; | ||
| 2854 | |||
| 2855 | // TODO: implement more of this function | ||
| 2856 | |||
| 2857 | return .no_match; | ||
| 2858 | } | ||
| 2859 | |||
| 2860 | fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool { | ||
| 2861 | return @bitCast(u128, a) == @bitCast(u128, b); | ||
| 2862 | } | ||
| 2863 | |||
| 2864 | pub fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value { | ||
| 2865 | // TODO is this a performance issue? maybe we should try the operation without | ||
| 2866 | // resorting to BigInt first. | ||
| 2867 | var lhs_space: Value.BigIntSpace = undefined; | ||
| 2868 | var rhs_space: Value.BigIntSpace = undefined; | ||
| 2869 | const lhs_bigint = lhs.toBigInt(&lhs_space); | ||
| 2870 | const rhs_bigint = rhs.toBigInt(&rhs_space); | ||
| 2871 | const limbs = try allocator.alloc( | ||
| 2872 | std.math.big.Limb, | ||
| 2873 | std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, | ||
| 2874 | ); | ||
| 2875 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | ||
| 2876 | result_bigint.add(lhs_bigint, rhs_bigint); | ||
| 2877 | const result_limbs = result_bigint.limbs[0..result_bigint.len]; | ||
| 2878 | |||
| 2879 | const val_payload = if (result_bigint.positive) blk: { | ||
| 2880 | const val_payload = try allocator.create(Value.Payload.IntBigPositive); | ||
| 2881 | val_payload.* = .{ .limbs = result_limbs }; | ||
| 2882 | break :blk &val_payload.base; | ||
| 2883 | } else blk: { | ||
| 2884 | const val_payload = try allocator.create(Value.Payload.IntBigNegative); | ||
| 2885 | val_payload.* = .{ .limbs = result_limbs }; | ||
| 2886 | break :blk &val_payload.base; | ||
| 2887 | }; | ||
| 2888 | |||
| 2889 | return Value.initPayload(val_payload); | ||
| 2890 | } | ||
| 2891 | |||
| 2892 | pub fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value { | ||
| 2893 | // TODO is this a performance issue? maybe we should try the operation without | ||
| 2894 | // resorting to BigInt first. | ||
| 2895 | var lhs_space: Value.BigIntSpace = undefined; | ||
| 2896 | var rhs_space: Value.BigIntSpace = undefined; | ||
| 2897 | const lhs_bigint = lhs.toBigInt(&lhs_space); | ||
| 2898 | const rhs_bigint = rhs.toBigInt(&rhs_space); | ||
| 2899 | const limbs = try allocator.alloc( | ||
| 2900 | std.math.big.Limb, | ||
| 2901 | std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, | ||
| 2902 | ); | ||
| 2903 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | ||
| 2904 | result_bigint.sub(lhs_bigint, rhs_bigint); | ||
| 2905 | const result_limbs = result_bigint.limbs[0..result_bigint.len]; | ||
| 2906 | |||
| 2907 | const val_payload = if (result_bigint.positive) blk: { | ||
| 2908 | const val_payload = try allocator.create(Value.Payload.IntBigPositive); | ||
| 2909 | val_payload.* = .{ .limbs = result_limbs }; | ||
| 2910 | break :blk &val_payload.base; | ||
| 2911 | } else blk: { | ||
| 2912 | const val_payload = try allocator.create(Value.Payload.IntBigNegative); | ||
| 2913 | val_payload.* = .{ .limbs = result_limbs }; | ||
| 2914 | break :blk &val_payload.base; | ||
| 2915 | }; | ||
| 2916 | |||
| 2917 | return Value.initPayload(val_payload); | ||
| 2918 | } | ||
| 2919 | |||
| 2920 | pub fn floatAdd(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: Value, rhs: Value) !Value { | ||
| 2921 | var bit_count = switch (float_type.tag()) { | ||
| 2922 | .comptime_float => 128, | ||
| 2923 | else => float_type.floatBits(self.getTarget()), | ||
| 2924 | }; | ||
| 2925 | |||
| 2926 | const allocator = scope.arena(); | ||
| 2927 | const val_payload = switch (bit_count) { | ||
| 2928 | 16 => { | ||
| 2929 | return self.fail(scope, src, "TODO Implement addition for soft floats", .{}); | ||
| 2930 | }, | ||
| 2931 | 32 => blk: { | ||
| 2932 | const lhs_val = lhs.toFloat(f32); | ||
| 2933 | const rhs_val = rhs.toFloat(f32); | ||
| 2934 | const val_payload = try allocator.create(Value.Payload.Float_32); | ||
| 2935 | val_payload.* = .{ .val = lhs_val + rhs_val }; | ||
| 2936 | break :blk &val_payload.base; | ||
| 2937 | }, | ||
| 2938 | 64 => blk: { | ||
| 2939 | const lhs_val = lhs.toFloat(f64); | ||
| 2940 | const rhs_val = rhs.toFloat(f64); | ||
| 2941 | const val_payload = try allocator.create(Value.Payload.Float_64); | ||
| 2942 | val_payload.* = .{ .val = lhs_val + rhs_val }; | ||
| 2943 | break :blk &val_payload.base; | ||
| 2944 | }, | ||
| 2945 | 128 => { | ||
| 2946 | return self.fail(scope, src, "TODO Implement addition for big floats", .{}); | ||
| 2947 | }, | ||
| 2948 | else => unreachable, | ||
| 2949 | }; | ||
| 2950 | |||
| 2951 | return Value.initPayload(val_payload); | ||
| 2952 | } | ||
| 2953 | |||
| 2954 | pub fn floatSub(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: Value, rhs: Value) !Value { | ||
| 2955 | var bit_count = switch (float_type.tag()) { | ||
| 2956 | .comptime_float => 128, | ||
| 2957 | else => float_type.floatBits(self.getTarget()), | ||
| 2958 | }; | ||
| 2959 | |||
| 2960 | const allocator = scope.arena(); | ||
| 2961 | const val_payload = switch (bit_count) { | ||
| 2962 | 16 => { | ||
| 2963 | return self.fail(scope, src, "TODO Implement substraction for soft floats", .{}); | ||
| 2964 | }, | ||
| 2965 | 32 => blk: { | ||
| 2966 | const lhs_val = lhs.toFloat(f32); | ||
| 2967 | const rhs_val = rhs.toFloat(f32); | ||
| 2968 | const val_payload = try allocator.create(Value.Payload.Float_32); | ||
| 2969 | val_payload.* = .{ .val = lhs_val - rhs_val }; | ||
| 2970 | break :blk &val_payload.base; | ||
| 2971 | }, | ||
| 2972 | 64 => blk: { | ||
| 2973 | const lhs_val = lhs.toFloat(f64); | ||
| 2974 | const rhs_val = rhs.toFloat(f64); | ||
| 2975 | const val_payload = try allocator.create(Value.Payload.Float_64); | ||
| 2976 | val_payload.* = .{ .val = lhs_val - rhs_val }; | ||
| 2977 | break :blk &val_payload.base; | ||
| 2978 | }, | ||
| 2979 | 128 => { | ||
| 2980 | return self.fail(scope, src, "TODO Implement substraction for big floats", .{}); | ||
| 2981 | }, | ||
| 2982 | else => unreachable, | ||
| 2983 | }; | ||
| 2984 | |||
| 2985 | return Value.initPayload(val_payload); | ||
| 2986 | } | ||
| 2987 | |||
| 2988 | pub fn simplePtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type, mutable: bool, size: std.builtin.TypeInfo.Pointer.Size) Allocator.Error!Type { | ||
| 2989 | if (!mutable and size == .Slice and elem_ty.eql(Type.initTag(.u8))) { | ||
| 2990 | return Type.initTag(.const_slice_u8); | ||
| 2991 | } | ||
| 2992 | // TODO stage1 type inference bug | ||
| 2993 | const T = Type.Tag; | ||
| 2994 | |||
| 2995 | const type_payload = try scope.arena().create(Type.Payload.PointerSimple); | ||
| 2996 | type_payload.* = .{ | ||
| 2997 | .base = .{ | ||
| 2998 | .tag = switch (size) { | ||
| 2999 | .One => if (mutable) T.single_mut_pointer else T.single_const_pointer, | ||
| 3000 | .Many => if (mutable) T.many_mut_pointer else T.many_const_pointer, | ||
| 3001 | .C => if (mutable) T.c_mut_pointer else T.c_const_pointer, | ||
| 3002 | .Slice => if (mutable) T.mut_slice else T.const_slice, | ||
| 3003 | }, | ||
| 3004 | }, | ||
| 3005 | .pointee_type = elem_ty, | ||
| 3006 | }; | ||
| 3007 | return Type.initPayload(&type_payload.base); | ||
| 3008 | } | ||
| 3009 | |||
| 3010 | pub fn ptrType( | ||
| 3011 | self: *Module, | ||
| 3012 | scope: *Scope, | ||
| 3013 | src: usize, | ||
| 3014 | elem_ty: Type, | ||
| 3015 | sentinel: ?Value, | ||
| 3016 | @"align": u32, | ||
| 3017 | bit_offset: u16, | ||
| 3018 | host_size: u16, | ||
| 3019 | mutable: bool, | ||
| 3020 | @"allowzero": bool, | ||
| 3021 | @"volatile": bool, | ||
| 3022 | size: std.builtin.TypeInfo.Pointer.Size, | ||
| 3023 | ) Allocator.Error!Type { | ||
| 3024 | assert(host_size == 0 or bit_offset < host_size * 8); | ||
| 3025 | |||
| 3026 | // TODO check if type can be represented by simplePtrType | ||
| 3027 | const type_payload = try scope.arena().create(Type.Payload.Pointer); | ||
| 3028 | type_payload.* = .{ | ||
| 3029 | .pointee_type = elem_ty, | ||
| 3030 | .sentinel = sentinel, | ||
| 3031 | .@"align" = @"align", | ||
| 3032 | .bit_offset = bit_offset, | ||
| 3033 | .host_size = host_size, | ||
| 3034 | .@"allowzero" = @"allowzero", | ||
| 3035 | .mutable = mutable, | ||
| 3036 | .@"volatile" = @"volatile", | ||
| 3037 | .size = size, | ||
| 3038 | }; | ||
| 3039 | return Type.initPayload(&type_payload.base); | ||
| 3040 | } | ||
| 3041 | |||
| 3042 | pub fn optionalType(self: *Module, scope: *Scope, child_type: Type) Allocator.Error!Type { | ||
| 3043 | return Type.initPayload(switch (child_type.tag()) { | ||
| 3044 | .single_const_pointer => blk: { | ||
| 3045 | const payload = try scope.arena().create(Type.Payload.PointerSimple); | ||
| 3046 | payload.* = .{ | ||
| 3047 | .base = .{ .tag = .optional_single_const_pointer }, | ||
| 3048 | .pointee_type = child_type.elemType(), | ||
| 3049 | }; | ||
| 3050 | break :blk &payload.base; | ||
| 3051 | }, | ||
| 3052 | .single_mut_pointer => blk: { | ||
| 3053 | const payload = try scope.arena().create(Type.Payload.PointerSimple); | ||
| 3054 | payload.* = .{ | ||
| 3055 | .base = .{ .tag = .optional_single_mut_pointer }, | ||
| 3056 | .pointee_type = child_type.elemType(), | ||
| 3057 | }; | ||
| 3058 | break :blk &payload.base; | ||
| 3059 | }, | ||
| 3060 | else => blk: { | ||
| 3061 | const payload = try scope.arena().create(Type.Payload.Optional); | ||
| 3062 | payload.* = .{ | ||
| 3063 | .child_type = child_type, | ||
| 3064 | }; | ||
| 3065 | break :blk &payload.base; | ||
| 3066 | }, | ||
| 3067 | }); | ||
| 3068 | } | ||
| 3069 | |||
| 3070 | pub fn arrayType(self: *Module, scope: *Scope, len: u64, sentinel: ?Value, elem_type: Type) Allocator.Error!Type { | ||
| 3071 | if (elem_type.eql(Type.initTag(.u8))) { | ||
| 3072 | if (sentinel) |some| { | ||
| 3073 | if (some.eql(Value.initTag(.zero))) { | ||
| 3074 | const payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0); | ||
| 3075 | payload.* = .{ | ||
| 3076 | .len = len, | ||
| 3077 | }; | ||
| 3078 | return Type.initPayload(&payload.base); | ||
| 3079 | } | ||
| 3080 | } else { | ||
| 3081 | const payload = try scope.arena().create(Type.Payload.Array_u8); | ||
| 3082 | payload.* = .{ | ||
| 3083 | .len = len, | ||
| 3084 | }; | ||
| 3085 | return Type.initPayload(&payload.base); | ||
| 3086 | } | ||
| 3087 | } | ||
| 3088 | |||
| 3089 | if (sentinel) |some| { | ||
| 3090 | const payload = try scope.arena().create(Type.Payload.ArraySentinel); | ||
| 3091 | payload.* = .{ | ||
| 3092 | .len = len, | ||
| 3093 | .sentinel = some, | ||
| 3094 | .elem_type = elem_type, | ||
| 3095 | }; | ||
| 3096 | return Type.initPayload(&payload.base); | ||
| 3097 | } | ||
| 3098 | |||
| 3099 | const payload = try scope.arena().create(Type.Payload.Array); | ||
| 3100 | payload.* = .{ | ||
| 3101 | .len = len, | ||
| 3102 | .elem_type = elem_type, | ||
| 3103 | }; | ||
| 3104 | return Type.initPayload(&payload.base); | ||
| 3105 | } | ||
| 3106 | |||
| 3107 | pub fn errorUnionType(self: *Module, scope: *Scope, error_set: Type, payload: Type) Allocator.Error!Type { | ||
| 3108 | assert(error_set.zigTypeTag() == .ErrorSet); | ||
| 3109 | if (error_set.eql(Type.initTag(.anyerror)) and payload.eql(Type.initTag(.void))) { | ||
| 3110 | return Type.initTag(.anyerror_void_error_union); | ||
| 3111 | } | ||
| 3112 | |||
| 3113 | const result = try scope.arena().create(Type.Payload.ErrorUnion); | ||
| 3114 | result.* = .{ | ||
| 3115 | .error_set = error_set, | ||
| 3116 | .payload = payload, | ||
| 3117 | }; | ||
| 3118 | return Type.initPayload(&result.base); | ||
| 3119 | } | ||
| 3120 | |||
| 3121 | pub fn anyframeType(self: *Module, scope: *Scope, return_type: Type) Allocator.Error!Type { | ||
| 3122 | const result = try scope.arena().create(Type.Payload.AnyFrame); | ||
| 3123 | result.* = .{ | ||
| 3124 | .return_type = return_type, | ||
| 3125 | }; | ||
| 3126 | return Type.initPayload(&result.base); | ||
| 3127 | } | ||
| 3128 | |||
| 3129 | pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void { | ||
| 3130 | const zir_module = scope.namespace(); | ||
| 3131 | const source = zir_module.getSource(self) catch @panic("dumpInst failed to get source"); | ||
| 3132 | const loc = std.zig.findLineColumn(source, inst.src); | ||
| 3133 | if (inst.tag == .constant) { | ||
| 3134 | std.debug.print("constant ty={} val={} src={}:{}:{}\n", .{ | ||
| 3135 | inst.ty, | ||
| 3136 | inst.castTag(.constant).?.val, | ||
| 3137 | zir_module.subFilePath(), | ||
| 3138 | loc.line + 1, | ||
| 3139 | loc.column + 1, | ||
| 3140 | }); | ||
| 3141 | } else if (inst.deaths == 0) { | ||
| 3142 | std.debug.print("{} ty={} src={}:{}:{}\n", .{ | ||
| 3143 | @tagName(inst.tag), | ||
| 3144 | inst.ty, | ||
| 3145 | zir_module.subFilePath(), | ||
| 3146 | loc.line + 1, | ||
| 3147 | loc.column + 1, | ||
| 3148 | }); | ||
| 3149 | } else { | ||
| 3150 | std.debug.print("{} ty={} deaths={b} src={}:{}:{}\n", .{ | ||
| 3151 | @tagName(inst.tag), | ||
| 3152 | inst.ty, | ||
| 3153 | inst.deaths, | ||
| 3154 | zir_module.subFilePath(), | ||
| 3155 | loc.line + 1, | ||
| 3156 | loc.column + 1, | ||
| 3157 | }); | ||
| 3158 | } | ||
| 3159 | } | ||
| 3160 | |||
| 3161 | pub const PanicId = enum { | ||
| 3162 | unreach, | ||
| 3163 | unwrap_null, | ||
| 3164 | }; | ||
| 3165 | |||
| 3166 | pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic_id: PanicId) !void { | ||
| 3167 | const block_inst = try parent_block.arena.create(Inst.Block); | ||
| 3168 | block_inst.* = .{ | ||
| 3169 | .base = .{ | ||
| 3170 | .tag = Inst.Block.base_tag, | ||
| 3171 | .ty = Type.initTag(.void), | ||
| 3172 | .src = ok.src, | ||
| 3173 | }, | ||
| 3174 | .body = .{ | ||
| 3175 | .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the condbr. | ||
| 3176 | }, | ||
| 3177 | }; | ||
| 3178 | |||
| 3179 | const ok_body: ir.Body = .{ | ||
| 3180 | .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the brvoid. | ||
| 3181 | }; | ||
| 3182 | const brvoid = try parent_block.arena.create(Inst.BrVoid); | ||
| 3183 | brvoid.* = .{ | ||
| 3184 | .base = .{ | ||
| 3185 | .tag = .brvoid, | ||
| 3186 | .ty = Type.initTag(.noreturn), | ||
| 3187 | .src = ok.src, | ||
| 3188 | }, | ||
| 3189 | .block = block_inst, | ||
| 3190 | }; | ||
| 3191 | ok_body.instructions[0] = &brvoid.base; | ||
| 3192 | |||
| 3193 | var fail_block: Scope.Block = .{ | ||
| 3194 | .parent = parent_block, | ||
| 3195 | .func = parent_block.func, | ||
| 3196 | .decl = parent_block.decl, | ||
| 3197 | .instructions = .{}, | ||
| 3198 | .arena = parent_block.arena, | ||
| 3199 | .is_comptime = parent_block.is_comptime, | ||
| 3200 | }; | ||
| 3201 | defer fail_block.instructions.deinit(mod.gpa); | ||
| 3202 | |||
| 3203 | _ = try mod.safetyPanic(&fail_block, ok.src, panic_id); | ||
| 3204 | |||
| 3205 | const fail_body: ir.Body = .{ .instructions = try parent_block.arena.dupe(*Inst, fail_block.instructions.items) }; | ||
| 3206 | |||
| 3207 | const condbr = try parent_block.arena.create(Inst.CondBr); | ||
| 3208 | condbr.* = .{ | ||
| 3209 | .base = .{ | ||
| 3210 | .tag = .condbr, | ||
| 3211 | .ty = Type.initTag(.noreturn), | ||
| 3212 | .src = ok.src, | ||
| 3213 | }, | ||
| 3214 | .condition = ok, | ||
| 3215 | .then_body = ok_body, | ||
| 3216 | .else_body = fail_body, | ||
| 3217 | }; | ||
| 3218 | block_inst.body.instructions[0] = &condbr.base; | ||
| 3219 | |||
| 3220 | try parent_block.instructions.append(mod.gpa, &block_inst.base); | ||
| 3221 | } | ||
| 3222 | |||
| 3223 | pub fn safetyPanic(mod: *Module, block: *Scope.Block, src: usize, panic_id: PanicId) !*Inst { | ||
| 3224 | // TODO Once we have a panic function to call, call it here instead of breakpoint. | ||
| 3225 | _ = try mod.addNoOp(block, src, Type.initTag(.void), .breakpoint); | ||
| 3226 | return mod.addNoOp(block, src, Type.initTag(.noreturn), .unreach); | ||
| 3227 | } | ||
| 3228 | |||
| 3229 | pub fn getTarget(self: Module) Target { | ||
| 3230 | return self.comp.bin_file.options.target; | ||
| 3231 | } | ||
| 3232 | |||
| 3233 | pub fn optimizeMode(self: Module) std.builtin.Mode { | ||
| 3234 | return self.comp.bin_file.options.optimize_mode; | ||
| 3235 | } | ||
src-self-hosted/ZigModule.zig deleted-3236| ... | @@ -1,3236 +0,0 @@ | ||
| 1 | //! TODO This is going to get renamed from ZigModule to Module | ||
| 2 | const Module = @This(); | ||
| 3 | const std = @import("std"); | ||
| 4 | const Compilation = @import("Compilation.zig"); | ||
| 5 | const mem = std.mem; | ||
| 6 | const Allocator = std.mem.Allocator; | ||
| 7 | const ArrayListUnmanaged = std.ArrayListUnmanaged; | ||
| 8 | const Value = @import("value.zig").Value; | ||
| 9 | const Type = @import("type.zig").Type; | ||
| 10 | const TypedValue = @import("TypedValue.zig"); | ||
| 11 | const assert = std.debug.assert; | ||
| 12 | const log = std.log.scoped(.module); | ||
| 13 | const BigIntConst = std.math.big.int.Const; | ||
| 14 | const BigIntMutable = std.math.big.int.Mutable; | ||
| 15 | const Target = std.Target; | ||
| 16 | const Package = @import("Package.zig"); | ||
| 17 | const link = @import("link.zig"); | ||
| 18 | const ir = @import("ir.zig"); | ||
| 19 | const zir = @import("zir.zig"); | ||
| 20 | const Inst = ir.Inst; | ||
| 21 | const Body = ir.Body; | ||
| 22 | const ast = std.zig.ast; | ||
| 23 | const trace = @import("tracy.zig").trace; | ||
| 24 | const astgen = @import("astgen.zig"); | ||
| 25 | const zir_sema = @import("zir_sema.zig"); | ||
| 26 | |||
| 27 | /// General-purpose allocator. Used for both temporary and long-term storage. | ||
| 28 | gpa: *Allocator, | ||
| 29 | comp: *Compilation, | ||
| 30 | |||
| 31 | /// Where our incremental compilation metadata serialization will go. | ||
| 32 | zig_cache_artifact_directory: Compilation.Directory, | ||
| 33 | /// Pointer to externally managed resource. `null` if there is no zig file being compiled. | ||
| 34 | root_pkg: *Package, | ||
| 35 | /// Module owns this resource. | ||
| 36 | /// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`. | ||
| 37 | root_scope: *Scope, | ||
| 38 | /// It's rare for a decl to be exported, so we save memory by having a sparse map of | ||
| 39 | /// Decl pointers to details about them being exported. | ||
| 40 | /// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table. | ||
| 41 | decl_exports: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{}, | ||
| 42 | /// We track which export is associated with the given symbol name for quick | ||
| 43 | /// detection of symbol collisions. | ||
| 44 | symbol_exports: std.StringArrayHashMapUnmanaged(*Export) = .{}, | ||
| 45 | /// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl | ||
| 46 | /// is modified. Note that the key of this table is not the Decl being exported, but the Decl that | ||
| 47 | /// is performing the export of another Decl. | ||
| 48 | /// This table owns the Export memory. | ||
| 49 | export_owners: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{}, | ||
| 50 | /// Maps fully qualified namespaced names to the Decl struct for them. | ||
| 51 | decl_table: std.ArrayHashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{}, | ||
| 52 | /// We optimize memory usage for a compilation with no compile errors by storing the | ||
| 53 | /// error messages and mapping outside of `Decl`. | ||
| 54 | /// The ErrorMsg memory is owned by the decl, using Module's general purpose allocator. | ||
| 55 | /// Note that a Decl can succeed but the Fn it represents can fail. In this case, | ||
| 56 | /// a Decl can have a failed_decls entry but have analysis status of success. | ||
| 57 | failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *Compilation.ErrorMsg) = .{}, | ||
| 58 | /// Using a map here for consistency with the other fields here. | ||
| 59 | /// The ErrorMsg memory is owned by the `Scope`, using Module's general purpose allocator. | ||
| 60 | failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *Compilation.ErrorMsg) = .{}, | ||
| 61 | /// Using a map here for consistency with the other fields here. | ||
| 62 | /// The ErrorMsg memory is owned by the `Export`, using Module's general purpose allocator. | ||
| 63 | failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *Compilation.ErrorMsg) = .{}, | ||
| 64 | |||
| 65 | next_anon_name_index: usize = 0, | ||
| 66 | |||
| 67 | /// Candidates for deletion. After a semantic analysis update completes, this list | ||
| 68 | /// contains Decls that need to be deleted if they end up having no references to them. | ||
| 69 | deletion_set: ArrayListUnmanaged(*Decl) = .{}, | ||
| 70 | |||
| 71 | /// Error tags and their values, tag names are duped with mod.gpa. | ||
| 72 | global_error_set: std.StringHashMapUnmanaged(u16) = .{}, | ||
| 73 | |||
| 74 | /// Incrementing integer used to compare against the corresponding Decl | ||
| 75 | /// field to determine whether a Decl's status applies to an ongoing update, or a | ||
| 76 | /// previous analysis. | ||
| 77 | generation: u32 = 0, | ||
| 78 | |||
| 79 | pub const Export = struct { | ||
| 80 | options: std.builtin.ExportOptions, | ||
| 81 | /// Byte offset into the file that contains the export directive. | ||
| 82 | src: usize, | ||
| 83 | /// Represents the position of the export, if any, in the output file. | ||
| 84 | link: link.File.Elf.Export, | ||
| 85 | /// The Decl that performs the export. Note that this is *not* the Decl being exported. | ||
| 86 | owner_decl: *Decl, | ||
| 87 | /// The Decl being exported. Note this is *not* the Decl performing the export. | ||
| 88 | exported_decl: *Decl, | ||
| 89 | status: enum { | ||
| 90 | in_progress, | ||
| 91 | failed, | ||
| 92 | /// Indicates that the failure was due to a temporary issue, such as an I/O error | ||
| 93 | /// when writing to the output file. Retrying the export may succeed. | ||
| 94 | failed_retryable, | ||
| 95 | complete, | ||
| 96 | }, | ||
| 97 | }; | ||
| 98 | |||
| 99 | pub const Decl = struct { | ||
| 100 | /// This name is relative to the containing namespace of the decl. It uses a null-termination | ||
| 101 | /// to save bytes, since there can be a lot of decls in a compilation. The null byte is not allowed | ||
| 102 | /// in symbol names, because executable file formats use null-terminated strings for symbol names. | ||
| 103 | /// All Decls have names, even values that are not bound to a zig namespace. This is necessary for | ||
| 104 | /// mapping them to an address in the output file. | ||
| 105 | /// Memory owned by this decl, using Module's allocator. | ||
| 106 | name: [*:0]const u8, | ||
| 107 | /// The direct parent container of the Decl. This is either a `Scope.Container` or `Scope.ZIRModule`. | ||
| 108 | /// Reference to externally owned memory. | ||
| 109 | scope: *Scope, | ||
| 110 | /// The AST Node decl index or ZIR Inst index that contains this declaration. | ||
| 111 | /// Must be recomputed when the corresponding source file is modified. | ||
| 112 | src_index: usize, | ||
| 113 | /// The most recent value of the Decl after a successful semantic analysis. | ||
| 114 | typed_value: union(enum) { | ||
| 115 | never_succeeded: void, | ||
| 116 | most_recent: TypedValue.Managed, | ||
| 117 | }, | ||
| 118 | /// Represents the "shallow" analysis status. For example, for decls that are functions, | ||
| 119 | /// the function type is analyzed with this set to `in_progress`, however, the semantic | ||
| 120 | /// analysis of the function body is performed with this value set to `success`. Functions | ||
| 121 | /// have their own analysis status field. | ||
| 122 | analysis: enum { | ||
| 123 | /// This Decl corresponds to an AST Node that has not been referenced yet, and therefore | ||
| 124 | /// because of Zig's lazy declaration analysis, it will remain unanalyzed until referenced. | ||
| 125 | unreferenced, | ||
| 126 | /// Semantic analysis for this Decl is running right now. This state detects dependency loops. | ||
| 127 | in_progress, | ||
| 128 | /// This Decl might be OK but it depends on another one which did not successfully complete | ||
| 129 | /// semantic analysis. | ||
| 130 | dependency_failure, | ||
| 131 | /// Semantic analysis failure. | ||
| 132 | /// There will be a corresponding ErrorMsg in Module.failed_decls. | ||
| 133 | sema_failure, | ||
| 134 | /// There will be a corresponding ErrorMsg in Module.failed_decls. | ||
| 135 | /// This indicates the failure was something like running out of disk space, | ||
| 136 | /// and attempting semantic analysis again may succeed. | ||
| 137 | sema_failure_retryable, | ||
| 138 | /// There will be a corresponding ErrorMsg in Module.failed_decls. | ||
| 139 | codegen_failure, | ||
| 140 | /// There will be a corresponding ErrorMsg in Module.failed_decls. | ||
| 141 | /// This indicates the failure was something like running out of disk space, | ||
| 142 | /// and attempting codegen again may succeed. | ||
| 143 | codegen_failure_retryable, | ||
| 144 | /// Everything is done. During an update, this Decl may be out of date, depending | ||
| 145 | /// on its dependencies. The `generation` field can be used to determine if this | ||
| 146 | /// completion status occurred before or after a given update. | ||
| 147 | complete, | ||
| 148 | /// A Module update is in progress, and this Decl has been flagged as being known | ||
| 149 | /// to require re-analysis. | ||
| 150 | outdated, | ||
| 151 | }, | ||
| 152 | /// This flag is set when this Decl is added to a check_for_deletion set, and cleared | ||
| 153 | /// when removed. | ||
| 154 | deletion_flag: bool, | ||
| 155 | /// Whether the corresponding AST decl has a `pub` keyword. | ||
| 156 | is_pub: bool, | ||
| 157 | |||
| 158 | /// An integer that can be checked against the corresponding incrementing | ||
| 159 | /// generation field of Module. This is used to determine whether `complete` status | ||
| 160 | /// represents pre- or post- re-analysis. | ||
| 161 | generation: u32, | ||
| 162 | |||
| 163 | /// Represents the position of the code in the output file. | ||
| 164 | /// This is populated regardless of semantic analysis and code generation. | ||
| 165 | link: link.File.LinkBlock, | ||
| 166 | |||
| 167 | /// Represents the function in the linked output file, if the `Decl` is a function. | ||
| 168 | /// This is stored here and not in `Fn` because `Decl` survives across updates but | ||
| 169 | /// `Fn` does not. | ||
| 170 | /// TODO Look into making `Fn` a longer lived structure and moving this field there | ||
| 171 | /// to save on memory usage. | ||
| 172 | fn_link: link.File.LinkFn, | ||
| 173 | |||
| 174 | contents_hash: std.zig.SrcHash, | ||
| 175 | |||
| 176 | /// The shallow set of other decls whose typed_value could possibly change if this Decl's | ||
| 177 | /// typed_value is modified. | ||
| 178 | dependants: DepsTable = .{}, | ||
| 179 | /// The shallow set of other decls whose typed_value changing indicates that this Decl's | ||
| 180 | /// typed_value may need to be regenerated. | ||
| 181 | dependencies: DepsTable = .{}, | ||
| 182 | |||
| 183 | /// The reason this is not `std.AutoArrayHashMapUnmanaged` is a workaround for | ||
| 184 | /// stage1 compiler giving me: `error: struct 'Module.Decl' depends on itself` | ||
| 185 | pub const DepsTable = std.ArrayHashMapUnmanaged(*Decl, void, std.array_hash_map.getAutoHashFn(*Decl), std.array_hash_map.getAutoEqlFn(*Decl), false); | ||
| 186 | |||
| 187 | pub fn destroy(self: *Decl, gpa: *Allocator) void { | ||
| 188 | gpa.free(mem.spanZ(self.name)); | ||
| 189 | if (self.typedValueManaged()) |tvm| { | ||
| 190 | tvm.deinit(gpa); | ||
| 191 | } | ||
| 192 | self.dependants.deinit(gpa); | ||
| 193 | self.dependencies.deinit(gpa); | ||
| 194 | gpa.destroy(self); | ||
| 195 | } | ||
| 196 | |||
| 197 | pub fn src(self: Decl) usize { | ||
| 198 | switch (self.scope.tag) { | ||
| 199 | .container => { | ||
| 200 | const container = @fieldParentPtr(Scope.Container, "base", self.scope); | ||
| 201 | const tree = container.file_scope.contents.tree; | ||
| 202 | // TODO Container should have it's own decls() | ||
| 203 | const decl_node = tree.root_node.decls()[self.src_index]; | ||
| 204 | return tree.token_locs[decl_node.firstToken()].start; | ||
| 205 | }, | ||
| 206 | .zir_module => { | ||
| 207 | const zir_module = @fieldParentPtr(Scope.ZIRModule, "base", self.scope); | ||
| 208 | const module = zir_module.contents.module; | ||
| 209 | const src_decl = module.decls[self.src_index]; | ||
| 210 | return src_decl.inst.src; | ||
| 211 | }, | ||
| 212 | .file, .block => unreachable, | ||
| 213 | .gen_zir => unreachable, | ||
| 214 | .local_val => unreachable, | ||
| 215 | .local_ptr => unreachable, | ||
| 216 | .decl => unreachable, | ||
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 220 | pub fn fullyQualifiedNameHash(self: Decl) Scope.NameHash { | ||
| 221 | return self.scope.fullyQualifiedNameHash(mem.spanZ(self.name)); | ||
| 222 | } | ||
| 223 | |||
| 224 | pub fn typedValue(self: *Decl) error{AnalysisFail}!TypedValue { | ||
| 225 | const tvm = self.typedValueManaged() orelse return error.AnalysisFail; | ||
| 226 | return tvm.typed_value; | ||
| 227 | } | ||
| 228 | |||
| 229 | pub fn value(self: *Decl) error{AnalysisFail}!Value { | ||
| 230 | return (try self.typedValue()).val; | ||
| 231 | } | ||
| 232 | |||
| 233 | pub fn dump(self: *Decl) void { | ||
| 234 | const loc = std.zig.findLineColumn(self.scope.source.bytes, self.src); | ||
| 235 | std.debug.print("{}:{}:{} name={} status={}", .{ | ||
| 236 | self.scope.sub_file_path, | ||
| 237 | loc.line + 1, | ||
| 238 | loc.column + 1, | ||
| 239 | mem.spanZ(self.name), | ||
| 240 | @tagName(self.analysis), | ||
| 241 | }); | ||
| 242 | if (self.typedValueManaged()) |tvm| { | ||
| 243 | std.debug.print(" ty={} val={}", .{ tvm.typed_value.ty, tvm.typed_value.val }); | ||
| 244 | } | ||
| 245 | std.debug.print("\n", .{}); | ||
| 246 | } | ||
| 247 | |||
| 248 | pub fn typedValueManaged(self: *Decl) ?*TypedValue.Managed { | ||
| 249 | switch (self.typed_value) { | ||
| 250 | .most_recent => |*x| return x, | ||
| 251 | .never_succeeded => return null, | ||
| 252 | } | ||
| 253 | } | ||
| 254 | |||
| 255 | fn removeDependant(self: *Decl, other: *Decl) void { | ||
| 256 | self.dependants.removeAssertDiscard(other); | ||
| 257 | } | ||
| 258 | |||
| 259 | fn removeDependency(self: *Decl, other: *Decl) void { | ||
| 260 | self.dependencies.removeAssertDiscard(other); | ||
| 261 | } | ||
| 262 | }; | ||
| 263 | |||
| 264 | /// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator. | ||
| 265 | pub const Fn = struct { | ||
| 266 | /// This memory owned by the Decl's TypedValue.Managed arena allocator. | ||
| 267 | analysis: union(enum) { | ||
| 268 | queued: *ZIR, | ||
| 269 | in_progress, | ||
| 270 | /// There will be a corresponding ErrorMsg in Module.failed_decls | ||
| 271 | sema_failure, | ||
| 272 | /// This Fn might be OK but it depends on another Decl which did not successfully complete | ||
| 273 | /// semantic analysis. | ||
| 274 | dependency_failure, | ||
| 275 | success: Body, | ||
| 276 | }, | ||
| 277 | owner_decl: *Decl, | ||
| 278 | |||
| 279 | /// This memory is temporary and points to stack memory for the duration | ||
| 280 | /// of Fn analysis. | ||
| 281 | pub const Analysis = struct { | ||
| 282 | inner_block: Scope.Block, | ||
| 283 | }; | ||
| 284 | |||
| 285 | /// Contains un-analyzed ZIR instructions generated from Zig source AST. | ||
| 286 | pub const ZIR = struct { | ||
| 287 | body: zir.Module.Body, | ||
| 288 | arena: std.heap.ArenaAllocator.State, | ||
| 289 | }; | ||
| 290 | |||
| 291 | /// For debugging purposes. | ||
| 292 | pub fn dump(self: *Fn, mod: Module) void { | ||
| 293 | std.debug.print("Module.Function(name={}) ", .{self.owner_decl.name}); | ||
| 294 | switch (self.analysis) { | ||
| 295 | .queued => { | ||
| 296 | std.debug.print("queued\n", .{}); | ||
| 297 | }, | ||
| 298 | .in_progress => { | ||
| 299 | std.debug.print("in_progress\n", .{}); | ||
| 300 | }, | ||
| 301 | else => { | ||
| 302 | std.debug.print("\n", .{}); | ||
| 303 | zir.dumpFn(mod, self); | ||
| 304 | }, | ||
| 305 | } | ||
| 306 | } | ||
| 307 | }; | ||
| 308 | |||
| 309 | pub const Var = struct { | ||
| 310 | /// if is_extern == true this is undefined | ||
| 311 | init: Value, | ||
| 312 | owner_decl: *Decl, | ||
| 313 | |||
| 314 | is_extern: bool, | ||
| 315 | is_mutable: bool, | ||
| 316 | is_threadlocal: bool, | ||
| 317 | }; | ||
| 318 | |||
| 319 | pub const Scope = struct { | ||
| 320 | tag: Tag, | ||
| 321 | |||
| 322 | pub const NameHash = [16]u8; | ||
| 323 | |||
| 324 | pub fn cast(base: *Scope, comptime T: type) ?*T { | ||
| 325 | if (base.tag != T.base_tag) | ||
| 326 | return null; | ||
| 327 | |||
| 328 | return @fieldParentPtr(T, "base", base); | ||
| 329 | } | ||
| 330 | |||
| 331 | /// Asserts the scope has a parent which is a DeclAnalysis and | ||
| 332 | /// returns the arena Allocator. | ||
| 333 | pub fn arena(self: *Scope) *Allocator { | ||
| 334 | switch (self.tag) { | ||
| 335 | .block => return self.cast(Block).?.arena, | ||
| 336 | .decl => return &self.cast(DeclAnalysis).?.arena.allocator, | ||
| 337 | .gen_zir => return self.cast(GenZIR).?.arena, | ||
| 338 | .local_val => return self.cast(LocalVal).?.gen_zir.arena, | ||
| 339 | .local_ptr => return self.cast(LocalPtr).?.gen_zir.arena, | ||
| 340 | .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator, | ||
| 341 | .file => unreachable, | ||
| 342 | .container => unreachable, | ||
| 343 | } | ||
| 344 | } | ||
| 345 | |||
| 346 | /// If the scope has a parent which is a `DeclAnalysis`, | ||
| 347 | /// returns the `Decl`, otherwise returns `null`. | ||
| 348 | pub fn decl(self: *Scope) ?*Decl { | ||
| 349 | return switch (self.tag) { | ||
| 350 | .block => self.cast(Block).?.decl, | ||
| 351 | .gen_zir => self.cast(GenZIR).?.decl, | ||
| 352 | .local_val => self.cast(LocalVal).?.gen_zir.decl, | ||
| 353 | .local_ptr => self.cast(LocalPtr).?.gen_zir.decl, | ||
| 354 | .decl => self.cast(DeclAnalysis).?.decl, | ||
| 355 | .zir_module => null, | ||
| 356 | .file => null, | ||
| 357 | .container => null, | ||
| 358 | }; | ||
| 359 | } | ||
| 360 | |||
| 361 | /// Asserts the scope has a parent which is a ZIRModule or Container and | ||
| 362 | /// returns it. | ||
| 363 | pub fn namespace(self: *Scope) *Scope { | ||
| 364 | switch (self.tag) { | ||
| 365 | .block => return self.cast(Block).?.decl.scope, | ||
| 366 | .gen_zir => return self.cast(GenZIR).?.decl.scope, | ||
| 367 | .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope, | ||
| 368 | .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope, | ||
| 369 | .decl => return self.cast(DeclAnalysis).?.decl.scope, | ||
| 370 | .file => return &self.cast(File).?.root_container.base, | ||
| 371 | .zir_module, .container => return self, | ||
| 372 | } | ||
| 373 | } | ||
| 374 | |||
| 375 | /// Must generate unique bytes with no collisions with other decls. | ||
| 376 | /// The point of hashing here is only to limit the number of bytes of | ||
| 377 | /// the unique identifier to a fixed size (16 bytes). | ||
| 378 | pub fn fullyQualifiedNameHash(self: *Scope, name: []const u8) NameHash { | ||
| 379 | switch (self.tag) { | ||
| 380 | .block => unreachable, | ||
| 381 | .gen_zir => unreachable, | ||
| 382 | .local_val => unreachable, | ||
| 383 | .local_ptr => unreachable, | ||
| 384 | .decl => unreachable, | ||
| 385 | .file => unreachable, | ||
| 386 | .zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name), | ||
| 387 | .container => return self.cast(Container).?.fullyQualifiedNameHash(name), | ||
| 388 | } | ||
| 389 | } | ||
| 390 | |||
| 391 | /// Asserts the scope is a child of a File and has an AST tree and returns the tree. | ||
| 392 | pub fn tree(self: *Scope) *ast.Tree { | ||
| 393 | switch (self.tag) { | ||
| 394 | .file => return self.cast(File).?.contents.tree, | ||
| 395 | .zir_module => unreachable, | ||
| 396 | .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(Container).?.file_scope.contents.tree, | ||
| 397 | .block => return self.cast(Block).?.decl.scope.cast(Container).?.file_scope.contents.tree, | ||
| 398 | .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(Container).?.file_scope.contents.tree, | ||
| 399 | .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree, | ||
| 400 | .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree, | ||
| 401 | .container => return self.cast(Container).?.file_scope.contents.tree, | ||
| 402 | } | ||
| 403 | } | ||
| 404 | |||
| 405 | /// Asserts the scope is a child of a `GenZIR` and returns it. | ||
| 406 | pub fn getGenZIR(self: *Scope) *GenZIR { | ||
| 407 | return switch (self.tag) { | ||
| 408 | .block => unreachable, | ||
| 409 | .gen_zir => self.cast(GenZIR).?, | ||
| 410 | .local_val => return self.cast(LocalVal).?.gen_zir, | ||
| 411 | .local_ptr => return self.cast(LocalPtr).?.gen_zir, | ||
| 412 | .decl => unreachable, | ||
| 413 | .zir_module => unreachable, | ||
| 414 | .file => unreachable, | ||
| 415 | .container => unreachable, | ||
| 416 | }; | ||
| 417 | } | ||
| 418 | |||
| 419 | /// Asserts the scope has a parent which is a ZIRModule, Contaienr or File and | ||
| 420 | /// returns the sub_file_path field. | ||
| 421 | pub fn subFilePath(base: *Scope) []const u8 { | ||
| 422 | switch (base.tag) { | ||
| 423 | .container => return @fieldParentPtr(Container, "base", base).file_scope.sub_file_path, | ||
| 424 | .file => return @fieldParentPtr(File, "base", base).sub_file_path, | ||
| 425 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path, | ||
| 426 | .block => unreachable, | ||
| 427 | .gen_zir => unreachable, | ||
| 428 | .local_val => unreachable, | ||
| 429 | .local_ptr => unreachable, | ||
| 430 | .decl => unreachable, | ||
| 431 | } | ||
| 432 | } | ||
| 433 | |||
| 434 | pub fn unload(base: *Scope, gpa: *Allocator) void { | ||
| 435 | switch (base.tag) { | ||
| 436 | .file => return @fieldParentPtr(File, "base", base).unload(gpa), | ||
| 437 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).unload(gpa), | ||
| 438 | .block => unreachable, | ||
| 439 | .gen_zir => unreachable, | ||
| 440 | .local_val => unreachable, | ||
| 441 | .local_ptr => unreachable, | ||
| 442 | .decl => unreachable, | ||
| 443 | .container => unreachable, | ||
| 444 | } | ||
| 445 | } | ||
| 446 | |||
| 447 | pub fn getSource(base: *Scope, module: *Module) ![:0]const u8 { | ||
| 448 | switch (base.tag) { | ||
| 449 | .container => return @fieldParentPtr(Container, "base", base).file_scope.getSource(module), | ||
| 450 | .file => return @fieldParentPtr(File, "base", base).getSource(module), | ||
| 451 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module), | ||
| 452 | .gen_zir => unreachable, | ||
| 453 | .local_val => unreachable, | ||
| 454 | .local_ptr => unreachable, | ||
| 455 | .block => unreachable, | ||
| 456 | .decl => unreachable, | ||
| 457 | } | ||
| 458 | } | ||
| 459 | |||
| 460 | /// Asserts the scope is a namespace Scope and removes the Decl from the namespace. | ||
| 461 | pub fn removeDecl(base: *Scope, child: *Decl) void { | ||
| 462 | switch (base.tag) { | ||
| 463 | .container => return @fieldParentPtr(Container, "base", base).removeDecl(child), | ||
| 464 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child), | ||
| 465 | .file => unreachable, | ||
| 466 | .block => unreachable, | ||
| 467 | .gen_zir => unreachable, | ||
| 468 | .local_val => unreachable, | ||
| 469 | .local_ptr => unreachable, | ||
| 470 | .decl => unreachable, | ||
| 471 | } | ||
| 472 | } | ||
| 473 | |||
| 474 | /// Asserts the scope is a File or ZIRModule and deinitializes it, then deallocates it. | ||
| 475 | pub fn destroy(base: *Scope, gpa: *Allocator) void { | ||
| 476 | switch (base.tag) { | ||
| 477 | .file => { | ||
| 478 | const scope_file = @fieldParentPtr(File, "base", base); | ||
| 479 | scope_file.deinit(gpa); | ||
| 480 | gpa.destroy(scope_file); | ||
| 481 | }, | ||
| 482 | .zir_module => { | ||
| 483 | const scope_zir_module = @fieldParentPtr(ZIRModule, "base", base); | ||
| 484 | scope_zir_module.deinit(gpa); | ||
| 485 | gpa.destroy(scope_zir_module); | ||
| 486 | }, | ||
| 487 | .block => unreachable, | ||
| 488 | .gen_zir => unreachable, | ||
| 489 | .local_val => unreachable, | ||
| 490 | .local_ptr => unreachable, | ||
| 491 | .decl => unreachable, | ||
| 492 | .container => unreachable, | ||
| 493 | } | ||
| 494 | } | ||
| 495 | |||
| 496 | fn name_hash_hash(x: NameHash) u32 { | ||
| 497 | return @truncate(u32, @bitCast(u128, x)); | ||
| 498 | } | ||
| 499 | |||
| 500 | fn name_hash_eql(a: NameHash, b: NameHash) bool { | ||
| 501 | return @bitCast(u128, a) == @bitCast(u128, b); | ||
| 502 | } | ||
| 503 | |||
| 504 | pub const Tag = enum { | ||
| 505 | /// .zir source code. | ||
| 506 | zir_module, | ||
| 507 | /// .zig source code. | ||
| 508 | file, | ||
| 509 | /// struct, enum or union, every .file contains one of these. | ||
| 510 | container, | ||
| 511 | block, | ||
| 512 | decl, | ||
| 513 | gen_zir, | ||
| 514 | local_val, | ||
| 515 | local_ptr, | ||
| 516 | }; | ||
| 517 | |||
| 518 | pub const Container = struct { | ||
| 519 | pub const base_tag: Tag = .container; | ||
| 520 | base: Scope = Scope{ .tag = base_tag }, | ||
| 521 | |||
| 522 | file_scope: *Scope.File, | ||
| 523 | |||
| 524 | /// Direct children of the file. | ||
| 525 | decls: std.AutoArrayHashMapUnmanaged(*Decl, void), | ||
| 526 | |||
| 527 | // TODO implement container types and put this in a status union | ||
| 528 | // ty: Type | ||
| 529 | |||
| 530 | pub fn deinit(self: *Container, gpa: *Allocator) void { | ||
| 531 | self.decls.deinit(gpa); | ||
| 532 | self.* = undefined; | ||
| 533 | } | ||
| 534 | |||
| 535 | pub fn removeDecl(self: *Container, child: *Decl) void { | ||
| 536 | _ = self.decls.remove(child); | ||
| 537 | } | ||
| 538 | |||
| 539 | pub fn fullyQualifiedNameHash(self: *Container, name: []const u8) NameHash { | ||
| 540 | // TODO container scope qualified names. | ||
| 541 | return std.zig.hashSrc(name); | ||
| 542 | } | ||
| 543 | }; | ||
| 544 | |||
| 545 | pub const File = struct { | ||
| 546 | pub const base_tag: Tag = .file; | ||
| 547 | base: Scope = Scope{ .tag = base_tag }, | ||
| 548 | |||
| 549 | /// Relative to the owning package's root_src_dir. | ||
| 550 | /// Reference to external memory, not owned by File. | ||
| 551 | sub_file_path: []const u8, | ||
| 552 | source: union(enum) { | ||
| 553 | unloaded: void, | ||
| 554 | bytes: [:0]const u8, | ||
| 555 | }, | ||
| 556 | contents: union { | ||
| 557 | not_available: void, | ||
| 558 | tree: *ast.Tree, | ||
| 559 | }, | ||
| 560 | status: enum { | ||
| 561 | never_loaded, | ||
| 562 | unloaded_success, | ||
| 563 | unloaded_parse_failure, | ||
| 564 | loaded_success, | ||
| 565 | }, | ||
| 566 | |||
| 567 | root_container: Container, | ||
| 568 | |||
| 569 | pub fn unload(self: *File, gpa: *Allocator) void { | ||
| 570 | switch (self.status) { | ||
| 571 | .never_loaded, | ||
| 572 | .unloaded_parse_failure, | ||
| 573 | .unloaded_success, | ||
| 574 | => {}, | ||
| 575 | |||
| 576 | .loaded_success => { | ||
| 577 | self.contents.tree.deinit(); | ||
| 578 | self.status = .unloaded_success; | ||
| 579 | }, | ||
| 580 | } | ||
| 581 | switch (self.source) { | ||
| 582 | .bytes => |bytes| { | ||
| 583 | gpa.free(bytes); | ||
| 584 | self.source = .{ .unloaded = {} }; | ||
| 585 | }, | ||
| 586 | .unloaded => {}, | ||
| 587 | } | ||
| 588 | } | ||
| 589 | |||
| 590 | pub fn deinit(self: *File, gpa: *Allocator) void { | ||
| 591 | self.root_container.deinit(gpa); | ||
| 592 | self.unload(gpa); | ||
| 593 | self.* = undefined; | ||
| 594 | } | ||
| 595 | |||
| 596 | pub fn dumpSrc(self: *File, src: usize) void { | ||
| 597 | const loc = std.zig.findLineColumn(self.source.bytes, src); | ||
| 598 | std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 }); | ||
| 599 | } | ||
| 600 | |||
| 601 | pub fn getSource(self: *File, module: *Module) ![:0]const u8 { | ||
| 602 | switch (self.source) { | ||
| 603 | .unloaded => { | ||
| 604 | const source = try module.root_pkg.root_src_directory.handle.readFileAllocOptions( | ||
| 605 | module.gpa, | ||
| 606 | self.sub_file_path, | ||
| 607 | std.math.maxInt(u32), | ||
| 608 | null, | ||
| 609 | 1, | ||
| 610 | 0, | ||
| 611 | ); | ||
| 612 | self.source = .{ .bytes = source }; | ||
| 613 | return source; | ||
| 614 | }, | ||
| 615 | .bytes => |bytes| return bytes, | ||
| 616 | } | ||
| 617 | } | ||
| 618 | }; | ||
| 619 | |||
| 620 | pub const ZIRModule = struct { | ||
| 621 | pub const base_tag: Tag = .zir_module; | ||
| 622 | base: Scope = Scope{ .tag = base_tag }, | ||
| 623 | /// Relative to the owning package's root_src_dir. | ||
| 624 | /// Reference to external memory, not owned by ZIRModule. | ||
| 625 | sub_file_path: []const u8, | ||
| 626 | source: union(enum) { | ||
| 627 | unloaded: void, | ||
| 628 | bytes: [:0]const u8, | ||
| 629 | }, | ||
| 630 | contents: union { | ||
| 631 | not_available: void, | ||
| 632 | module: *zir.Module, | ||
| 633 | }, | ||
| 634 | status: enum { | ||
| 635 | never_loaded, | ||
| 636 | unloaded_success, | ||
| 637 | unloaded_parse_failure, | ||
| 638 | unloaded_sema_failure, | ||
| 639 | |||
| 640 | loaded_sema_failure, | ||
| 641 | loaded_success, | ||
| 642 | }, | ||
| 643 | |||
| 644 | /// Even though .zir files only have 1 module, this set is still needed | ||
| 645 | /// because of anonymous Decls, which can exist in the global set, but | ||
| 646 | /// not this one. | ||
| 647 | decls: ArrayListUnmanaged(*Decl), | ||
| 648 | |||
| 649 | pub fn unload(self: *ZIRModule, gpa: *Allocator) void { | ||
| 650 | switch (self.status) { | ||
| 651 | .never_loaded, | ||
| 652 | .unloaded_parse_failure, | ||
| 653 | .unloaded_sema_failure, | ||
| 654 | .unloaded_success, | ||
| 655 | => {}, | ||
| 656 | |||
| 657 | .loaded_success => { | ||
| 658 | self.contents.module.deinit(gpa); | ||
| 659 | gpa.destroy(self.contents.module); | ||
| 660 | self.contents = .{ .not_available = {} }; | ||
| 661 | self.status = .unloaded_success; | ||
| 662 | }, | ||
| 663 | .loaded_sema_failure => { | ||
| 664 | self.contents.module.deinit(gpa); | ||
| 665 | gpa.destroy(self.contents.module); | ||
| 666 | self.contents = .{ .not_available = {} }; | ||
| 667 | self.status = .unloaded_sema_failure; | ||
| 668 | }, | ||
| 669 | } | ||
| 670 | switch (self.source) { | ||
| 671 | .bytes => |bytes| { | ||
| 672 | gpa.free(bytes); | ||
| 673 | self.source = .{ .unloaded = {} }; | ||
| 674 | }, | ||
| 675 | .unloaded => {}, | ||
| 676 | } | ||
| 677 | } | ||
| 678 | |||
| 679 | pub fn deinit(self: *ZIRModule, gpa: *Allocator) void { | ||
| 680 | self.decls.deinit(gpa); | ||
| 681 | self.unload(gpa); | ||
| 682 | self.* = undefined; | ||
| 683 | } | ||
| 684 | |||
| 685 | pub fn removeDecl(self: *ZIRModule, child: *Decl) void { | ||
| 686 | for (self.decls.items) |item, i| { | ||
| 687 | if (item == child) { | ||
| 688 | _ = self.decls.swapRemove(i); | ||
| 689 | return; | ||
| 690 | } | ||
| 691 | } | ||
| 692 | } | ||
| 693 | |||
| 694 | pub fn dumpSrc(self: *ZIRModule, src: usize) void { | ||
| 695 | const loc = std.zig.findLineColumn(self.source.bytes, src); | ||
| 696 | std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 }); | ||
| 697 | } | ||
| 698 | |||
| 699 | pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 { | ||
| 700 | switch (self.source) { | ||
| 701 | .unloaded => { | ||
| 702 | const source = try module.root_pkg.root_src_directory.handle.readFileAllocOptions( | ||
| 703 | module.gpa, | ||
| 704 | self.sub_file_path, | ||
| 705 | std.math.maxInt(u32), | ||
| 706 | null, | ||
| 707 | 1, | ||
| 708 | 0, | ||
| 709 | ); | ||
| 710 | self.source = .{ .bytes = source }; | ||
| 711 | return source; | ||
| 712 | }, | ||
| 713 | .bytes => |bytes| return bytes, | ||
| 714 | } | ||
| 715 | } | ||
| 716 | |||
| 717 | pub fn fullyQualifiedNameHash(self: *ZIRModule, name: []const u8) NameHash { | ||
| 718 | // ZIR modules only have 1 file with all decls global in the same namespace. | ||
| 719 | return std.zig.hashSrc(name); | ||
| 720 | } | ||
| 721 | }; | ||
| 722 | |||
| 723 | /// This is a temporary structure, references to it are valid only | ||
| 724 | /// during semantic analysis of the block. | ||
| 725 | pub const Block = struct { | ||
| 726 | pub const base_tag: Tag = .block; | ||
| 727 | base: Scope = Scope{ .tag = base_tag }, | ||
| 728 | parent: ?*Block, | ||
| 729 | func: ?*Fn, | ||
| 730 | decl: *Decl, | ||
| 731 | instructions: ArrayListUnmanaged(*Inst), | ||
| 732 | /// Points to the arena allocator of DeclAnalysis | ||
| 733 | arena: *Allocator, | ||
| 734 | label: ?Label = null, | ||
| 735 | is_comptime: bool, | ||
| 736 | |||
| 737 | pub const Label = struct { | ||
| 738 | zir_block: *zir.Inst.Block, | ||
| 739 | results: ArrayListUnmanaged(*Inst), | ||
| 740 | block_inst: *Inst.Block, | ||
| 741 | }; | ||
| 742 | }; | ||
| 743 | |||
| 744 | /// This is a temporary structure, references to it are valid only | ||
| 745 | /// during semantic analysis of the decl. | ||
| 746 | pub const DeclAnalysis = struct { | ||
| 747 | pub const base_tag: Tag = .decl; | ||
| 748 | base: Scope = Scope{ .tag = base_tag }, | ||
| 749 | decl: *Decl, | ||
| 750 | arena: std.heap.ArenaAllocator, | ||
| 751 | }; | ||
| 752 | |||
| 753 | /// This is a temporary structure, references to it are valid only | ||
| 754 | /// during semantic analysis of the decl. | ||
| 755 | pub const GenZIR = struct { | ||
| 756 | pub const base_tag: Tag = .gen_zir; | ||
| 757 | base: Scope = Scope{ .tag = base_tag }, | ||
| 758 | /// Parents can be: `GenZIR`, `ZIRModule`, `File` | ||
| 759 | parent: *Scope, | ||
| 760 | decl: *Decl, | ||
| 761 | arena: *Allocator, | ||
| 762 | /// The first N instructions in a function body ZIR are arg instructions. | ||
| 763 | instructions: std.ArrayListUnmanaged(*zir.Inst) = .{}, | ||
| 764 | label: ?Label = null, | ||
| 765 | |||
| 766 | pub const Label = struct { | ||
| 767 | token: ast.TokenIndex, | ||
| 768 | block_inst: *zir.Inst.Block, | ||
| 769 | result_loc: astgen.ResultLoc, | ||
| 770 | }; | ||
| 771 | }; | ||
| 772 | |||
| 773 | /// This is always a `const` local and importantly the `inst` is a value type, not a pointer. | ||
| 774 | /// This structure lives as long as the AST generation of the Block | ||
| 775 | /// node that contains the variable. | ||
| 776 | pub const LocalVal = struct { | ||
| 777 | pub const base_tag: Tag = .local_val; | ||
| 778 | base: Scope = Scope{ .tag = base_tag }, | ||
| 779 | /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`. | ||
| 780 | parent: *Scope, | ||
| 781 | gen_zir: *GenZIR, | ||
| 782 | name: []const u8, | ||
| 783 | inst: *zir.Inst, | ||
| 784 | }; | ||
| 785 | |||
| 786 | /// This could be a `const` or `var` local. It has a pointer instead of a value. | ||
| 787 | /// This structure lives as long as the AST generation of the Block | ||
| 788 | /// node that contains the variable. | ||
| 789 | pub const LocalPtr = struct { | ||
| 790 | pub const base_tag: Tag = .local_ptr; | ||
| 791 | base: Scope = Scope{ .tag = base_tag }, | ||
| 792 | /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`. | ||
| 793 | parent: *Scope, | ||
| 794 | gen_zir: *GenZIR, | ||
| 795 | name: []const u8, | ||
| 796 | ptr: *zir.Inst, | ||
| 797 | }; | ||
| 798 | }; | ||
| 799 | |||
| 800 | pub const InnerError = error{ OutOfMemory, AnalysisFail }; | ||
| 801 | |||
| 802 | pub fn deinit(self: *Module) void { | ||
| 803 | const gpa = self.gpa; | ||
| 804 | |||
| 805 | self.zig_cache_artifact_directory.handle.close(); | ||
| 806 | |||
| 807 | self.deletion_set.deinit(gpa); | ||
| 808 | |||
| 809 | for (self.decl_table.items()) |entry| { | ||
| 810 | entry.value.destroy(gpa); | ||
| 811 | } | ||
| 812 | self.decl_table.deinit(gpa); | ||
| 813 | |||
| 814 | for (self.failed_decls.items()) |entry| { | ||
| 815 | entry.value.destroy(gpa); | ||
| 816 | } | ||
| 817 | self.failed_decls.deinit(gpa); | ||
| 818 | |||
| 819 | for (self.failed_files.items()) |entry| { | ||
| 820 | entry.value.destroy(gpa); | ||
| 821 | } | ||
| 822 | self.failed_files.deinit(gpa); | ||
| 823 | |||
| 824 | for (self.failed_exports.items()) |entry| { | ||
| 825 | entry.value.destroy(gpa); | ||
| 826 | } | ||
| 827 | self.failed_exports.deinit(gpa); | ||
| 828 | |||
| 829 | for (self.decl_exports.items()) |entry| { | ||
| 830 | const export_list = entry.value; | ||
| 831 | gpa.free(export_list); | ||
| 832 | } | ||
| 833 | self.decl_exports.deinit(gpa); | ||
| 834 | |||
| 835 | for (self.export_owners.items()) |entry| { | ||
| 836 | freeExportList(gpa, entry.value); | ||
| 837 | } | ||
| 838 | self.export_owners.deinit(gpa); | ||
| 839 | |||
| 840 | self.symbol_exports.deinit(gpa); | ||
| 841 | self.root_scope.destroy(gpa); | ||
| 842 | |||
| 843 | var it = self.global_error_set.iterator(); | ||
| 844 | while (it.next()) |entry| { | ||
| 845 | gpa.free(entry.key); | ||
| 846 | } | ||
| 847 | self.global_error_set.deinit(gpa); | ||
| 848 | } | ||
| 849 | |||
| 850 | fn freeExportList(gpa: *Allocator, export_list: []*Export) void { | ||
| 851 | for (export_list) |exp| { | ||
| 852 | gpa.free(exp.options.name); | ||
| 853 | gpa.destroy(exp); | ||
| 854 | } | ||
| 855 | gpa.free(export_list); | ||
| 856 | } | ||
| 857 | |||
| 858 | pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { | ||
| 859 | const tracy = trace(@src()); | ||
| 860 | defer tracy.end(); | ||
| 861 | |||
| 862 | const subsequent_analysis = switch (decl.analysis) { | ||
| 863 | .in_progress => unreachable, | ||
| 864 | |||
| 865 | .sema_failure, | ||
| 866 | .sema_failure_retryable, | ||
| 867 | .codegen_failure, | ||
| 868 | .dependency_failure, | ||
| 869 | .codegen_failure_retryable, | ||
| 870 | => return error.AnalysisFail, | ||
| 871 | |||
| 872 | .complete => return, | ||
| 873 | |||
| 874 | .outdated => blk: { | ||
| 875 | log.debug("re-analyzing {}\n", .{decl.name}); | ||
| 876 | |||
| 877 | // The exports this Decl performs will be re-discovered, so we remove them here | ||
| 878 | // prior to re-analysis. | ||
| 879 | self.deleteDeclExports(decl); | ||
| 880 | // Dependencies will be re-discovered, so we remove them here prior to re-analysis. | ||
| 881 | for (decl.dependencies.items()) |entry| { | ||
| 882 | const dep = entry.key; | ||
| 883 | dep.removeDependant(decl); | ||
| 884 | if (dep.dependants.items().len == 0 and !dep.deletion_flag) { | ||
| 885 | // We don't perform a deletion here, because this Decl or another one | ||
| 886 | // may end up referencing it before the update is complete. | ||
| 887 | dep.deletion_flag = true; | ||
| 888 | try self.deletion_set.append(self.gpa, dep); | ||
| 889 | } | ||
| 890 | } | ||
| 891 | decl.dependencies.clearRetainingCapacity(); | ||
| 892 | |||
| 893 | break :blk true; | ||
| 894 | }, | ||
| 895 | |||
| 896 | .unreferenced => false, | ||
| 897 | }; | ||
| 898 | |||
| 899 | const type_changed = if (self.root_scope.cast(Scope.ZIRModule)) |zir_module| | ||
| 900 | try zir_sema.analyzeZirDecl(self, decl, zir_module.contents.module.decls[decl.src_index]) | ||
| 901 | else | ||
| 902 | self.astGenAndAnalyzeDecl(decl) catch |err| switch (err) { | ||
| 903 | error.OutOfMemory => return error.OutOfMemory, | ||
| 904 | error.AnalysisFail => return error.AnalysisFail, | ||
| 905 | else => { | ||
| 906 | try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1); | ||
| 907 | self.failed_decls.putAssumeCapacityNoClobber(decl, try Compilation.ErrorMsg.create( | ||
| 908 | self.gpa, | ||
| 909 | decl.src(), | ||
| 910 | "unable to analyze: {}", | ||
| 911 | .{@errorName(err)}, | ||
| 912 | )); | ||
| 913 | decl.analysis = .sema_failure_retryable; | ||
| 914 | return error.AnalysisFail; | ||
| 915 | }, | ||
| 916 | }; | ||
| 917 | |||
| 918 | if (subsequent_analysis) { | ||
| 919 | // We may need to chase the dependants and re-analyze them. | ||
| 920 | // However, if the decl is a function, and the type is the same, we do not need to. | ||
| 921 | if (type_changed or decl.typed_value.most_recent.typed_value.val.tag() != .function) { | ||
| 922 | for (decl.dependants.items()) |entry| { | ||
| 923 | const dep = entry.key; | ||
| 924 | switch (dep.analysis) { | ||
| 925 | .unreferenced => unreachable, | ||
| 926 | .in_progress => unreachable, | ||
| 927 | .outdated => continue, // already queued for update | ||
| 928 | |||
| 929 | .dependency_failure, | ||
| 930 | .sema_failure, | ||
| 931 | .sema_failure_retryable, | ||
| 932 | .codegen_failure, | ||
| 933 | .codegen_failure_retryable, | ||
| 934 | .complete, | ||
| 935 | => if (dep.generation != self.generation) { | ||
| 936 | try self.markOutdatedDecl(dep); | ||
| 937 | }, | ||
| 938 | } | ||
| 939 | } | ||
| 940 | } | ||
| 941 | } | ||
| 942 | } | ||
| 943 | |||
| 944 | fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { | ||
| 945 | const tracy = trace(@src()); | ||
| 946 | defer tracy.end(); | ||
| 947 | |||
| 948 | const container_scope = decl.scope.cast(Scope.Container).?; | ||
| 949 | const tree = try self.getAstTree(container_scope); | ||
| 950 | const ast_node = tree.root_node.decls()[decl.src_index]; | ||
| 951 | switch (ast_node.tag) { | ||
| 952 | .FnProto => { | ||
| 953 | const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", ast_node); | ||
| 954 | |||
| 955 | decl.analysis = .in_progress; | ||
| 956 | |||
| 957 | // This arena allocator's memory is discarded at the end of this function. It is used | ||
| 958 | // to determine the type of the function, and hence the type of the decl, which is needed | ||
| 959 | // to complete the Decl analysis. | ||
| 960 | var fn_type_scope_arena = std.heap.ArenaAllocator.init(self.gpa); | ||
| 961 | defer fn_type_scope_arena.deinit(); | ||
| 962 | var fn_type_scope: Scope.GenZIR = .{ | ||
| 963 | .decl = decl, | ||
| 964 | .arena = &fn_type_scope_arena.allocator, | ||
| 965 | .parent = decl.scope, | ||
| 966 | }; | ||
| 967 | defer fn_type_scope.instructions.deinit(self.gpa); | ||
| 968 | |||
| 969 | decl.is_pub = fn_proto.getVisibToken() != null; | ||
| 970 | const body_node = fn_proto.getBodyNode() orelse | ||
| 971 | return self.failTok(&fn_type_scope.base, fn_proto.fn_token, "TODO implement extern functions", .{}); | ||
| 972 | |||
| 973 | const param_decls = fn_proto.params(); | ||
| 974 | const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len); | ||
| 975 | |||
| 976 | const fn_src = tree.token_locs[fn_proto.fn_token].start; | ||
| 977 | const type_type = try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{ | ||
| 978 | .ty = Type.initTag(.type), | ||
| 979 | .val = Value.initTag(.type_type), | ||
| 980 | }); | ||
| 981 | const type_type_rl: astgen.ResultLoc = .{ .ty = type_type }; | ||
| 982 | for (param_decls) |param_decl, i| { | ||
| 983 | const param_type_node = switch (param_decl.param_type) { | ||
| 984 | .any_type => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement anytype parameter", .{}), | ||
| 985 | .type_expr => |node| node, | ||
| 986 | }; | ||
| 987 | param_types[i] = try astgen.expr(self, &fn_type_scope.base, type_type_rl, param_type_node); | ||
| 988 | } | ||
| 989 | if (fn_proto.getVarArgsToken()) |var_args_token| { | ||
| 990 | return self.failTok(&fn_type_scope.base, var_args_token, "TODO implement var args", .{}); | ||
| 991 | } | ||
| 992 | if (fn_proto.getLibName()) |lib_name| { | ||
| 993 | return self.failNode(&fn_type_scope.base, lib_name, "TODO implement function library name", .{}); | ||
| 994 | } | ||
| 995 | if (fn_proto.getAlignExpr()) |align_expr| { | ||
| 996 | return self.failNode(&fn_type_scope.base, align_expr, "TODO implement function align expression", .{}); | ||
| 997 | } | ||
| 998 | if (fn_proto.getSectionExpr()) |sect_expr| { | ||
| 999 | return self.failNode(&fn_type_scope.base, sect_expr, "TODO implement function section expression", .{}); | ||
| 1000 | } | ||
| 1001 | if (fn_proto.getCallconvExpr()) |callconv_expr| { | ||
| 1002 | return self.failNode( | ||
| 1003 | &fn_type_scope.base, | ||
| 1004 | callconv_expr, | ||
| 1005 | "TODO implement function calling convention expression", | ||
| 1006 | .{}, | ||
| 1007 | ); | ||
| 1008 | } | ||
| 1009 | const return_type_expr = switch (fn_proto.return_type) { | ||
| 1010 | .Explicit => |node| node, | ||
| 1011 | .InferErrorSet => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement inferred error sets", .{}), | ||
| 1012 | .Invalid => |tok| return self.failTok(&fn_type_scope.base, tok, "unable to parse return type", .{}), | ||
| 1013 | }; | ||
| 1014 | |||
| 1015 | const return_type_inst = try astgen.expr(self, &fn_type_scope.base, type_type_rl, return_type_expr); | ||
| 1016 | const fn_type_inst = try astgen.addZIRInst(self, &fn_type_scope.base, fn_src, zir.Inst.FnType, .{ | ||
| 1017 | .return_type = return_type_inst, | ||
| 1018 | .param_types = param_types, | ||
| 1019 | }, .{}); | ||
| 1020 | |||
| 1021 | // We need the memory for the Type to go into the arena for the Decl | ||
| 1022 | var decl_arena = std.heap.ArenaAllocator.init(self.gpa); | ||
| 1023 | errdefer decl_arena.deinit(); | ||
| 1024 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); | ||
| 1025 | |||
| 1026 | var block_scope: Scope.Block = .{ | ||
| 1027 | .parent = null, | ||
| 1028 | .func = null, | ||
| 1029 | .decl = decl, | ||
| 1030 | .instructions = .{}, | ||
| 1031 | .arena = &decl_arena.allocator, | ||
| 1032 | .is_comptime = false, | ||
| 1033 | }; | ||
| 1034 | defer block_scope.instructions.deinit(self.gpa); | ||
| 1035 | |||
| 1036 | const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{ | ||
| 1037 | .instructions = fn_type_scope.instructions.items, | ||
| 1038 | }); | ||
| 1039 | const new_func = try decl_arena.allocator.create(Fn); | ||
| 1040 | const fn_payload = try decl_arena.allocator.create(Value.Payload.Function); | ||
| 1041 | |||
| 1042 | const fn_zir = blk: { | ||
| 1043 | // This scope's arena memory is discarded after the ZIR generation | ||
| 1044 | // pass completes, and semantic analysis of it completes. | ||
| 1045 | var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa); | ||
| 1046 | errdefer gen_scope_arena.deinit(); | ||
| 1047 | var gen_scope: Scope.GenZIR = .{ | ||
| 1048 | .decl = decl, | ||
| 1049 | .arena = &gen_scope_arena.allocator, | ||
| 1050 | .parent = decl.scope, | ||
| 1051 | }; | ||
| 1052 | defer gen_scope.instructions.deinit(self.gpa); | ||
| 1053 | |||
| 1054 | // We need an instruction for each parameter, and they must be first in the body. | ||
| 1055 | try gen_scope.instructions.resize(self.gpa, fn_proto.params_len); | ||
| 1056 | var params_scope = &gen_scope.base; | ||
| 1057 | for (fn_proto.params()) |param, i| { | ||
| 1058 | const name_token = param.name_token.?; | ||
| 1059 | const src = tree.token_locs[name_token].start; | ||
| 1060 | const param_name = tree.tokenSlice(name_token); // TODO: call identifierTokenString | ||
| 1061 | const arg = try gen_scope_arena.allocator.create(zir.Inst.Arg); | ||
| 1062 | arg.* = .{ | ||
| 1063 | .base = .{ | ||
| 1064 | .tag = .arg, | ||
| 1065 | .src = src, | ||
| 1066 | }, | ||
| 1067 | .positionals = .{ | ||
| 1068 | .name = param_name, | ||
| 1069 | }, | ||
| 1070 | .kw_args = .{}, | ||
| 1071 | }; | ||
| 1072 | gen_scope.instructions.items[i] = &arg.base; | ||
| 1073 | const sub_scope = try gen_scope_arena.allocator.create(Scope.LocalVal); | ||
| 1074 | sub_scope.* = .{ | ||
| 1075 | .parent = params_scope, | ||
| 1076 | .gen_zir = &gen_scope, | ||
| 1077 | .name = param_name, | ||
| 1078 | .inst = &arg.base, | ||
| 1079 | }; | ||
| 1080 | params_scope = &sub_scope.base; | ||
| 1081 | } | ||
| 1082 | |||
| 1083 | const body_block = body_node.cast(ast.Node.Block).?; | ||
| 1084 | |||
| 1085 | try astgen.blockExpr(self, params_scope, body_block); | ||
| 1086 | |||
| 1087 | if (gen_scope.instructions.items.len == 0 or | ||
| 1088 | !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()) | ||
| 1089 | { | ||
| 1090 | const src = tree.token_locs[body_block.rbrace].start; | ||
| 1091 | _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid); | ||
| 1092 | } | ||
| 1093 | |||
| 1094 | const fn_zir = try gen_scope_arena.allocator.create(Fn.ZIR); | ||
| 1095 | fn_zir.* = .{ | ||
| 1096 | .body = .{ | ||
| 1097 | .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items), | ||
| 1098 | }, | ||
| 1099 | .arena = gen_scope_arena.state, | ||
| 1100 | }; | ||
| 1101 | break :blk fn_zir; | ||
| 1102 | }; | ||
| 1103 | |||
| 1104 | new_func.* = .{ | ||
| 1105 | .analysis = .{ .queued = fn_zir }, | ||
| 1106 | .owner_decl = decl, | ||
| 1107 | }; | ||
| 1108 | fn_payload.* = .{ .func = new_func }; | ||
| 1109 | |||
| 1110 | var prev_type_has_bits = false; | ||
| 1111 | var type_changed = true; | ||
| 1112 | |||
| 1113 | if (decl.typedValueManaged()) |tvm| { | ||
| 1114 | prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits(); | ||
| 1115 | type_changed = !tvm.typed_value.ty.eql(fn_type); | ||
| 1116 | |||
| 1117 | tvm.deinit(self.gpa); | ||
| 1118 | } | ||
| 1119 | |||
| 1120 | decl_arena_state.* = decl_arena.state; | ||
| 1121 | decl.typed_value = .{ | ||
| 1122 | .most_recent = .{ | ||
| 1123 | .typed_value = .{ | ||
| 1124 | .ty = fn_type, | ||
| 1125 | .val = Value.initPayload(&fn_payload.base), | ||
| 1126 | }, | ||
| 1127 | .arena = decl_arena_state, | ||
| 1128 | }, | ||
| 1129 | }; | ||
| 1130 | decl.analysis = .complete; | ||
| 1131 | decl.generation = self.generation; | ||
| 1132 | |||
| 1133 | if (fn_type.hasCodeGenBits()) { | ||
| 1134 | // We don't fully codegen the decl until later, but we do need to reserve a global | ||
| 1135 | // offset table index for it. This allows us to codegen decls out of dependency order, | ||
| 1136 | // increasing how many computations can be done in parallel. | ||
| 1137 | try self.comp.bin_file.allocateDeclIndexes(decl); | ||
| 1138 | try self.comp.work_queue.writeItem(.{ .codegen_decl = decl }); | ||
| 1139 | } else if (prev_type_has_bits) { | ||
| 1140 | self.comp.bin_file.freeDecl(decl); | ||
| 1141 | } | ||
| 1142 | |||
| 1143 | if (fn_proto.getExternExportInlineToken()) |maybe_export_token| { | ||
| 1144 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { | ||
| 1145 | const export_src = tree.token_locs[maybe_export_token].start; | ||
| 1146 | const name_loc = tree.token_locs[fn_proto.getNameToken().?]; | ||
| 1147 | const name = tree.tokenSliceLoc(name_loc); | ||
| 1148 | // The scope needs to have the decl in it. | ||
| 1149 | try self.analyzeExport(&block_scope.base, export_src, name, decl); | ||
| 1150 | } | ||
| 1151 | } | ||
| 1152 | return type_changed; | ||
| 1153 | }, | ||
| 1154 | .VarDecl => { | ||
| 1155 | const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", ast_node); | ||
| 1156 | |||
| 1157 | decl.analysis = .in_progress; | ||
| 1158 | |||
| 1159 | // We need the memory for the Type to go into the arena for the Decl | ||
| 1160 | var decl_arena = std.heap.ArenaAllocator.init(self.gpa); | ||
| 1161 | errdefer decl_arena.deinit(); | ||
| 1162 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); | ||
| 1163 | |||
| 1164 | var block_scope: Scope.Block = .{ | ||
| 1165 | .parent = null, | ||
| 1166 | .func = null, | ||
| 1167 | .decl = decl, | ||
| 1168 | .instructions = .{}, | ||
| 1169 | .arena = &decl_arena.allocator, | ||
| 1170 | .is_comptime = true, | ||
| 1171 | }; | ||
| 1172 | defer block_scope.instructions.deinit(self.gpa); | ||
| 1173 | |||
| 1174 | decl.is_pub = var_decl.getVisibToken() != null; | ||
| 1175 | const is_extern = blk: { | ||
| 1176 | const maybe_extern_token = var_decl.getExternExportToken() orelse | ||
| 1177 | break :blk false; | ||
| 1178 | if (tree.token_ids[maybe_extern_token] != .Keyword_extern) break :blk false; | ||
| 1179 | if (var_decl.getInitNode()) |some| { | ||
| 1180 | return self.failNode(&block_scope.base, some, "extern variables have no initializers", .{}); | ||
| 1181 | } | ||
| 1182 | break :blk true; | ||
| 1183 | }; | ||
| 1184 | if (var_decl.getLibName()) |lib_name| { | ||
| 1185 | assert(is_extern); | ||
| 1186 | return self.failNode(&block_scope.base, lib_name, "TODO implement function library name", .{}); | ||
| 1187 | } | ||
| 1188 | const is_mutable = tree.token_ids[var_decl.mut_token] == .Keyword_var; | ||
| 1189 | const is_threadlocal = if (var_decl.getThreadLocalToken()) |some| blk: { | ||
| 1190 | if (!is_mutable) { | ||
| 1191 | return self.failTok(&block_scope.base, some, "threadlocal variable cannot be constant", .{}); | ||
| 1192 | } | ||
| 1193 | break :blk true; | ||
| 1194 | } else false; | ||
| 1195 | assert(var_decl.getComptimeToken() == null); | ||
| 1196 | if (var_decl.getAlignNode()) |align_expr| { | ||
| 1197 | return self.failNode(&block_scope.base, align_expr, "TODO implement function align expression", .{}); | ||
| 1198 | } | ||
| 1199 | if (var_decl.getSectionNode()) |sect_expr| { | ||
| 1200 | return self.failNode(&block_scope.base, sect_expr, "TODO implement function section expression", .{}); | ||
| 1201 | } | ||
| 1202 | |||
| 1203 | const var_info: struct { ty: Type, val: ?Value } = if (var_decl.getInitNode()) |init_node| vi: { | ||
| 1204 | var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa); | ||
| 1205 | defer gen_scope_arena.deinit(); | ||
| 1206 | var gen_scope: Scope.GenZIR = .{ | ||
| 1207 | .decl = decl, | ||
| 1208 | .arena = &gen_scope_arena.allocator, | ||
| 1209 | .parent = decl.scope, | ||
| 1210 | }; | ||
| 1211 | defer gen_scope.instructions.deinit(self.gpa); | ||
| 1212 | |||
| 1213 | const init_result_loc: astgen.ResultLoc = if (var_decl.getTypeNode()) |type_node| rl: { | ||
| 1214 | const src = tree.token_locs[type_node.firstToken()].start; | ||
| 1215 | const type_type = try astgen.addZIRInstConst(self, &gen_scope.base, src, .{ | ||
| 1216 | .ty = Type.initTag(.type), | ||
| 1217 | .val = Value.initTag(.type_type), | ||
| 1218 | }); | ||
| 1219 | const var_type = try astgen.expr(self, &gen_scope.base, .{ .ty = type_type }, type_node); | ||
| 1220 | break :rl .{ .ty = var_type }; | ||
| 1221 | } else .none; | ||
| 1222 | |||
| 1223 | const src = tree.token_locs[init_node.firstToken()].start; | ||
| 1224 | const init_inst = try astgen.expr(self, &gen_scope.base, init_result_loc, init_node); | ||
| 1225 | |||
| 1226 | var inner_block: Scope.Block = .{ | ||
| 1227 | .parent = null, | ||
| 1228 | .func = null, | ||
| 1229 | .decl = decl, | ||
| 1230 | .instructions = .{}, | ||
| 1231 | .arena = &gen_scope_arena.allocator, | ||
| 1232 | .is_comptime = true, | ||
| 1233 | }; | ||
| 1234 | defer inner_block.instructions.deinit(self.gpa); | ||
| 1235 | try zir_sema.analyzeBody(self, &inner_block.base, .{ .instructions = gen_scope.instructions.items }); | ||
| 1236 | |||
| 1237 | // The result location guarantees the type coercion. | ||
| 1238 | const analyzed_init_inst = init_inst.analyzed_inst.?; | ||
| 1239 | // The is_comptime in the Scope.Block guarantees the result is comptime-known. | ||
| 1240 | const val = analyzed_init_inst.value().?; | ||
| 1241 | |||
| 1242 | const ty = try analyzed_init_inst.ty.copy(block_scope.arena); | ||
| 1243 | break :vi .{ | ||
| 1244 | .ty = ty, | ||
| 1245 | .val = try val.copy(block_scope.arena), | ||
| 1246 | }; | ||
| 1247 | } else if (!is_extern) { | ||
| 1248 | return self.failTok(&block_scope.base, var_decl.firstToken(), "variables must be initialized", .{}); | ||
| 1249 | } else if (var_decl.getTypeNode()) |type_node| vi: { | ||
| 1250 | // Temporary arena for the zir instructions. | ||
| 1251 | var type_scope_arena = std.heap.ArenaAllocator.init(self.gpa); | ||
| 1252 | defer type_scope_arena.deinit(); | ||
| 1253 | var type_scope: Scope.GenZIR = .{ | ||
| 1254 | .decl = decl, | ||
| 1255 | .arena = &type_scope_arena.allocator, | ||
| 1256 | .parent = decl.scope, | ||
| 1257 | }; | ||
| 1258 | defer type_scope.instructions.deinit(self.gpa); | ||
| 1259 | |||
| 1260 | const src = tree.token_locs[type_node.firstToken()].start; | ||
| 1261 | const type_type = try astgen.addZIRInstConst(self, &type_scope.base, src, .{ | ||
| 1262 | .ty = Type.initTag(.type), | ||
| 1263 | .val = Value.initTag(.type_type), | ||
| 1264 | }); | ||
| 1265 | const var_type = try astgen.expr(self, &type_scope.base, .{ .ty = type_type }, type_node); | ||
| 1266 | const ty = try zir_sema.analyzeBodyValueAsType(self, &block_scope, var_type, .{ | ||
| 1267 | .instructions = type_scope.instructions.items, | ||
| 1268 | }); | ||
| 1269 | break :vi .{ | ||
| 1270 | .ty = ty, | ||
| 1271 | .val = null, | ||
| 1272 | }; | ||
| 1273 | } else { | ||
| 1274 | return self.failTok(&block_scope.base, var_decl.firstToken(), "unable to infer variable type", .{}); | ||
| 1275 | }; | ||
| 1276 | |||
| 1277 | if (is_mutable and !var_info.ty.isValidVarType(is_extern)) { | ||
| 1278 | return self.failTok(&block_scope.base, var_decl.firstToken(), "variable of type '{}' must be const", .{var_info.ty}); | ||
| 1279 | } | ||
| 1280 | |||
| 1281 | var type_changed = true; | ||
| 1282 | if (decl.typedValueManaged()) |tvm| { | ||
| 1283 | type_changed = !tvm.typed_value.ty.eql(var_info.ty); | ||
| 1284 | |||
| 1285 | tvm.deinit(self.gpa); | ||
| 1286 | } | ||
| 1287 | |||
| 1288 | const new_variable = try decl_arena.allocator.create(Var); | ||
| 1289 | const var_payload = try decl_arena.allocator.create(Value.Payload.Variable); | ||
| 1290 | new_variable.* = .{ | ||
| 1291 | .owner_decl = decl, | ||
| 1292 | .init = var_info.val orelse undefined, | ||
| 1293 | .is_extern = is_extern, | ||
| 1294 | .is_mutable = is_mutable, | ||
| 1295 | .is_threadlocal = is_threadlocal, | ||
| 1296 | }; | ||
| 1297 | var_payload.* = .{ .variable = new_variable }; | ||
| 1298 | |||
| 1299 | decl_arena_state.* = decl_arena.state; | ||
| 1300 | decl.typed_value = .{ | ||
| 1301 | .most_recent = .{ | ||
| 1302 | .typed_value = .{ | ||
| 1303 | .ty = var_info.ty, | ||
| 1304 | .val = Value.initPayload(&var_payload.base), | ||
| 1305 | }, | ||
| 1306 | .arena = decl_arena_state, | ||
| 1307 | }, | ||
| 1308 | }; | ||
| 1309 | decl.analysis = .complete; | ||
| 1310 | decl.generation = self.generation; | ||
| 1311 | |||
| 1312 | if (var_decl.getExternExportToken()) |maybe_export_token| { | ||
| 1313 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { | ||
| 1314 | const export_src = tree.token_locs[maybe_export_token].start; | ||
| 1315 | const name_loc = tree.token_locs[var_decl.name_token]; | ||
| 1316 | const name = tree.tokenSliceLoc(name_loc); | ||
| 1317 | // The scope needs to have the decl in it. | ||
| 1318 | try self.analyzeExport(&block_scope.base, export_src, name, decl); | ||
| 1319 | } | ||
| 1320 | } | ||
| 1321 | return type_changed; | ||
| 1322 | }, | ||
| 1323 | .Comptime => { | ||
| 1324 | const comptime_decl = @fieldParentPtr(ast.Node.Comptime, "base", ast_node); | ||
| 1325 | |||
| 1326 | decl.analysis = .in_progress; | ||
| 1327 | |||
| 1328 | // A comptime decl does not store any value so we can just deinit this arena after analysis is done. | ||
| 1329 | var analysis_arena = std.heap.ArenaAllocator.init(self.gpa); | ||
| 1330 | defer analysis_arena.deinit(); | ||
| 1331 | var gen_scope: Scope.GenZIR = .{ | ||
| 1332 | .decl = decl, | ||
| 1333 | .arena = &analysis_arena.allocator, | ||
| 1334 | .parent = decl.scope, | ||
| 1335 | }; | ||
| 1336 | defer gen_scope.instructions.deinit(self.gpa); | ||
| 1337 | |||
| 1338 | _ = try astgen.comptimeExpr(self, &gen_scope.base, .none, comptime_decl.expr); | ||
| 1339 | |||
| 1340 | var block_scope: Scope.Block = .{ | ||
| 1341 | .parent = null, | ||
| 1342 | .func = null, | ||
| 1343 | .decl = decl, | ||
| 1344 | .instructions = .{}, | ||
| 1345 | .arena = &analysis_arena.allocator, | ||
| 1346 | .is_comptime = true, | ||
| 1347 | }; | ||
| 1348 | defer block_scope.instructions.deinit(self.gpa); | ||
| 1349 | |||
| 1350 | _ = try zir_sema.analyzeBody(self, &block_scope.base, .{ | ||
| 1351 | .instructions = gen_scope.instructions.items, | ||
| 1352 | }); | ||
| 1353 | |||
| 1354 | decl.analysis = .complete; | ||
| 1355 | decl.generation = self.generation; | ||
| 1356 | return true; | ||
| 1357 | }, | ||
| 1358 | .Use => @panic("TODO usingnamespace decl"), | ||
| 1359 | else => unreachable, | ||
| 1360 | } | ||
| 1361 | } | ||
| 1362 | |||
| 1363 | fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void { | ||
| 1364 | try depender.dependencies.ensureCapacity(self.gpa, depender.dependencies.items().len + 1); | ||
| 1365 | try dependee.dependants.ensureCapacity(self.gpa, dependee.dependants.items().len + 1); | ||
| 1366 | |||
| 1367 | depender.dependencies.putAssumeCapacity(dependee, {}); | ||
| 1368 | dependee.dependants.putAssumeCapacity(depender, {}); | ||
| 1369 | } | ||
| 1370 | |||
| 1371 | fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module { | ||
| 1372 | switch (root_scope.status) { | ||
| 1373 | .never_loaded, .unloaded_success => { | ||
| 1374 | try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1); | ||
| 1375 | |||
| 1376 | const source = try root_scope.getSource(self); | ||
| 1377 | |||
| 1378 | var keep_zir_module = false; | ||
| 1379 | const zir_module = try self.gpa.create(zir.Module); | ||
| 1380 | defer if (!keep_zir_module) self.gpa.destroy(zir_module); | ||
| 1381 | |||
| 1382 | zir_module.* = try zir.parse(self.gpa, source); | ||
| 1383 | defer if (!keep_zir_module) zir_module.deinit(self.gpa); | ||
| 1384 | |||
| 1385 | if (zir_module.error_msg) |src_err_msg| { | ||
| 1386 | self.failed_files.putAssumeCapacityNoClobber( | ||
| 1387 | &root_scope.base, | ||
| 1388 | try Compilation.ErrorMsg.create(self.gpa, src_err_msg.byte_offset, "{}", .{src_err_msg.msg}), | ||
| 1389 | ); | ||
| 1390 | root_scope.status = .unloaded_parse_failure; | ||
| 1391 | return error.AnalysisFail; | ||
| 1392 | } | ||
| 1393 | |||
| 1394 | root_scope.status = .loaded_success; | ||
| 1395 | root_scope.contents = .{ .module = zir_module }; | ||
| 1396 | keep_zir_module = true; | ||
| 1397 | |||
| 1398 | return zir_module; | ||
| 1399 | }, | ||
| 1400 | |||
| 1401 | .unloaded_parse_failure, | ||
| 1402 | .unloaded_sema_failure, | ||
| 1403 | => return error.AnalysisFail, | ||
| 1404 | |||
| 1405 | .loaded_success, .loaded_sema_failure => return root_scope.contents.module, | ||
| 1406 | } | ||
| 1407 | } | ||
| 1408 | |||
| 1409 | fn getAstTree(self: *Module, container_scope: *Scope.Container) !*ast.Tree { | ||
| 1410 | const tracy = trace(@src()); | ||
| 1411 | defer tracy.end(); | ||
| 1412 | |||
| 1413 | const root_scope = container_scope.file_scope; | ||
| 1414 | |||
| 1415 | switch (root_scope.status) { | ||
| 1416 | .never_loaded, .unloaded_success => { | ||
| 1417 | try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1); | ||
| 1418 | |||
| 1419 | const source = try root_scope.getSource(self); | ||
| 1420 | |||
| 1421 | var keep_tree = false; | ||
| 1422 | const tree = try std.zig.parse(self.gpa, source); | ||
| 1423 | defer if (!keep_tree) tree.deinit(); | ||
| 1424 | |||
| 1425 | if (tree.errors.len != 0) { | ||
| 1426 | const parse_err = tree.errors[0]; | ||
| 1427 | |||
| 1428 | var msg = std.ArrayList(u8).init(self.gpa); | ||
| 1429 | defer msg.deinit(); | ||
| 1430 | |||
| 1431 | try parse_err.render(tree.token_ids, msg.outStream()); | ||
| 1432 | const err_msg = try self.gpa.create(Compilation.ErrorMsg); | ||
| 1433 | err_msg.* = .{ | ||
| 1434 | .msg = msg.toOwnedSlice(), | ||
| 1435 | .byte_offset = tree.token_locs[parse_err.loc()].start, | ||
| 1436 | }; | ||
| 1437 | |||
| 1438 | self.failed_files.putAssumeCapacityNoClobber(&root_scope.base, err_msg); | ||
| 1439 | root_scope.status = .unloaded_parse_failure; | ||
| 1440 | return error.AnalysisFail; | ||
| 1441 | } | ||
| 1442 | |||
| 1443 | root_scope.status = .loaded_success; | ||
| 1444 | root_scope.contents = .{ .tree = tree }; | ||
| 1445 | keep_tree = true; | ||
| 1446 | |||
| 1447 | return tree; | ||
| 1448 | }, | ||
| 1449 | |||
| 1450 | .unloaded_parse_failure => return error.AnalysisFail, | ||
| 1451 | |||
| 1452 | .loaded_success => return root_scope.contents.tree, | ||
| 1453 | } | ||
| 1454 | } | ||
| 1455 | |||
| 1456 | pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void { | ||
| 1457 | const tracy = trace(@src()); | ||
| 1458 | defer tracy.end(); | ||
| 1459 | |||
| 1460 | // We may be analyzing it for the first time, or this may be | ||
| 1461 | // an incremental update. This code handles both cases. | ||
| 1462 | const tree = try self.getAstTree(container_scope); | ||
| 1463 | const decls = tree.root_node.decls(); | ||
| 1464 | |||
| 1465 | try self.comp.work_queue.ensureUnusedCapacity(decls.len); | ||
| 1466 | try container_scope.decls.ensureCapacity(self.gpa, decls.len); | ||
| 1467 | |||
| 1468 | // Keep track of the decls that we expect to see in this file so that | ||
| 1469 | // we know which ones have been deleted. | ||
| 1470 | var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa); | ||
| 1471 | defer deleted_decls.deinit(); | ||
| 1472 | try deleted_decls.ensureCapacity(container_scope.decls.items().len); | ||
| 1473 | for (container_scope.decls.items()) |entry| { | ||
| 1474 | deleted_decls.putAssumeCapacityNoClobber(entry.key, {}); | ||
| 1475 | } | ||
| 1476 | |||
| 1477 | for (decls) |src_decl, decl_i| { | ||
| 1478 | if (src_decl.cast(ast.Node.FnProto)) |fn_proto| { | ||
| 1479 | // We will create a Decl for it regardless of analysis status. | ||
| 1480 | const name_tok = fn_proto.getNameToken() orelse { | ||
| 1481 | @panic("TODO missing function name"); | ||
| 1482 | }; | ||
| 1483 | |||
| 1484 | const name_loc = tree.token_locs[name_tok]; | ||
| 1485 | const name = tree.tokenSliceLoc(name_loc); | ||
| 1486 | const name_hash = container_scope.fullyQualifiedNameHash(name); | ||
| 1487 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl)); | ||
| 1488 | if (self.decl_table.get(name_hash)) |decl| { | ||
| 1489 | // Update the AST Node index of the decl, even if its contents are unchanged, it may | ||
| 1490 | // have been re-ordered. | ||
| 1491 | decl.src_index = decl_i; | ||
| 1492 | if (deleted_decls.remove(decl) == null) { | ||
| 1493 | decl.analysis = .sema_failure; | ||
| 1494 | const err_msg = try Compilation.ErrorMsg.create(self.gpa, tree.token_locs[name_tok].start, "redefinition of '{}'", .{decl.name}); | ||
| 1495 | errdefer err_msg.destroy(self.gpa); | ||
| 1496 | try self.failed_decls.putNoClobber(self.gpa, decl, err_msg); | ||
| 1497 | } else { | ||
| 1498 | if (!srcHashEql(decl.contents_hash, contents_hash)) { | ||
| 1499 | try self.markOutdatedDecl(decl); | ||
| 1500 | decl.contents_hash = contents_hash; | ||
| 1501 | } else switch (self.comp.bin_file.tag) { | ||
| 1502 | .coff => { | ||
| 1503 | // TODO Implement for COFF | ||
| 1504 | }, | ||
| 1505 | .elf => if (decl.fn_link.elf.len != 0) { | ||
| 1506 | // TODO Look into detecting when this would be unnecessary by storing enough state | ||
| 1507 | // in `Decl` to notice that the line number did not change. | ||
| 1508 | self.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); | ||
| 1509 | }, | ||
| 1510 | .macho => { | ||
| 1511 | // TODO Implement for MachO | ||
| 1512 | }, | ||
| 1513 | .c, .wasm => {}, | ||
| 1514 | } | ||
| 1515 | } | ||
| 1516 | } else { | ||
| 1517 | const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); | ||
| 1518 | container_scope.decls.putAssumeCapacity(new_decl, {}); | ||
| 1519 | if (fn_proto.getExternExportInlineToken()) |maybe_export_token| { | ||
| 1520 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { | ||
| 1521 | self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); | ||
| 1522 | } | ||
| 1523 | } | ||
| 1524 | } | ||
| 1525 | } else if (src_decl.castTag(.VarDecl)) |var_decl| { | ||
| 1526 | const name_loc = tree.token_locs[var_decl.name_token]; | ||
| 1527 | const name = tree.tokenSliceLoc(name_loc); | ||
| 1528 | const name_hash = container_scope.fullyQualifiedNameHash(name); | ||
| 1529 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl)); | ||
| 1530 | if (self.decl_table.get(name_hash)) |decl| { | ||
| 1531 | // Update the AST Node index of the decl, even if its contents are unchanged, it may | ||
| 1532 | // have been re-ordered. | ||
| 1533 | decl.src_index = decl_i; | ||
| 1534 | if (deleted_decls.remove(decl) == null) { | ||
| 1535 | decl.analysis = .sema_failure; | ||
| 1536 | const err_msg = try Compilation.ErrorMsg.create(self.gpa, name_loc.start, "redefinition of '{}'", .{decl.name}); | ||
| 1537 | errdefer err_msg.destroy(self.gpa); | ||
| 1538 | try self.failed_decls.putNoClobber(self.gpa, decl, err_msg); | ||
| 1539 | } else if (!srcHashEql(decl.contents_hash, contents_hash)) { | ||
| 1540 | try self.markOutdatedDecl(decl); | ||
| 1541 | decl.contents_hash = contents_hash; | ||
| 1542 | } | ||
| 1543 | } else { | ||
| 1544 | const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); | ||
| 1545 | container_scope.decls.putAssumeCapacity(new_decl, {}); | ||
| 1546 | if (var_decl.getExternExportToken()) |maybe_export_token| { | ||
| 1547 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { | ||
| 1548 | self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); | ||
| 1549 | } | ||
| 1550 | } | ||
| 1551 | } | ||
| 1552 | } else if (src_decl.castTag(.Comptime)) |comptime_node| { | ||
| 1553 | const name_index = self.getNextAnonNameIndex(); | ||
| 1554 | const name = try std.fmt.allocPrint(self.gpa, "__comptime_{}", .{name_index}); | ||
| 1555 | defer self.gpa.free(name); | ||
| 1556 | |||
| 1557 | const name_hash = container_scope.fullyQualifiedNameHash(name); | ||
| 1558 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl)); | ||
| 1559 | |||
| 1560 | const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); | ||
| 1561 | container_scope.decls.putAssumeCapacity(new_decl, {}); | ||
| 1562 | self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); | ||
| 1563 | } else if (src_decl.castTag(.ContainerField)) |container_field| { | ||
| 1564 | log.err("TODO: analyze container field", .{}); | ||
| 1565 | } else if (src_decl.castTag(.TestDecl)) |test_decl| { | ||
| 1566 | log.err("TODO: analyze test decl", .{}); | ||
| 1567 | } else if (src_decl.castTag(.Use)) |use_decl| { | ||
| 1568 | log.err("TODO: analyze usingnamespace decl", .{}); | ||
| 1569 | } else { | ||
| 1570 | unreachable; | ||
| 1571 | } | ||
| 1572 | } | ||
| 1573 | // Handle explicitly deleted decls from the source code. Not to be confused | ||
| 1574 | // with when we delete decls because they are no longer referenced. | ||
| 1575 | for (deleted_decls.items()) |entry| { | ||
| 1576 | log.debug("noticed '{}' deleted from source\n", .{entry.key.name}); | ||
| 1577 | try self.deleteDecl(entry.key); | ||
| 1578 | } | ||
| 1579 | } | ||
| 1580 | |||
| 1581 | pub fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void { | ||
| 1582 | // We may be analyzing it for the first time, or this may be | ||
| 1583 | // an incremental update. This code handles both cases. | ||
| 1584 | const src_module = try self.getSrcModule(root_scope); | ||
| 1585 | |||
| 1586 | try self.comp.work_queue.ensureUnusedCapacity(src_module.decls.len); | ||
| 1587 | try root_scope.decls.ensureCapacity(self.gpa, src_module.decls.len); | ||
| 1588 | |||
| 1589 | var exports_to_resolve = std.ArrayList(*zir.Decl).init(self.gpa); | ||
| 1590 | defer exports_to_resolve.deinit(); | ||
| 1591 | |||
| 1592 | // Keep track of the decls that we expect to see in this file so that | ||
| 1593 | // we know which ones have been deleted. | ||
| 1594 | var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa); | ||
| 1595 | defer deleted_decls.deinit(); | ||
| 1596 | try deleted_decls.ensureCapacity(self.decl_table.items().len); | ||
| 1597 | for (self.decl_table.items()) |entry| { | ||
| 1598 | deleted_decls.putAssumeCapacityNoClobber(entry.value, {}); | ||
| 1599 | } | ||
| 1600 | |||
| 1601 | for (src_module.decls) |src_decl, decl_i| { | ||
| 1602 | const name_hash = root_scope.fullyQualifiedNameHash(src_decl.name); | ||
| 1603 | if (self.decl_table.get(name_hash)) |decl| { | ||
| 1604 | deleted_decls.removeAssertDiscard(decl); | ||
| 1605 | if (!srcHashEql(src_decl.contents_hash, decl.contents_hash)) { | ||
| 1606 | try self.markOutdatedDecl(decl); | ||
| 1607 | decl.contents_hash = src_decl.contents_hash; | ||
| 1608 | } | ||
| 1609 | } else { | ||
| 1610 | const new_decl = try self.createNewDecl( | ||
| 1611 | &root_scope.base, | ||
| 1612 | src_decl.name, | ||
| 1613 | decl_i, | ||
| 1614 | name_hash, | ||
| 1615 | src_decl.contents_hash, | ||
| 1616 | ); | ||
| 1617 | root_scope.decls.appendAssumeCapacity(new_decl); | ||
| 1618 | if (src_decl.inst.cast(zir.Inst.Export)) |export_inst| { | ||
| 1619 | try exports_to_resolve.append(src_decl); | ||
| 1620 | } | ||
| 1621 | } | ||
| 1622 | } | ||
| 1623 | for (exports_to_resolve.items) |export_decl| { | ||
| 1624 | _ = try zir_sema.resolveZirDecl(self, &root_scope.base, export_decl); | ||
| 1625 | } | ||
| 1626 | // Handle explicitly deleted decls from the source code. Not to be confused | ||
| 1627 | // with when we delete decls because they are no longer referenced. | ||
| 1628 | for (deleted_decls.items()) |entry| { | ||
| 1629 | log.debug("noticed '{}' deleted from source\n", .{entry.key.name}); | ||
| 1630 | try self.deleteDecl(entry.key); | ||
| 1631 | } | ||
| 1632 | } | ||
| 1633 | |||
| 1634 | pub fn deleteDecl(self: *Module, decl: *Decl) !void { | ||
| 1635 | try self.deletion_set.ensureCapacity(self.gpa, self.deletion_set.items.len + decl.dependencies.items().len); | ||
| 1636 | |||
| 1637 | // Remove from the namespace it resides in. In the case of an anonymous Decl it will | ||
| 1638 | // not be present in the set, and this does nothing. | ||
| 1639 | decl.scope.removeDecl(decl); | ||
| 1640 | |||
| 1641 | log.debug("deleting decl '{}'\n", .{decl.name}); | ||
| 1642 | const name_hash = decl.fullyQualifiedNameHash(); | ||
| 1643 | self.decl_table.removeAssertDiscard(name_hash); | ||
| 1644 | // Remove itself from its dependencies, because we are about to destroy the decl pointer. | ||
| 1645 | for (decl.dependencies.items()) |entry| { | ||
| 1646 | const dep = entry.key; | ||
| 1647 | dep.removeDependant(decl); | ||
| 1648 | if (dep.dependants.items().len == 0 and !dep.deletion_flag) { | ||
| 1649 | // We don't recursively perform a deletion here, because during the update, | ||
| 1650 | // another reference to it may turn up. | ||
| 1651 | dep.deletion_flag = true; | ||
| 1652 | self.deletion_set.appendAssumeCapacity(dep); | ||
| 1653 | } | ||
| 1654 | } | ||
| 1655 | // Anything that depends on this deleted decl certainly needs to be re-analyzed. | ||
| 1656 | for (decl.dependants.items()) |entry| { | ||
| 1657 | const dep = entry.key; | ||
| 1658 | dep.removeDependency(decl); | ||
| 1659 | if (dep.analysis != .outdated) { | ||
| 1660 | // TODO Move this failure possibility to the top of the function. | ||
| 1661 | try self.markOutdatedDecl(dep); | ||
| 1662 | } | ||
| 1663 | } | ||
| 1664 | if (self.failed_decls.remove(decl)) |entry| { | ||
| 1665 | entry.value.destroy(self.gpa); | ||
| 1666 | } | ||
| 1667 | self.deleteDeclExports(decl); | ||
| 1668 | self.comp.bin_file.freeDecl(decl); | ||
| 1669 | decl.destroy(self.gpa); | ||
| 1670 | } | ||
| 1671 | |||
| 1672 | /// Delete all the Export objects that are caused by this Decl. Re-analysis of | ||
| 1673 | /// this Decl will cause them to be re-created (or not). | ||
| 1674 | fn deleteDeclExports(self: *Module, decl: *Decl) void { | ||
| 1675 | const kv = self.export_owners.remove(decl) orelse return; | ||
| 1676 | |||
| 1677 | for (kv.value) |exp| { | ||
| 1678 | if (self.decl_exports.getEntry(exp.exported_decl)) |decl_exports_kv| { | ||
| 1679 | // Remove exports with owner_decl matching the regenerating decl. | ||
| 1680 | const list = decl_exports_kv.value; | ||
| 1681 | var i: usize = 0; | ||
| 1682 | var new_len = list.len; | ||
| 1683 | while (i < new_len) { | ||
| 1684 | if (list[i].owner_decl == decl) { | ||
| 1685 | mem.copyBackwards(*Export, list[i..], list[i + 1 .. new_len]); | ||
| 1686 | new_len -= 1; | ||
| 1687 | } else { | ||
| 1688 | i += 1; | ||
| 1689 | } | ||
| 1690 | } | ||
| 1691 | decl_exports_kv.value = self.gpa.shrink(list, new_len); | ||
| 1692 | if (new_len == 0) { | ||
| 1693 | self.decl_exports.removeAssertDiscard(exp.exported_decl); | ||
| 1694 | } | ||
| 1695 | } | ||
| 1696 | if (self.comp.bin_file.cast(link.File.Elf)) |elf| { | ||
| 1697 | elf.deleteExport(exp.link); | ||
| 1698 | } | ||
| 1699 | if (self.failed_exports.remove(exp)) |entry| { | ||
| 1700 | entry.value.destroy(self.gpa); | ||
| 1701 | } | ||
| 1702 | _ = self.symbol_exports.remove(exp.options.name); | ||
| 1703 | self.gpa.free(exp.options.name); | ||
| 1704 | self.gpa.destroy(exp); | ||
| 1705 | } | ||
| 1706 | self.gpa.free(kv.value); | ||
| 1707 | } | ||
| 1708 | |||
| 1709 | pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { | ||
| 1710 | const tracy = trace(@src()); | ||
| 1711 | defer tracy.end(); | ||
| 1712 | |||
| 1713 | // Use the Decl's arena for function memory. | ||
| 1714 | var arena = decl.typed_value.most_recent.arena.?.promote(self.gpa); | ||
| 1715 | defer decl.typed_value.most_recent.arena.?.* = arena.state; | ||
| 1716 | var inner_block: Scope.Block = .{ | ||
| 1717 | .parent = null, | ||
| 1718 | .func = func, | ||
| 1719 | .decl = decl, | ||
| 1720 | .instructions = .{}, | ||
| 1721 | .arena = &arena.allocator, | ||
| 1722 | .is_comptime = false, | ||
| 1723 | }; | ||
| 1724 | defer inner_block.instructions.deinit(self.gpa); | ||
| 1725 | |||
| 1726 | const fn_zir = func.analysis.queued; | ||
| 1727 | defer fn_zir.arena.promote(self.gpa).deinit(); | ||
| 1728 | func.analysis = .{ .in_progress = {} }; | ||
| 1729 | log.debug("set {} to in_progress\n", .{decl.name}); | ||
| 1730 | |||
| 1731 | try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body); | ||
| 1732 | |||
| 1733 | const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items); | ||
| 1734 | func.analysis = .{ .success = .{ .instructions = instructions } }; | ||
| 1735 | log.debug("set {} to success\n", .{decl.name}); | ||
| 1736 | } | ||
| 1737 | |||
| 1738 | fn markOutdatedDecl(self: *Module, decl: *Decl) !void { | ||
| 1739 | log.debug("mark {} outdated\n", .{decl.name}); | ||
| 1740 | try self.comp.work_queue.writeItem(.{ .analyze_decl = decl }); | ||
| 1741 | if (self.failed_decls.remove(decl)) |entry| { | ||
| 1742 | entry.value.destroy(self.gpa); | ||
| 1743 | } | ||
| 1744 | decl.analysis = .outdated; | ||
| 1745 | } | ||
| 1746 | |||
| 1747 | fn allocateNewDecl( | ||
| 1748 | self: *Module, | ||
| 1749 | scope: *Scope, | ||
| 1750 | src_index: usize, | ||
| 1751 | contents_hash: std.zig.SrcHash, | ||
| 1752 | ) !*Decl { | ||
| 1753 | const new_decl = try self.gpa.create(Decl); | ||
| 1754 | new_decl.* = .{ | ||
| 1755 | .name = "", | ||
| 1756 | .scope = scope.namespace(), | ||
| 1757 | .src_index = src_index, | ||
| 1758 | .typed_value = .{ .never_succeeded = {} }, | ||
| 1759 | .analysis = .unreferenced, | ||
| 1760 | .deletion_flag = false, | ||
| 1761 | .contents_hash = contents_hash, | ||
| 1762 | .link = switch (self.comp.bin_file.tag) { | ||
| 1763 | .coff => .{ .coff = link.File.Coff.TextBlock.empty }, | ||
| 1764 | .elf => .{ .elf = link.File.Elf.TextBlock.empty }, | ||
| 1765 | .macho => .{ .macho = link.File.MachO.TextBlock.empty }, | ||
| 1766 | .c => .{ .c = {} }, | ||
| 1767 | .wasm => .{ .wasm = {} }, | ||
| 1768 | }, | ||
| 1769 | .fn_link = switch (self.comp.bin_file.tag) { | ||
| 1770 | .coff => .{ .coff = {} }, | ||
| 1771 | .elf => .{ .elf = link.File.Elf.SrcFn.empty }, | ||
| 1772 | .macho => .{ .macho = link.File.MachO.SrcFn.empty }, | ||
| 1773 | .c => .{ .c = {} }, | ||
| 1774 | .wasm => .{ .wasm = null }, | ||
| 1775 | }, | ||
| 1776 | .generation = 0, | ||
| 1777 | .is_pub = false, | ||
| 1778 | }; | ||
| 1779 | return new_decl; | ||
| 1780 | } | ||
| 1781 | |||
| 1782 | fn createNewDecl( | ||
| 1783 | self: *Module, | ||
| 1784 | scope: *Scope, | ||
| 1785 | decl_name: []const u8, | ||
| 1786 | src_index: usize, | ||
| 1787 | name_hash: Scope.NameHash, | ||
| 1788 | contents_hash: std.zig.SrcHash, | ||
| 1789 | ) !*Decl { | ||
| 1790 | try self.decl_table.ensureCapacity(self.gpa, self.decl_table.items().len + 1); | ||
| 1791 | const new_decl = try self.allocateNewDecl(scope, src_index, contents_hash); | ||
| 1792 | errdefer self.gpa.destroy(new_decl); | ||
| 1793 | new_decl.name = try mem.dupeZ(self.gpa, u8, decl_name); | ||
| 1794 | self.decl_table.putAssumeCapacityNoClobber(name_hash, new_decl); | ||
| 1795 | return new_decl; | ||
| 1796 | } | ||
| 1797 | |||
| 1798 | /// Get error value for error tag `name`. | ||
| 1799 | pub fn getErrorValue(self: *Module, name: []const u8) !std.StringHashMapUnmanaged(u16).Entry { | ||
| 1800 | const gop = try self.global_error_set.getOrPut(self.gpa, name); | ||
| 1801 | if (gop.found_existing) | ||
| 1802 | return gop.entry.*; | ||
| 1803 | errdefer self.global_error_set.removeAssertDiscard(name); | ||
| 1804 | |||
| 1805 | gop.entry.key = try self.gpa.dupe(u8, name); | ||
| 1806 | gop.entry.value = @intCast(u16, self.global_error_set.count() - 1); | ||
| 1807 | return gop.entry.*; | ||
| 1808 | } | ||
| 1809 | |||
| 1810 | pub fn requireFunctionBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block { | ||
| 1811 | return scope.cast(Scope.Block) orelse | ||
| 1812 | return self.fail(scope, src, "instruction illegal outside function body", .{}); | ||
| 1813 | } | ||
| 1814 | |||
| 1815 | pub fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block { | ||
| 1816 | const block = try self.requireFunctionBlock(scope, src); | ||
| 1817 | if (block.is_comptime) { | ||
| 1818 | return self.fail(scope, src, "unable to resolve comptime value", .{}); | ||
| 1819 | } | ||
| 1820 | return block; | ||
| 1821 | } | ||
| 1822 | |||
| 1823 | pub fn resolveConstValue(self: *Module, scope: *Scope, base: *Inst) !Value { | ||
| 1824 | return (try self.resolveDefinedValue(scope, base)) orelse | ||
| 1825 | return self.fail(scope, base.src, "unable to resolve comptime value", .{}); | ||
| 1826 | } | ||
| 1827 | |||
| 1828 | pub fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value { | ||
| 1829 | if (base.value()) |val| { | ||
| 1830 | if (val.isUndef()) { | ||
| 1831 | return self.fail(scope, base.src, "use of undefined value here causes undefined behavior", .{}); | ||
| 1832 | } | ||
| 1833 | return val; | ||
| 1834 | } | ||
| 1835 | return null; | ||
| 1836 | } | ||
| 1837 | |||
| 1838 | pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, borrowed_symbol_name: []const u8, exported_decl: *Decl) !void { | ||
| 1839 | try self.ensureDeclAnalyzed(exported_decl); | ||
| 1840 | const typed_value = exported_decl.typed_value.most_recent.typed_value; | ||
| 1841 | switch (typed_value.ty.zigTypeTag()) { | ||
| 1842 | .Fn => {}, | ||
| 1843 | else => return self.fail(scope, src, "unable to export type '{}'", .{typed_value.ty}), | ||
| 1844 | } | ||
| 1845 | |||
| 1846 | try self.decl_exports.ensureCapacity(self.gpa, self.decl_exports.items().len + 1); | ||
| 1847 | try self.export_owners.ensureCapacity(self.gpa, self.export_owners.items().len + 1); | ||
| 1848 | |||
| 1849 | const new_export = try self.gpa.create(Export); | ||
| 1850 | errdefer self.gpa.destroy(new_export); | ||
| 1851 | |||
| 1852 | const symbol_name = try self.gpa.dupe(u8, borrowed_symbol_name); | ||
| 1853 | errdefer self.gpa.free(symbol_name); | ||
| 1854 | |||
| 1855 | const owner_decl = scope.decl().?; | ||
| 1856 | |||
| 1857 | new_export.* = .{ | ||
| 1858 | .options = .{ .name = symbol_name }, | ||
| 1859 | .src = src, | ||
| 1860 | .link = .{}, | ||
| 1861 | .owner_decl = owner_decl, | ||
| 1862 | .exported_decl = exported_decl, | ||
| 1863 | .status = .in_progress, | ||
| 1864 | }; | ||
| 1865 | |||
| 1866 | // Add to export_owners table. | ||
| 1867 | const eo_gop = self.export_owners.getOrPutAssumeCapacity(owner_decl); | ||
| 1868 | if (!eo_gop.found_existing) { | ||
| 1869 | eo_gop.entry.value = &[0]*Export{}; | ||
| 1870 | } | ||
| 1871 | eo_gop.entry.value = try self.gpa.realloc(eo_gop.entry.value, eo_gop.entry.value.len + 1); | ||
| 1872 | eo_gop.entry.value[eo_gop.entry.value.len - 1] = new_export; | ||
| 1873 | errdefer eo_gop.entry.value = self.gpa.shrink(eo_gop.entry.value, eo_gop.entry.value.len - 1); | ||
| 1874 | |||
| 1875 | // Add to exported_decl table. | ||
| 1876 | const de_gop = self.decl_exports.getOrPutAssumeCapacity(exported_decl); | ||
| 1877 | if (!de_gop.found_existing) { | ||
| 1878 | de_gop.entry.value = &[0]*Export{}; | ||
| 1879 | } | ||
| 1880 | de_gop.entry.value = try self.gpa.realloc(de_gop.entry.value, de_gop.entry.value.len + 1); | ||
| 1881 | de_gop.entry.value[de_gop.entry.value.len - 1] = new_export; | ||
| 1882 | errdefer de_gop.entry.value = self.gpa.shrink(de_gop.entry.value, de_gop.entry.value.len - 1); | ||
| 1883 | |||
| 1884 | if (self.symbol_exports.get(symbol_name)) |_| { | ||
| 1885 | try self.failed_exports.ensureCapacity(self.gpa, self.failed_exports.items().len + 1); | ||
| 1886 | self.failed_exports.putAssumeCapacityNoClobber(new_export, try Compilation.ErrorMsg.create( | ||
| 1887 | self.gpa, | ||
| 1888 | src, | ||
| 1889 | "exported symbol collision: {}", | ||
| 1890 | .{symbol_name}, | ||
| 1891 | )); | ||
| 1892 | // TODO: add a note | ||
| 1893 | new_export.status = .failed; | ||
| 1894 | return; | ||
| 1895 | } | ||
| 1896 | |||
| 1897 | try self.symbol_exports.putNoClobber(self.gpa, symbol_name, new_export); | ||
| 1898 | self.comp.bin_file.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) { | ||
| 1899 | error.OutOfMemory => return error.OutOfMemory, | ||
| 1900 | else => { | ||
| 1901 | try self.failed_exports.ensureCapacity(self.gpa, self.failed_exports.items().len + 1); | ||
| 1902 | self.failed_exports.putAssumeCapacityNoClobber(new_export, try Compilation.ErrorMsg.create( | ||
| 1903 | self.gpa, | ||
| 1904 | src, | ||
| 1905 | "unable to export: {}", | ||
| 1906 | .{@errorName(err)}, | ||
| 1907 | )); | ||
| 1908 | new_export.status = .failed_retryable; | ||
| 1909 | }, | ||
| 1910 | }; | ||
| 1911 | } | ||
| 1912 | |||
| 1913 | pub fn addNoOp( | ||
| 1914 | self: *Module, | ||
| 1915 | block: *Scope.Block, | ||
| 1916 | src: usize, | ||
| 1917 | ty: Type, | ||
| 1918 | comptime tag: Inst.Tag, | ||
| 1919 | ) !*Inst { | ||
| 1920 | const inst = try block.arena.create(tag.Type()); | ||
| 1921 | inst.* = .{ | ||
| 1922 | .base = .{ | ||
| 1923 | .tag = tag, | ||
| 1924 | .ty = ty, | ||
| 1925 | .src = src, | ||
| 1926 | }, | ||
| 1927 | }; | ||
| 1928 | try block.instructions.append(self.gpa, &inst.base); | ||
| 1929 | return &inst.base; | ||
| 1930 | } | ||
| 1931 | |||
| 1932 | pub fn addUnOp( | ||
| 1933 | self: *Module, | ||
| 1934 | block: *Scope.Block, | ||
| 1935 | src: usize, | ||
| 1936 | ty: Type, | ||
| 1937 | tag: Inst.Tag, | ||
| 1938 | operand: *Inst, | ||
| 1939 | ) !*Inst { | ||
| 1940 | const inst = try block.arena.create(Inst.UnOp); | ||
| 1941 | inst.* = .{ | ||
| 1942 | .base = .{ | ||
| 1943 | .tag = tag, | ||
| 1944 | .ty = ty, | ||
| 1945 | .src = src, | ||
| 1946 | }, | ||
| 1947 | .operand = operand, | ||
| 1948 | }; | ||
| 1949 | try block.instructions.append(self.gpa, &inst.base); | ||
| 1950 | return &inst.base; | ||
| 1951 | } | ||
| 1952 | |||
| 1953 | pub fn addBinOp( | ||
| 1954 | self: *Module, | ||
| 1955 | block: *Scope.Block, | ||
| 1956 | src: usize, | ||
| 1957 | ty: Type, | ||
| 1958 | tag: Inst.Tag, | ||
| 1959 | lhs: *Inst, | ||
| 1960 | rhs: *Inst, | ||
| 1961 | ) !*Inst { | ||
| 1962 | const inst = try block.arena.create(Inst.BinOp); | ||
| 1963 | inst.* = .{ | ||
| 1964 | .base = .{ | ||
| 1965 | .tag = tag, | ||
| 1966 | .ty = ty, | ||
| 1967 | .src = src, | ||
| 1968 | }, | ||
| 1969 | .lhs = lhs, | ||
| 1970 | .rhs = rhs, | ||
| 1971 | }; | ||
| 1972 | try block.instructions.append(self.gpa, &inst.base); | ||
| 1973 | return &inst.base; | ||
| 1974 | } | ||
| 1975 | |||
| 1976 | pub fn addArg(self: *Module, block: *Scope.Block, src: usize, ty: Type, name: [*:0]const u8) !*Inst { | ||
| 1977 | const inst = try block.arena.create(Inst.Arg); | ||
| 1978 | inst.* = .{ | ||
| 1979 | .base = .{ | ||
| 1980 | .tag = .arg, | ||
| 1981 | .ty = ty, | ||
| 1982 | .src = src, | ||
| 1983 | }, | ||
| 1984 | .name = name, | ||
| 1985 | }; | ||
| 1986 | try block.instructions.append(self.gpa, &inst.base); | ||
| 1987 | return &inst.base; | ||
| 1988 | } | ||
| 1989 | |||
| 1990 | pub fn addBr( | ||
| 1991 | self: *Module, | ||
| 1992 | scope_block: *Scope.Block, | ||
| 1993 | src: usize, | ||
| 1994 | target_block: *Inst.Block, | ||
| 1995 | operand: *Inst, | ||
| 1996 | ) !*Inst { | ||
| 1997 | const inst = try scope_block.arena.create(Inst.Br); | ||
| 1998 | inst.* = .{ | ||
| 1999 | .base = .{ | ||
| 2000 | .tag = .br, | ||
| 2001 | .ty = Type.initTag(.noreturn), | ||
| 2002 | .src = src, | ||
| 2003 | }, | ||
| 2004 | .operand = operand, | ||
| 2005 | .block = target_block, | ||
| 2006 | }; | ||
| 2007 | try scope_block.instructions.append(self.gpa, &inst.base); | ||
| 2008 | return &inst.base; | ||
| 2009 | } | ||
| 2010 | |||
| 2011 | pub fn addCondBr( | ||
| 2012 | self: *Module, | ||
| 2013 | block: *Scope.Block, | ||
| 2014 | src: usize, | ||
| 2015 | condition: *Inst, | ||
| 2016 | then_body: ir.Body, | ||
| 2017 | else_body: ir.Body, | ||
| 2018 | ) !*Inst { | ||
| 2019 | const inst = try block.arena.create(Inst.CondBr); | ||
| 2020 | inst.* = .{ | ||
| 2021 | .base = .{ | ||
| 2022 | .tag = .condbr, | ||
| 2023 | .ty = Type.initTag(.noreturn), | ||
| 2024 | .src = src, | ||
| 2025 | }, | ||
| 2026 | .condition = condition, | ||
| 2027 | .then_body = then_body, | ||
| 2028 | .else_body = else_body, | ||
| 2029 | }; | ||
| 2030 | try block.instructions.append(self.gpa, &inst.base); | ||
| 2031 | return &inst.base; | ||
| 2032 | } | ||
| 2033 | |||
| 2034 | pub fn addCall( | ||
| 2035 | self: *Module, | ||
| 2036 | block: *Scope.Block, | ||
| 2037 | src: usize, | ||
| 2038 | ty: Type, | ||
| 2039 | func: *Inst, | ||
| 2040 | args: []const *Inst, | ||
| 2041 | ) !*Inst { | ||
| 2042 | const inst = try block.arena.create(Inst.Call); | ||
| 2043 | inst.* = .{ | ||
| 2044 | .base = .{ | ||
| 2045 | .tag = .call, | ||
| 2046 | .ty = ty, | ||
| 2047 | .src = src, | ||
| 2048 | }, | ||
| 2049 | .func = func, | ||
| 2050 | .args = args, | ||
| 2051 | }; | ||
| 2052 | try block.instructions.append(self.gpa, &inst.base); | ||
| 2053 | return &inst.base; | ||
| 2054 | } | ||
| 2055 | |||
| 2056 | pub fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst { | ||
| 2057 | const const_inst = try scope.arena().create(Inst.Constant); | ||
| 2058 | const_inst.* = .{ | ||
| 2059 | .base = .{ | ||
| 2060 | .tag = Inst.Constant.base_tag, | ||
| 2061 | .ty = typed_value.ty, | ||
| 2062 | .src = src, | ||
| 2063 | }, | ||
| 2064 | .val = typed_value.val, | ||
| 2065 | }; | ||
| 2066 | return &const_inst.base; | ||
| 2067 | } | ||
| 2068 | |||
| 2069 | pub fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst { | ||
| 2070 | return self.constInst(scope, src, .{ | ||
| 2071 | .ty = Type.initTag(.type), | ||
| 2072 | .val = try ty.toValue(scope.arena()), | ||
| 2073 | }); | ||
| 2074 | } | ||
| 2075 | |||
| 2076 | pub fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst { | ||
| 2077 | return self.constInst(scope, src, .{ | ||
| 2078 | .ty = Type.initTag(.void), | ||
| 2079 | .val = Value.initTag(.void_value), | ||
| 2080 | }); | ||
| 2081 | } | ||
| 2082 | |||
| 2083 | pub fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst { | ||
| 2084 | return self.constInst(scope, src, .{ | ||
| 2085 | .ty = Type.initTag(.noreturn), | ||
| 2086 | .val = Value.initTag(.unreachable_value), | ||
| 2087 | }); | ||
| 2088 | } | ||
| 2089 | |||
| 2090 | pub fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst { | ||
| 2091 | return self.constInst(scope, src, .{ | ||
| 2092 | .ty = ty, | ||
| 2093 | .val = Value.initTag(.undef), | ||
| 2094 | }); | ||
| 2095 | } | ||
| 2096 | |||
| 2097 | pub fn constBool(self: *Module, scope: *Scope, src: usize, v: bool) !*Inst { | ||
| 2098 | return self.constInst(scope, src, .{ | ||
| 2099 | .ty = Type.initTag(.bool), | ||
| 2100 | .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)], | ||
| 2101 | }); | ||
| 2102 | } | ||
| 2103 | |||
| 2104 | pub fn constIntUnsigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: u64) !*Inst { | ||
| 2105 | const int_payload = try scope.arena().create(Value.Payload.Int_u64); | ||
| 2106 | int_payload.* = .{ .int = int }; | ||
| 2107 | |||
| 2108 | return self.constInst(scope, src, .{ | ||
| 2109 | .ty = ty, | ||
| 2110 | .val = Value.initPayload(&int_payload.base), | ||
| 2111 | }); | ||
| 2112 | } | ||
| 2113 | |||
| 2114 | pub fn constIntSigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: i64) !*Inst { | ||
| 2115 | const int_payload = try scope.arena().create(Value.Payload.Int_i64); | ||
| 2116 | int_payload.* = .{ .int = int }; | ||
| 2117 | |||
| 2118 | return self.constInst(scope, src, .{ | ||
| 2119 | .ty = ty, | ||
| 2120 | .val = Value.initPayload(&int_payload.base), | ||
| 2121 | }); | ||
| 2122 | } | ||
| 2123 | |||
| 2124 | pub fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigIntConst) !*Inst { | ||
| 2125 | const val_payload = if (big_int.positive) blk: { | ||
| 2126 | if (big_int.to(u64)) |x| { | ||
| 2127 | return self.constIntUnsigned(scope, src, ty, x); | ||
| 2128 | } else |err| switch (err) { | ||
| 2129 | error.NegativeIntoUnsigned => unreachable, | ||
| 2130 | error.TargetTooSmall => {}, // handled below | ||
| 2131 | } | ||
| 2132 | const big_int_payload = try scope.arena().create(Value.Payload.IntBigPositive); | ||
| 2133 | big_int_payload.* = .{ .limbs = big_int.limbs }; | ||
| 2134 | break :blk &big_int_payload.base; | ||
| 2135 | } else blk: { | ||
| 2136 | if (big_int.to(i64)) |x| { | ||
| 2137 | return self.constIntSigned(scope, src, ty, x); | ||
| 2138 | } else |err| switch (err) { | ||
| 2139 | error.NegativeIntoUnsigned => unreachable, | ||
| 2140 | error.TargetTooSmall => {}, // handled below | ||
| 2141 | } | ||
| 2142 | const big_int_payload = try scope.arena().create(Value.Payload.IntBigNegative); | ||
| 2143 | big_int_payload.* = .{ .limbs = big_int.limbs }; | ||
| 2144 | break :blk &big_int_payload.base; | ||
| 2145 | }; | ||
| 2146 | |||
| 2147 | return self.constInst(scope, src, .{ | ||
| 2148 | .ty = ty, | ||
| 2149 | .val = Value.initPayload(val_payload), | ||
| 2150 | }); | ||
| 2151 | } | ||
| 2152 | |||
| 2153 | pub fn createAnonymousDecl( | ||
| 2154 | self: *Module, | ||
| 2155 | scope: *Scope, | ||
| 2156 | decl_arena: *std.heap.ArenaAllocator, | ||
| 2157 | typed_value: TypedValue, | ||
| 2158 | ) !*Decl { | ||
| 2159 | const name_index = self.getNextAnonNameIndex(); | ||
| 2160 | const scope_decl = scope.decl().?; | ||
| 2161 | const name = try std.fmt.allocPrint(self.gpa, "{}__anon_{}", .{ scope_decl.name, name_index }); | ||
| 2162 | defer self.gpa.free(name); | ||
| 2163 | const name_hash = scope.namespace().fullyQualifiedNameHash(name); | ||
| 2164 | const src_hash: std.zig.SrcHash = undefined; | ||
| 2165 | const new_decl = try self.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash); | ||
| 2166 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); | ||
| 2167 | |||
| 2168 | decl_arena_state.* = decl_arena.state; | ||
| 2169 | new_decl.typed_value = .{ | ||
| 2170 | .most_recent = .{ | ||
| 2171 | .typed_value = typed_value, | ||
| 2172 | .arena = decl_arena_state, | ||
| 2173 | }, | ||
| 2174 | }; | ||
| 2175 | new_decl.analysis = .complete; | ||
| 2176 | new_decl.generation = self.generation; | ||
| 2177 | |||
| 2178 | // TODO: This generates the Decl into the machine code file if it is of a type that is non-zero size. | ||
| 2179 | // We should be able to further improve the compiler to not omit Decls which are only referenced at | ||
| 2180 | // compile-time and not runtime. | ||
| 2181 | if (typed_value.ty.hasCodeGenBits()) { | ||
| 2182 | try self.comp.bin_file.allocateDeclIndexes(new_decl); | ||
| 2183 | try self.comp.work_queue.writeItem(.{ .codegen_decl = new_decl }); | ||
| 2184 | } | ||
| 2185 | |||
| 2186 | return new_decl; | ||
| 2187 | } | ||
| 2188 | |||
| 2189 | fn getNextAnonNameIndex(self: *Module) usize { | ||
| 2190 | return @atomicRmw(usize, &self.next_anon_name_index, .Add, 1, .Monotonic); | ||
| 2191 | } | ||
| 2192 | |||
| 2193 | pub fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*Decl { | ||
| 2194 | const namespace = scope.namespace(); | ||
| 2195 | const name_hash = namespace.fullyQualifiedNameHash(ident_name); | ||
| 2196 | return self.decl_table.get(name_hash); | ||
| 2197 | } | ||
| 2198 | |||
| 2199 | pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst { | ||
| 2200 | const scope_decl = scope.decl().?; | ||
| 2201 | try self.declareDeclDependency(scope_decl, decl); | ||
| 2202 | self.ensureDeclAnalyzed(decl) catch |err| { | ||
| 2203 | if (scope.cast(Scope.Block)) |block| { | ||
| 2204 | if (block.func) |func| { | ||
| 2205 | func.analysis = .dependency_failure; | ||
| 2206 | } else { | ||
| 2207 | block.decl.analysis = .dependency_failure; | ||
| 2208 | } | ||
| 2209 | } else { | ||
| 2210 | scope_decl.analysis = .dependency_failure; | ||
| 2211 | } | ||
| 2212 | return err; | ||
| 2213 | }; | ||
| 2214 | |||
| 2215 | const decl_tv = try decl.typedValue(); | ||
| 2216 | if (decl_tv.val.tag() == .variable) { | ||
| 2217 | return self.analyzeVarRef(scope, src, decl_tv); | ||
| 2218 | } | ||
| 2219 | const ty = try self.simplePtrType(scope, src, decl_tv.ty, false, .One); | ||
| 2220 | const val_payload = try scope.arena().create(Value.Payload.DeclRef); | ||
| 2221 | val_payload.* = .{ .decl = decl }; | ||
| 2222 | |||
| 2223 | return self.constInst(scope, src, .{ | ||
| 2224 | .ty = ty, | ||
| 2225 | .val = Value.initPayload(&val_payload.base), | ||
| 2226 | }); | ||
| 2227 | } | ||
| 2228 | |||
| 2229 | fn analyzeVarRef(self: *Module, scope: *Scope, src: usize, tv: TypedValue) InnerError!*Inst { | ||
| 2230 | const variable = tv.val.cast(Value.Payload.Variable).?.variable; | ||
| 2231 | |||
| 2232 | const ty = try self.simplePtrType(scope, src, tv.ty, variable.is_mutable, .One); | ||
| 2233 | if (!variable.is_mutable and !variable.is_extern) { | ||
| 2234 | const val_payload = try scope.arena().create(Value.Payload.RefVal); | ||
| 2235 | val_payload.* = .{ .val = variable.init }; | ||
| 2236 | return self.constInst(scope, src, .{ | ||
| 2237 | .ty = ty, | ||
| 2238 | .val = Value.initPayload(&val_payload.base), | ||
| 2239 | }); | ||
| 2240 | } | ||
| 2241 | |||
| 2242 | const b = try self.requireRuntimeBlock(scope, src); | ||
| 2243 | const inst = try b.arena.create(Inst.VarPtr); | ||
| 2244 | inst.* = .{ | ||
| 2245 | .base = .{ | ||
| 2246 | .tag = .varptr, | ||
| 2247 | .ty = ty, | ||
| 2248 | .src = src, | ||
| 2249 | }, | ||
| 2250 | .variable = variable, | ||
| 2251 | }; | ||
| 2252 | try b.instructions.append(self.gpa, &inst.base); | ||
| 2253 | return &inst.base; | ||
| 2254 | } | ||
| 2255 | |||
| 2256 | pub fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_src: usize) InnerError!*Inst { | ||
| 2257 | const elem_ty = switch (ptr.ty.zigTypeTag()) { | ||
| 2258 | .Pointer => ptr.ty.elemType(), | ||
| 2259 | else => return self.fail(scope, ptr_src, "expected pointer, found '{}'", .{ptr.ty}), | ||
| 2260 | }; | ||
| 2261 | if (ptr.value()) |val| { | ||
| 2262 | return self.constInst(scope, src, .{ | ||
| 2263 | .ty = elem_ty, | ||
| 2264 | .val = try val.pointerDeref(scope.arena()), | ||
| 2265 | }); | ||
| 2266 | } | ||
| 2267 | |||
| 2268 | const b = try self.requireRuntimeBlock(scope, src); | ||
| 2269 | return self.addUnOp(b, src, elem_ty, .load, ptr); | ||
| 2270 | } | ||
| 2271 | |||
| 2272 | pub fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name: []const u8) InnerError!*Inst { | ||
| 2273 | const decl = self.lookupDeclName(scope, decl_name) orelse | ||
| 2274 | return self.fail(scope, src, "decl '{}' not found", .{decl_name}); | ||
| 2275 | return self.analyzeDeclRef(scope, src, decl); | ||
| 2276 | } | ||
| 2277 | |||
| 2278 | pub fn wantSafety(self: *Module, scope: *Scope) bool { | ||
| 2279 | // TODO take into account scope's safety overrides | ||
| 2280 | return switch (self.optimizeMode()) { | ||
| 2281 | .Debug => true, | ||
| 2282 | .ReleaseSafe => true, | ||
| 2283 | .ReleaseFast => false, | ||
| 2284 | .ReleaseSmall => false, | ||
| 2285 | }; | ||
| 2286 | } | ||
| 2287 | |||
| 2288 | pub fn analyzeIsNull( | ||
| 2289 | self: *Module, | ||
| 2290 | scope: *Scope, | ||
| 2291 | src: usize, | ||
| 2292 | operand: *Inst, | ||
| 2293 | invert_logic: bool, | ||
| 2294 | ) InnerError!*Inst { | ||
| 2295 | if (operand.value()) |opt_val| { | ||
| 2296 | const is_null = opt_val.isNull(); | ||
| 2297 | const bool_value = if (invert_logic) !is_null else is_null; | ||
| 2298 | return self.constBool(scope, src, bool_value); | ||
| 2299 | } | ||
| 2300 | const b = try self.requireRuntimeBlock(scope, src); | ||
| 2301 | const inst_tag: Inst.Tag = if (invert_logic) .isnonnull else .isnull; | ||
| 2302 | return self.addUnOp(b, src, Type.initTag(.bool), inst_tag, operand); | ||
| 2303 | } | ||
| 2304 | |||
| 2305 | pub fn analyzeIsErr(self: *Module, scope: *Scope, src: usize, operand: *Inst) InnerError!*Inst { | ||
| 2306 | return self.fail(scope, src, "TODO implement analysis of iserr", .{}); | ||
| 2307 | } | ||
| 2308 | |||
| 2309 | pub fn analyzeSlice(self: *Module, scope: *Scope, src: usize, array_ptr: *Inst, start: *Inst, end_opt: ?*Inst, sentinel_opt: ?*Inst) InnerError!*Inst { | ||
| 2310 | const ptr_child = switch (array_ptr.ty.zigTypeTag()) { | ||
| 2311 | .Pointer => array_ptr.ty.elemType(), | ||
| 2312 | else => return self.fail(scope, src, "expected pointer, found '{}'", .{array_ptr.ty}), | ||
| 2313 | }; | ||
| 2314 | |||
| 2315 | var array_type = ptr_child; | ||
| 2316 | const elem_type = switch (ptr_child.zigTypeTag()) { | ||
| 2317 | .Array => ptr_child.elemType(), | ||
| 2318 | .Pointer => blk: { | ||
| 2319 | if (ptr_child.isSinglePointer()) { | ||
| 2320 | if (ptr_child.elemType().zigTypeTag() == .Array) { | ||
| 2321 | array_type = ptr_child.elemType(); | ||
| 2322 | break :blk ptr_child.elemType().elemType(); | ||
| 2323 | } | ||
| 2324 | |||
| 2325 | return self.fail(scope, src, "slice of single-item pointer", .{}); | ||
| 2326 | } | ||
| 2327 | break :blk ptr_child.elemType(); | ||
| 2328 | }, | ||
| 2329 | else => return self.fail(scope, src, "slice of non-array type '{}'", .{ptr_child}), | ||
| 2330 | }; | ||
| 2331 | |||
| 2332 | const slice_sentinel = if (sentinel_opt) |sentinel| blk: { | ||
| 2333 | const casted = try self.coerce(scope, elem_type, sentinel); | ||
| 2334 | break :blk try self.resolveConstValue(scope, casted); | ||
| 2335 | } else null; | ||
| 2336 | |||
| 2337 | var return_ptr_size: std.builtin.TypeInfo.Pointer.Size = .Slice; | ||
| 2338 | var return_elem_type = elem_type; | ||
| 2339 | if (end_opt) |end| { | ||
| 2340 | if (end.value()) |end_val| { | ||
| 2341 | if (start.value()) |start_val| { | ||
| 2342 | const start_u64 = start_val.toUnsignedInt(); | ||
| 2343 | const end_u64 = end_val.toUnsignedInt(); | ||
| 2344 | if (start_u64 > end_u64) { | ||
| 2345 | return self.fail(scope, src, "out of bounds slice", .{}); | ||
| 2346 | } | ||
| 2347 | |||
| 2348 | const len = end_u64 - start_u64; | ||
| 2349 | const array_sentinel = if (array_type.zigTypeTag() == .Array and end_u64 == array_type.arrayLen()) | ||
| 2350 | array_type.sentinel() | ||
| 2351 | else | ||
| 2352 | slice_sentinel; | ||
| 2353 | return_elem_type = try self.arrayType(scope, len, array_sentinel, elem_type); | ||
| 2354 | return_ptr_size = .One; | ||
| 2355 | } | ||
| 2356 | } | ||
| 2357 | } | ||
| 2358 | const return_type = try self.ptrType( | ||
| 2359 | scope, | ||
| 2360 | src, | ||
| 2361 | return_elem_type, | ||
| 2362 | if (end_opt == null) slice_sentinel else null, | ||
| 2363 | 0, // TODO alignment | ||
| 2364 | 0, | ||
| 2365 | 0, | ||
| 2366 | !ptr_child.isConstPtr(), | ||
| 2367 | ptr_child.isAllowzeroPtr(), | ||
| 2368 | ptr_child.isVolatilePtr(), | ||
| 2369 | return_ptr_size, | ||
| 2370 | ); | ||
| 2371 | |||
| 2372 | return self.fail(scope, src, "TODO implement analysis of slice", .{}); | ||
| 2373 | } | ||
| 2374 | |||
| 2375 | /// Asserts that lhs and rhs types are both numeric. | ||
| 2376 | pub fn cmpNumeric( | ||
| 2377 | self: *Module, | ||
| 2378 | scope: *Scope, | ||
| 2379 | src: usize, | ||
| 2380 | lhs: *Inst, | ||
| 2381 | rhs: *Inst, | ||
| 2382 | op: std.math.CompareOperator, | ||
| 2383 | ) !*Inst { | ||
| 2384 | assert(lhs.ty.isNumeric()); | ||
| 2385 | assert(rhs.ty.isNumeric()); | ||
| 2386 | |||
| 2387 | const lhs_ty_tag = lhs.ty.zigTypeTag(); | ||
| 2388 | const rhs_ty_tag = rhs.ty.zigTypeTag(); | ||
| 2389 | |||
| 2390 | if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) { | ||
| 2391 | if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) { | ||
| 2392 | return self.fail(scope, src, "vector length mismatch: {} and {}", .{ | ||
| 2393 | lhs.ty.arrayLen(), | ||
| 2394 | rhs.ty.arrayLen(), | ||
| 2395 | }); | ||
| 2396 | } | ||
| 2397 | return self.fail(scope, src, "TODO implement support for vectors in cmpNumeric", .{}); | ||
| 2398 | } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) { | ||
| 2399 | return self.fail(scope, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{ | ||
| 2400 | lhs.ty, | ||
| 2401 | rhs.ty, | ||
| 2402 | }); | ||
| 2403 | } | ||
| 2404 | |||
| 2405 | if (lhs.value()) |lhs_val| { | ||
| 2406 | if (rhs.value()) |rhs_val| { | ||
| 2407 | return self.constBool(scope, src, Value.compare(lhs_val, op, rhs_val)); | ||
| 2408 | } | ||
| 2409 | } | ||
| 2410 | |||
| 2411 | // TODO handle comparisons against lazy zero values | ||
| 2412 | // Some values can be compared against zero without being runtime known or without forcing | ||
| 2413 | // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to | ||
| 2414 | // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout | ||
| 2415 | // of this function if we don't need to. | ||
| 2416 | |||
| 2417 | // It must be a runtime comparison. | ||
| 2418 | const b = try self.requireRuntimeBlock(scope, src); | ||
| 2419 | // For floats, emit a float comparison instruction. | ||
| 2420 | const lhs_is_float = switch (lhs_ty_tag) { | ||
| 2421 | .Float, .ComptimeFloat => true, | ||
| 2422 | else => false, | ||
| 2423 | }; | ||
| 2424 | const rhs_is_float = switch (rhs_ty_tag) { | ||
| 2425 | .Float, .ComptimeFloat => true, | ||
| 2426 | else => false, | ||
| 2427 | }; | ||
| 2428 | if (lhs_is_float and rhs_is_float) { | ||
| 2429 | // Implicit cast the smaller one to the larger one. | ||
| 2430 | const dest_type = x: { | ||
| 2431 | if (lhs_ty_tag == .ComptimeFloat) { | ||
| 2432 | break :x rhs.ty; | ||
| 2433 | } else if (rhs_ty_tag == .ComptimeFloat) { | ||
| 2434 | break :x lhs.ty; | ||
| 2435 | } | ||
| 2436 | if (lhs.ty.floatBits(self.getTarget()) >= rhs.ty.floatBits(self.getTarget())) { | ||
| 2437 | break :x lhs.ty; | ||
| 2438 | } else { | ||
| 2439 | break :x rhs.ty; | ||
| 2440 | } | ||
| 2441 | }; | ||
| 2442 | const casted_lhs = try self.coerce(scope, dest_type, lhs); | ||
| 2443 | const casted_rhs = try self.coerce(scope, dest_type, rhs); | ||
| 2444 | return self.addBinOp(b, src, dest_type, Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs); | ||
| 2445 | } | ||
| 2446 | // For mixed unsigned integer sizes, implicit cast both operands to the larger integer. | ||
| 2447 | // For mixed signed and unsigned integers, implicit cast both operands to a signed | ||
| 2448 | // integer with + 1 bit. | ||
| 2449 | // For mixed floats and integers, extract the integer part from the float, cast that to | ||
| 2450 | // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float, | ||
| 2451 | // add/subtract 1. | ||
| 2452 | const lhs_is_signed = if (lhs.value()) |lhs_val| | ||
| 2453 | lhs_val.compareWithZero(.lt) | ||
| 2454 | else | ||
| 2455 | (lhs.ty.isFloat() or lhs.ty.isSignedInt()); | ||
| 2456 | const rhs_is_signed = if (rhs.value()) |rhs_val| | ||
| 2457 | rhs_val.compareWithZero(.lt) | ||
| 2458 | else | ||
| 2459 | (rhs.ty.isFloat() or rhs.ty.isSignedInt()); | ||
| 2460 | const dest_int_is_signed = lhs_is_signed or rhs_is_signed; | ||
| 2461 | |||
| 2462 | var dest_float_type: ?Type = null; | ||
| 2463 | |||
| 2464 | var lhs_bits: usize = undefined; | ||
| 2465 | if (lhs.value()) |lhs_val| { | ||
| 2466 | if (lhs_val.isUndef()) | ||
| 2467 | return self.constUndef(scope, src, Type.initTag(.bool)); | ||
| 2468 | const is_unsigned = if (lhs_is_float) x: { | ||
| 2469 | var bigint_space: Value.BigIntSpace = undefined; | ||
| 2470 | var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(self.gpa); | ||
| 2471 | defer bigint.deinit(); | ||
| 2472 | const zcmp = lhs_val.orderAgainstZero(); | ||
| 2473 | if (lhs_val.floatHasFraction()) { | ||
| 2474 | switch (op) { | ||
| 2475 | .eq => return self.constBool(scope, src, false), | ||
| 2476 | .neq => return self.constBool(scope, src, true), | ||
| 2477 | else => {}, | ||
| 2478 | } | ||
| 2479 | if (zcmp == .lt) { | ||
| 2480 | try bigint.addScalar(bigint.toConst(), -1); | ||
| 2481 | } else { | ||
| 2482 | try bigint.addScalar(bigint.toConst(), 1); | ||
| 2483 | } | ||
| 2484 | } | ||
| 2485 | lhs_bits = bigint.toConst().bitCountTwosComp(); | ||
| 2486 | break :x (zcmp != .lt); | ||
| 2487 | } else x: { | ||
| 2488 | lhs_bits = lhs_val.intBitCountTwosComp(); | ||
| 2489 | break :x (lhs_val.orderAgainstZero() != .lt); | ||
| 2490 | }; | ||
| 2491 | lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed); | ||
| 2492 | } else if (lhs_is_float) { | ||
| 2493 | dest_float_type = lhs.ty; | ||
| 2494 | } else { | ||
| 2495 | const int_info = lhs.ty.intInfo(self.getTarget()); | ||
| 2496 | lhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed); | ||
| 2497 | } | ||
| 2498 | |||
| 2499 | var rhs_bits: usize = undefined; | ||
| 2500 | if (rhs.value()) |rhs_val| { | ||
| 2501 | if (rhs_val.isUndef()) | ||
| 2502 | return self.constUndef(scope, src, Type.initTag(.bool)); | ||
| 2503 | const is_unsigned = if (rhs_is_float) x: { | ||
| 2504 | var bigint_space: Value.BigIntSpace = undefined; | ||
| 2505 | var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(self.gpa); | ||
| 2506 | defer bigint.deinit(); | ||
| 2507 | const zcmp = rhs_val.orderAgainstZero(); | ||
| 2508 | if (rhs_val.floatHasFraction()) { | ||
| 2509 | switch (op) { | ||
| 2510 | .eq => return self.constBool(scope, src, false), | ||
| 2511 | .neq => return self.constBool(scope, src, true), | ||
| 2512 | else => {}, | ||
| 2513 | } | ||
| 2514 | if (zcmp == .lt) { | ||
| 2515 | try bigint.addScalar(bigint.toConst(), -1); | ||
| 2516 | } else { | ||
| 2517 | try bigint.addScalar(bigint.toConst(), 1); | ||
| 2518 | } | ||
| 2519 | } | ||
| 2520 | rhs_bits = bigint.toConst().bitCountTwosComp(); | ||
| 2521 | break :x (zcmp != .lt); | ||
| 2522 | } else x: { | ||
| 2523 | rhs_bits = rhs_val.intBitCountTwosComp(); | ||
| 2524 | break :x (rhs_val.orderAgainstZero() != .lt); | ||
| 2525 | }; | ||
| 2526 | rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed); | ||
| 2527 | } else if (rhs_is_float) { | ||
| 2528 | dest_float_type = rhs.ty; | ||
| 2529 | } else { | ||
| 2530 | const int_info = rhs.ty.intInfo(self.getTarget()); | ||
| 2531 | rhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed); | ||
| 2532 | } | ||
| 2533 | |||
| 2534 | const dest_type = if (dest_float_type) |ft| ft else blk: { | ||
| 2535 | const max_bits = std.math.max(lhs_bits, rhs_bits); | ||
| 2536 | const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) { | ||
| 2537 | error.Overflow => return self.fail(scope, src, "{} exceeds maximum integer bit count", .{max_bits}), | ||
| 2538 | }; | ||
| 2539 | break :blk try self.makeIntType(scope, dest_int_is_signed, casted_bits); | ||
| 2540 | }; | ||
| 2541 | const casted_lhs = try self.coerce(scope, dest_type, lhs); | ||
| 2542 | const casted_rhs = try self.coerce(scope, dest_type, rhs); | ||
| 2543 | |||
| 2544 | return self.addBinOp(b, src, Type.initTag(.bool), Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs); | ||
| 2545 | } | ||
| 2546 | |||
| 2547 | fn wrapOptional(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { | ||
| 2548 | if (inst.value()) |val| { | ||
| 2549 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 2550 | } | ||
| 2551 | |||
| 2552 | const b = try self.requireRuntimeBlock(scope, inst.src); | ||
| 2553 | return self.addUnOp(b, inst.src, dest_type, .wrap_optional, inst); | ||
| 2554 | } | ||
| 2555 | |||
| 2556 | fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type { | ||
| 2557 | if (signed) { | ||
| 2558 | const int_payload = try scope.arena().create(Type.Payload.IntSigned); | ||
| 2559 | int_payload.* = .{ .bits = bits }; | ||
| 2560 | return Type.initPayload(&int_payload.base); | ||
| 2561 | } else { | ||
| 2562 | const int_payload = try scope.arena().create(Type.Payload.IntUnsigned); | ||
| 2563 | int_payload.* = .{ .bits = bits }; | ||
| 2564 | return Type.initPayload(&int_payload.base); | ||
| 2565 | } | ||
| 2566 | } | ||
| 2567 | |||
| 2568 | pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type { | ||
| 2569 | if (instructions.len == 0) | ||
| 2570 | return Type.initTag(.noreturn); | ||
| 2571 | |||
| 2572 | if (instructions.len == 1) | ||
| 2573 | return instructions[0].ty; | ||
| 2574 | |||
| 2575 | var prev_inst = instructions[0]; | ||
| 2576 | for (instructions[1..]) |next_inst| { | ||
| 2577 | if (next_inst.ty.eql(prev_inst.ty)) | ||
| 2578 | continue; | ||
| 2579 | if (next_inst.ty.zigTypeTag() == .NoReturn) | ||
| 2580 | continue; | ||
| 2581 | if (prev_inst.ty.zigTypeTag() == .NoReturn) { | ||
| 2582 | prev_inst = next_inst; | ||
| 2583 | continue; | ||
| 2584 | } | ||
| 2585 | if (next_inst.ty.zigTypeTag() == .Undefined) | ||
| 2586 | continue; | ||
| 2587 | if (prev_inst.ty.zigTypeTag() == .Undefined) { | ||
| 2588 | prev_inst = next_inst; | ||
| 2589 | continue; | ||
| 2590 | } | ||
| 2591 | if (prev_inst.ty.isInt() and | ||
| 2592 | next_inst.ty.isInt() and | ||
| 2593 | prev_inst.ty.isSignedInt() == next_inst.ty.isSignedInt()) | ||
| 2594 | { | ||
| 2595 | if (prev_inst.ty.intInfo(self.getTarget()).bits < next_inst.ty.intInfo(self.getTarget()).bits) { | ||
| 2596 | prev_inst = next_inst; | ||
| 2597 | } | ||
| 2598 | continue; | ||
| 2599 | } | ||
| 2600 | if (prev_inst.ty.isFloat() and next_inst.ty.isFloat()) { | ||
| 2601 | if (prev_inst.ty.floatBits(self.getTarget()) < next_inst.ty.floatBits(self.getTarget())) { | ||
| 2602 | prev_inst = next_inst; | ||
| 2603 | } | ||
| 2604 | continue; | ||
| 2605 | } | ||
| 2606 | |||
| 2607 | // TODO error notes pointing out each type | ||
| 2608 | return self.fail(scope, next_inst.src, "incompatible types: '{}' and '{}'", .{ prev_inst.ty, next_inst.ty }); | ||
| 2609 | } | ||
| 2610 | |||
| 2611 | return prev_inst.ty; | ||
| 2612 | } | ||
| 2613 | |||
| 2614 | pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { | ||
| 2615 | // If the types are the same, we can return the operand. | ||
| 2616 | if (dest_type.eql(inst.ty)) | ||
| 2617 | return inst; | ||
| 2618 | |||
| 2619 | const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty); | ||
| 2620 | if (in_memory_result == .ok) { | ||
| 2621 | return self.bitcast(scope, dest_type, inst); | ||
| 2622 | } | ||
| 2623 | |||
| 2624 | // undefined to anything | ||
| 2625 | if (inst.value()) |val| { | ||
| 2626 | if (val.isUndef() or inst.ty.zigTypeTag() == .Undefined) { | ||
| 2627 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 2628 | } | ||
| 2629 | } | ||
| 2630 | assert(inst.ty.zigTypeTag() != .Undefined); | ||
| 2631 | |||
| 2632 | // null to ?T | ||
| 2633 | if (dest_type.zigTypeTag() == .Optional and inst.ty.zigTypeTag() == .Null) { | ||
| 2634 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = Value.initTag(.null_value) }); | ||
| 2635 | } | ||
| 2636 | |||
| 2637 | // T to ?T | ||
| 2638 | if (dest_type.zigTypeTag() == .Optional) { | ||
| 2639 | var buf: Type.Payload.PointerSimple = undefined; | ||
| 2640 | const child_type = dest_type.optionalChild(&buf); | ||
| 2641 | if (child_type.eql(inst.ty)) { | ||
| 2642 | return self.wrapOptional(scope, dest_type, inst); | ||
| 2643 | } else if (try self.coerceNum(scope, child_type, inst)) |some| { | ||
| 2644 | return self.wrapOptional(scope, dest_type, some); | ||
| 2645 | } | ||
| 2646 | } | ||
| 2647 | |||
| 2648 | // *[N]T to []T | ||
| 2649 | if (inst.ty.isSinglePointer() and dest_type.isSlice() and | ||
| 2650 | (!inst.ty.isConstPtr() or dest_type.isConstPtr())) | ||
| 2651 | { | ||
| 2652 | const array_type = inst.ty.elemType(); | ||
| 2653 | const dst_elem_type = dest_type.elemType(); | ||
| 2654 | if (array_type.zigTypeTag() == .Array and | ||
| 2655 | coerceInMemoryAllowed(dst_elem_type, array_type.elemType()) == .ok) | ||
| 2656 | { | ||
| 2657 | return self.coerceArrayPtrToSlice(scope, dest_type, inst); | ||
| 2658 | } | ||
| 2659 | } | ||
| 2660 | |||
| 2661 | // comptime known number to other number | ||
| 2662 | if (try self.coerceNum(scope, dest_type, inst)) |some| | ||
| 2663 | return some; | ||
| 2664 | |||
| 2665 | // integer widening | ||
| 2666 | if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) { | ||
| 2667 | assert(inst.value() == null); // handled above | ||
| 2668 | |||
| 2669 | const src_info = inst.ty.intInfo(self.getTarget()); | ||
| 2670 | const dst_info = dest_type.intInfo(self.getTarget()); | ||
| 2671 | if ((src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) or | ||
| 2672 | // small enough unsigned ints can get casted to large enough signed ints | ||
| 2673 | (src_info.signed and !dst_info.signed and dst_info.bits > src_info.bits)) | ||
| 2674 | { | ||
| 2675 | const b = try self.requireRuntimeBlock(scope, inst.src); | ||
| 2676 | return self.addUnOp(b, inst.src, dest_type, .intcast, inst); | ||
| 2677 | } | ||
| 2678 | } | ||
| 2679 | |||
| 2680 | // float widening | ||
| 2681 | if (inst.ty.zigTypeTag() == .Float and dest_type.zigTypeTag() == .Float) { | ||
| 2682 | assert(inst.value() == null); // handled above | ||
| 2683 | |||
| 2684 | const src_bits = inst.ty.floatBits(self.getTarget()); | ||
| 2685 | const dst_bits = dest_type.floatBits(self.getTarget()); | ||
| 2686 | if (dst_bits >= src_bits) { | ||
| 2687 | const b = try self.requireRuntimeBlock(scope, inst.src); | ||
| 2688 | return self.addUnOp(b, inst.src, dest_type, .floatcast, inst); | ||
| 2689 | } | ||
| 2690 | } | ||
| 2691 | |||
| 2692 | return self.fail(scope, inst.src, "expected {}, found {}", .{ dest_type, inst.ty }); | ||
| 2693 | } | ||
| 2694 | |||
| 2695 | pub fn coerceNum(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !?*Inst { | ||
| 2696 | const val = inst.value() orelse return null; | ||
| 2697 | const src_zig_tag = inst.ty.zigTypeTag(); | ||
| 2698 | const dst_zig_tag = dest_type.zigTypeTag(); | ||
| 2699 | |||
| 2700 | if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) { | ||
| 2701 | if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { | ||
| 2702 | if (val.floatHasFraction()) { | ||
| 2703 | return self.fail(scope, inst.src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst.ty }); | ||
| 2704 | } | ||
| 2705 | return self.fail(scope, inst.src, "TODO float to int", .{}); | ||
| 2706 | } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { | ||
| 2707 | if (!val.intFitsInType(dest_type, self.getTarget())) { | ||
| 2708 | return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val }); | ||
| 2709 | } | ||
| 2710 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 2711 | } | ||
| 2712 | } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) { | ||
| 2713 | if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { | ||
| 2714 | const res = val.floatCast(scope.arena(), dest_type, self.getTarget()) catch |err| switch (err) { | ||
| 2715 | error.Overflow => return self.fail( | ||
| 2716 | scope, | ||
| 2717 | inst.src, | ||
| 2718 | "cast of value {} to type '{}' loses information", | ||
| 2719 | .{ val, dest_type }, | ||
| 2720 | ), | ||
| 2721 | error.OutOfMemory => return error.OutOfMemory, | ||
| 2722 | }; | ||
| 2723 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = res }); | ||
| 2724 | } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { | ||
| 2725 | return self.fail(scope, inst.src, "TODO int to float", .{}); | ||
| 2726 | } | ||
| 2727 | } | ||
| 2728 | return null; | ||
| 2729 | } | ||
| 2730 | |||
| 2731 | pub fn storePtr(self: *Module, scope: *Scope, src: usize, ptr: *Inst, uncasted_value: *Inst) !*Inst { | ||
| 2732 | if (ptr.ty.isConstPtr()) | ||
| 2733 | return self.fail(scope, src, "cannot assign to constant", .{}); | ||
| 2734 | |||
| 2735 | const elem_ty = ptr.ty.elemType(); | ||
| 2736 | const value = try self.coerce(scope, elem_ty, uncasted_value); | ||
| 2737 | if (elem_ty.onePossibleValue() != null) | ||
| 2738 | return self.constVoid(scope, src); | ||
| 2739 | |||
| 2740 | // TODO handle comptime pointer writes | ||
| 2741 | // TODO handle if the element type requires comptime | ||
| 2742 | |||
| 2743 | const b = try self.requireRuntimeBlock(scope, src); | ||
| 2744 | return self.addBinOp(b, src, Type.initTag(.void), .store, ptr, value); | ||
| 2745 | } | ||
| 2746 | |||
| 2747 | pub fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { | ||
| 2748 | if (inst.value()) |val| { | ||
| 2749 | // Keep the comptime Value representation; take the new type. | ||
| 2750 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 2751 | } | ||
| 2752 | // TODO validate the type size and other compile errors | ||
| 2753 | const b = try self.requireRuntimeBlock(scope, inst.src); | ||
| 2754 | return self.addUnOp(b, inst.src, dest_type, .bitcast, inst); | ||
| 2755 | } | ||
| 2756 | |||
| 2757 | fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { | ||
| 2758 | if (inst.value()) |val| { | ||
| 2759 | // The comptime Value representation is compatible with both types. | ||
| 2760 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 2761 | } | ||
| 2762 | return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{}); | ||
| 2763 | } | ||
| 2764 | |||
| 2765 | pub fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError { | ||
| 2766 | @setCold(true); | ||
| 2767 | const err_msg = try Compilation.ErrorMsg.create(self.gpa, src, format, args); | ||
| 2768 | return self.failWithOwnedErrorMsg(scope, src, err_msg); | ||
| 2769 | } | ||
| 2770 | |||
| 2771 | pub fn failTok( | ||
| 2772 | self: *Module, | ||
| 2773 | scope: *Scope, | ||
| 2774 | token_index: ast.TokenIndex, | ||
| 2775 | comptime format: []const u8, | ||
| 2776 | args: anytype, | ||
| 2777 | ) InnerError { | ||
| 2778 | @setCold(true); | ||
| 2779 | const src = scope.tree().token_locs[token_index].start; | ||
| 2780 | return self.fail(scope, src, format, args); | ||
| 2781 | } | ||
| 2782 | |||
| 2783 | pub fn failNode( | ||
| 2784 | self: *Module, | ||
| 2785 | scope: *Scope, | ||
| 2786 | ast_node: *ast.Node, | ||
| 2787 | comptime format: []const u8, | ||
| 2788 | args: anytype, | ||
| 2789 | ) InnerError { | ||
| 2790 | @setCold(true); | ||
| 2791 | const src = scope.tree().token_locs[ast_node.firstToken()].start; | ||
| 2792 | return self.fail(scope, src, format, args); | ||
| 2793 | } | ||
| 2794 | |||
| 2795 | fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Compilation.ErrorMsg) InnerError { | ||
| 2796 | { | ||
| 2797 | errdefer err_msg.destroy(self.gpa); | ||
| 2798 | try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1); | ||
| 2799 | try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1); | ||
| 2800 | } | ||
| 2801 | switch (scope.tag) { | ||
| 2802 | .decl => { | ||
| 2803 | const decl = scope.cast(Scope.DeclAnalysis).?.decl; | ||
| 2804 | decl.analysis = .sema_failure; | ||
| 2805 | decl.generation = self.generation; | ||
| 2806 | self.failed_decls.putAssumeCapacityNoClobber(decl, err_msg); | ||
| 2807 | }, | ||
| 2808 | .block => { | ||
| 2809 | const block = scope.cast(Scope.Block).?; | ||
| 2810 | if (block.func) |func| { | ||
| 2811 | func.analysis = .sema_failure; | ||
| 2812 | } else { | ||
| 2813 | block.decl.analysis = .sema_failure; | ||
| 2814 | block.decl.generation = self.generation; | ||
| 2815 | } | ||
| 2816 | self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg); | ||
| 2817 | }, | ||
| 2818 | .gen_zir => { | ||
| 2819 | const gen_zir = scope.cast(Scope.GenZIR).?; | ||
| 2820 | gen_zir.decl.analysis = .sema_failure; | ||
| 2821 | gen_zir.decl.generation = self.generation; | ||
| 2822 | self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg); | ||
| 2823 | }, | ||
| 2824 | .local_val => { | ||
| 2825 | const gen_zir = scope.cast(Scope.LocalVal).?.gen_zir; | ||
| 2826 | gen_zir.decl.analysis = .sema_failure; | ||
| 2827 | gen_zir.decl.generation = self.generation; | ||
| 2828 | self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg); | ||
| 2829 | }, | ||
| 2830 | .local_ptr => { | ||
| 2831 | const gen_zir = scope.cast(Scope.LocalPtr).?.gen_zir; | ||
| 2832 | gen_zir.decl.analysis = .sema_failure; | ||
| 2833 | gen_zir.decl.generation = self.generation; | ||
| 2834 | self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg); | ||
| 2835 | }, | ||
| 2836 | .zir_module => { | ||
| 2837 | const zir_module = scope.cast(Scope.ZIRModule).?; | ||
| 2838 | zir_module.status = .loaded_sema_failure; | ||
| 2839 | self.failed_files.putAssumeCapacityNoClobber(scope, err_msg); | ||
| 2840 | }, | ||
| 2841 | .file => unreachable, | ||
| 2842 | .container => unreachable, | ||
| 2843 | } | ||
| 2844 | return error.AnalysisFail; | ||
| 2845 | } | ||
| 2846 | |||
| 2847 | const InMemoryCoercionResult = enum { | ||
| 2848 | ok, | ||
| 2849 | no_match, | ||
| 2850 | }; | ||
| 2851 | |||
| 2852 | fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult { | ||
| 2853 | if (dest_type.eql(src_type)) | ||
| 2854 | return .ok; | ||
| 2855 | |||
| 2856 | // TODO: implement more of this function | ||
| 2857 | |||
| 2858 | return .no_match; | ||
| 2859 | } | ||
| 2860 | |||
| 2861 | fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool { | ||
| 2862 | return @bitCast(u128, a) == @bitCast(u128, b); | ||
| 2863 | } | ||
| 2864 | |||
| 2865 | pub fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value { | ||
| 2866 | // TODO is this a performance issue? maybe we should try the operation without | ||
| 2867 | // resorting to BigInt first. | ||
| 2868 | var lhs_space: Value.BigIntSpace = undefined; | ||
| 2869 | var rhs_space: Value.BigIntSpace = undefined; | ||
| 2870 | const lhs_bigint = lhs.toBigInt(&lhs_space); | ||
| 2871 | const rhs_bigint = rhs.toBigInt(&rhs_space); | ||
| 2872 | const limbs = try allocator.alloc( | ||
| 2873 | std.math.big.Limb, | ||
| 2874 | std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, | ||
| 2875 | ); | ||
| 2876 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | ||
| 2877 | result_bigint.add(lhs_bigint, rhs_bigint); | ||
| 2878 | const result_limbs = result_bigint.limbs[0..result_bigint.len]; | ||
| 2879 | |||
| 2880 | const val_payload = if (result_bigint.positive) blk: { | ||
| 2881 | const val_payload = try allocator.create(Value.Payload.IntBigPositive); | ||
| 2882 | val_payload.* = .{ .limbs = result_limbs }; | ||
| 2883 | break :blk &val_payload.base; | ||
| 2884 | } else blk: { | ||
| 2885 | const val_payload = try allocator.create(Value.Payload.IntBigNegative); | ||
| 2886 | val_payload.* = .{ .limbs = result_limbs }; | ||
| 2887 | break :blk &val_payload.base; | ||
| 2888 | }; | ||
| 2889 | |||
| 2890 | return Value.initPayload(val_payload); | ||
| 2891 | } | ||
| 2892 | |||
| 2893 | pub fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value { | ||
| 2894 | // TODO is this a performance issue? maybe we should try the operation without | ||
| 2895 | // resorting to BigInt first. | ||
| 2896 | var lhs_space: Value.BigIntSpace = undefined; | ||
| 2897 | var rhs_space: Value.BigIntSpace = undefined; | ||
| 2898 | const lhs_bigint = lhs.toBigInt(&lhs_space); | ||
| 2899 | const rhs_bigint = rhs.toBigInt(&rhs_space); | ||
| 2900 | const limbs = try allocator.alloc( | ||
| 2901 | std.math.big.Limb, | ||
| 2902 | std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, | ||
| 2903 | ); | ||
| 2904 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | ||
| 2905 | result_bigint.sub(lhs_bigint, rhs_bigint); | ||
| 2906 | const result_limbs = result_bigint.limbs[0..result_bigint.len]; | ||
| 2907 | |||
| 2908 | const val_payload = if (result_bigint.positive) blk: { | ||
| 2909 | const val_payload = try allocator.create(Value.Payload.IntBigPositive); | ||
| 2910 | val_payload.* = .{ .limbs = result_limbs }; | ||
| 2911 | break :blk &val_payload.base; | ||
| 2912 | } else blk: { | ||
| 2913 | const val_payload = try allocator.create(Value.Payload.IntBigNegative); | ||
| 2914 | val_payload.* = .{ .limbs = result_limbs }; | ||
| 2915 | break :blk &val_payload.base; | ||
| 2916 | }; | ||
| 2917 | |||
| 2918 | return Value.initPayload(val_payload); | ||
| 2919 | } | ||
| 2920 | |||
| 2921 | pub fn floatAdd(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: Value, rhs: Value) !Value { | ||
| 2922 | var bit_count = switch (float_type.tag()) { | ||
| 2923 | .comptime_float => 128, | ||
| 2924 | else => float_type.floatBits(self.getTarget()), | ||
| 2925 | }; | ||
| 2926 | |||
| 2927 | const allocator = scope.arena(); | ||
| 2928 | const val_payload = switch (bit_count) { | ||
| 2929 | 16 => { | ||
| 2930 | return self.fail(scope, src, "TODO Implement addition for soft floats", .{}); | ||
| 2931 | }, | ||
| 2932 | 32 => blk: { | ||
| 2933 | const lhs_val = lhs.toFloat(f32); | ||
| 2934 | const rhs_val = rhs.toFloat(f32); | ||
| 2935 | const val_payload = try allocator.create(Value.Payload.Float_32); | ||
| 2936 | val_payload.* = .{ .val = lhs_val + rhs_val }; | ||
| 2937 | break :blk &val_payload.base; | ||
| 2938 | }, | ||
| 2939 | 64 => blk: { | ||
| 2940 | const lhs_val = lhs.toFloat(f64); | ||
| 2941 | const rhs_val = rhs.toFloat(f64); | ||
| 2942 | const val_payload = try allocator.create(Value.Payload.Float_64); | ||
| 2943 | val_payload.* = .{ .val = lhs_val + rhs_val }; | ||
| 2944 | break :blk &val_payload.base; | ||
| 2945 | }, | ||
| 2946 | 128 => { | ||
| 2947 | return self.fail(scope, src, "TODO Implement addition for big floats", .{}); | ||
| 2948 | }, | ||
| 2949 | else => unreachable, | ||
| 2950 | }; | ||
| 2951 | |||
| 2952 | return Value.initPayload(val_payload); | ||
| 2953 | } | ||
| 2954 | |||
| 2955 | pub fn floatSub(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: Value, rhs: Value) !Value { | ||
| 2956 | var bit_count = switch (float_type.tag()) { | ||
| 2957 | .comptime_float => 128, | ||
| 2958 | else => float_type.floatBits(self.getTarget()), | ||
| 2959 | }; | ||
| 2960 | |||
| 2961 | const allocator = scope.arena(); | ||
| 2962 | const val_payload = switch (bit_count) { | ||
| 2963 | 16 => { | ||
| 2964 | return self.fail(scope, src, "TODO Implement substraction for soft floats", .{}); | ||
| 2965 | }, | ||
| 2966 | 32 => blk: { | ||
| 2967 | const lhs_val = lhs.toFloat(f32); | ||
| 2968 | const rhs_val = rhs.toFloat(f32); | ||
| 2969 | const val_payload = try allocator.create(Value.Payload.Float_32); | ||
| 2970 | val_payload.* = .{ .val = lhs_val - rhs_val }; | ||
| 2971 | break :blk &val_payload.base; | ||
| 2972 | }, | ||
| 2973 | 64 => blk: { | ||
| 2974 | const lhs_val = lhs.toFloat(f64); | ||
| 2975 | const rhs_val = rhs.toFloat(f64); | ||
| 2976 | const val_payload = try allocator.create(Value.Payload.Float_64); | ||
| 2977 | val_payload.* = .{ .val = lhs_val - rhs_val }; | ||
| 2978 | break :blk &val_payload.base; | ||
| 2979 | }, | ||
| 2980 | 128 => { | ||
| 2981 | return self.fail(scope, src, "TODO Implement substraction for big floats", .{}); | ||
| 2982 | }, | ||
| 2983 | else => unreachable, | ||
| 2984 | }; | ||
| 2985 | |||
| 2986 | return Value.initPayload(val_payload); | ||
| 2987 | } | ||
| 2988 | |||
| 2989 | pub fn simplePtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type, mutable: bool, size: std.builtin.TypeInfo.Pointer.Size) Allocator.Error!Type { | ||
| 2990 | if (!mutable and size == .Slice and elem_ty.eql(Type.initTag(.u8))) { | ||
| 2991 | return Type.initTag(.const_slice_u8); | ||
| 2992 | } | ||
| 2993 | // TODO stage1 type inference bug | ||
| 2994 | const T = Type.Tag; | ||
| 2995 | |||
| 2996 | const type_payload = try scope.arena().create(Type.Payload.PointerSimple); | ||
| 2997 | type_payload.* = .{ | ||
| 2998 | .base = .{ | ||
| 2999 | .tag = switch (size) { | ||
| 3000 | .One => if (mutable) T.single_mut_pointer else T.single_const_pointer, | ||
| 3001 | .Many => if (mutable) T.many_mut_pointer else T.many_const_pointer, | ||
| 3002 | .C => if (mutable) T.c_mut_pointer else T.c_const_pointer, | ||
| 3003 | .Slice => if (mutable) T.mut_slice else T.const_slice, | ||
| 3004 | }, | ||
| 3005 | }, | ||
| 3006 | .pointee_type = elem_ty, | ||
| 3007 | }; | ||
| 3008 | return Type.initPayload(&type_payload.base); | ||
| 3009 | } | ||
| 3010 | |||
| 3011 | pub fn ptrType( | ||
| 3012 | self: *Module, | ||
| 3013 | scope: *Scope, | ||
| 3014 | src: usize, | ||
| 3015 | elem_ty: Type, | ||
| 3016 | sentinel: ?Value, | ||
| 3017 | @"align": u32, | ||
| 3018 | bit_offset: u16, | ||
| 3019 | host_size: u16, | ||
| 3020 | mutable: bool, | ||
| 3021 | @"allowzero": bool, | ||
| 3022 | @"volatile": bool, | ||
| 3023 | size: std.builtin.TypeInfo.Pointer.Size, | ||
| 3024 | ) Allocator.Error!Type { | ||
| 3025 | assert(host_size == 0 or bit_offset < host_size * 8); | ||
| 3026 | |||
| 3027 | // TODO check if type can be represented by simplePtrType | ||
| 3028 | const type_payload = try scope.arena().create(Type.Payload.Pointer); | ||
| 3029 | type_payload.* = .{ | ||
| 3030 | .pointee_type = elem_ty, | ||
| 3031 | .sentinel = sentinel, | ||
| 3032 | .@"align" = @"align", | ||
| 3033 | .bit_offset = bit_offset, | ||
| 3034 | .host_size = host_size, | ||
| 3035 | .@"allowzero" = @"allowzero", | ||
| 3036 | .mutable = mutable, | ||
| 3037 | .@"volatile" = @"volatile", | ||
| 3038 | .size = size, | ||
| 3039 | }; | ||
| 3040 | return Type.initPayload(&type_payload.base); | ||
| 3041 | } | ||
| 3042 | |||
| 3043 | pub fn optionalType(self: *Module, scope: *Scope, child_type: Type) Allocator.Error!Type { | ||
| 3044 | return Type.initPayload(switch (child_type.tag()) { | ||
| 3045 | .single_const_pointer => blk: { | ||
| 3046 | const payload = try scope.arena().create(Type.Payload.PointerSimple); | ||
| 3047 | payload.* = .{ | ||
| 3048 | .base = .{ .tag = .optional_single_const_pointer }, | ||
| 3049 | .pointee_type = child_type.elemType(), | ||
| 3050 | }; | ||
| 3051 | break :blk &payload.base; | ||
| 3052 | }, | ||
| 3053 | .single_mut_pointer => blk: { | ||
| 3054 | const payload = try scope.arena().create(Type.Payload.PointerSimple); | ||
| 3055 | payload.* = .{ | ||
| 3056 | .base = .{ .tag = .optional_single_mut_pointer }, | ||
| 3057 | .pointee_type = child_type.elemType(), | ||
| 3058 | }; | ||
| 3059 | break :blk &payload.base; | ||
| 3060 | }, | ||
| 3061 | else => blk: { | ||
| 3062 | const payload = try scope.arena().create(Type.Payload.Optional); | ||
| 3063 | payload.* = .{ | ||
| 3064 | .child_type = child_type, | ||
| 3065 | }; | ||
| 3066 | break :blk &payload.base; | ||
| 3067 | }, | ||
| 3068 | }); | ||
| 3069 | } | ||
| 3070 | |||
| 3071 | pub fn arrayType(self: *Module, scope: *Scope, len: u64, sentinel: ?Value, elem_type: Type) Allocator.Error!Type { | ||
| 3072 | if (elem_type.eql(Type.initTag(.u8))) { | ||
| 3073 | if (sentinel) |some| { | ||
| 3074 | if (some.eql(Value.initTag(.zero))) { | ||
| 3075 | const payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0); | ||
| 3076 | payload.* = .{ | ||
| 3077 | .len = len, | ||
| 3078 | }; | ||
| 3079 | return Type.initPayload(&payload.base); | ||
| 3080 | } | ||
| 3081 | } else { | ||
| 3082 | const payload = try scope.arena().create(Type.Payload.Array_u8); | ||
| 3083 | payload.* = .{ | ||
| 3084 | .len = len, | ||
| 3085 | }; | ||
| 3086 | return Type.initPayload(&payload.base); | ||
| 3087 | } | ||
| 3088 | } | ||
| 3089 | |||
| 3090 | if (sentinel) |some| { | ||
| 3091 | const payload = try scope.arena().create(Type.Payload.ArraySentinel); | ||
| 3092 | payload.* = .{ | ||
| 3093 | .len = len, | ||
| 3094 | .sentinel = some, | ||
| 3095 | .elem_type = elem_type, | ||
| 3096 | }; | ||
| 3097 | return Type.initPayload(&payload.base); | ||
| 3098 | } | ||
| 3099 | |||
| 3100 | const payload = try scope.arena().create(Type.Payload.Array); | ||
| 3101 | payload.* = .{ | ||
| 3102 | .len = len, | ||
| 3103 | .elem_type = elem_type, | ||
| 3104 | }; | ||
| 3105 | return Type.initPayload(&payload.base); | ||
| 3106 | } | ||
| 3107 | |||
| 3108 | pub fn errorUnionType(self: *Module, scope: *Scope, error_set: Type, payload: Type) Allocator.Error!Type { | ||
| 3109 | assert(error_set.zigTypeTag() == .ErrorSet); | ||
| 3110 | if (error_set.eql(Type.initTag(.anyerror)) and payload.eql(Type.initTag(.void))) { | ||
| 3111 | return Type.initTag(.anyerror_void_error_union); | ||
| 3112 | } | ||
| 3113 | |||
| 3114 | const result = try scope.arena().create(Type.Payload.ErrorUnion); | ||
| 3115 | result.* = .{ | ||
| 3116 | .error_set = error_set, | ||
| 3117 | .payload = payload, | ||
| 3118 | }; | ||
| 3119 | return Type.initPayload(&result.base); | ||
| 3120 | } | ||
| 3121 | |||
| 3122 | pub fn anyframeType(self: *Module, scope: *Scope, return_type: Type) Allocator.Error!Type { | ||
| 3123 | const result = try scope.arena().create(Type.Payload.AnyFrame); | ||
| 3124 | result.* = .{ | ||
| 3125 | .return_type = return_type, | ||
| 3126 | }; | ||
| 3127 | return Type.initPayload(&result.base); | ||
| 3128 | } | ||
| 3129 | |||
| 3130 | pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void { | ||
| 3131 | const zir_module = scope.namespace(); | ||
| 3132 | const source = zir_module.getSource(self) catch @panic("dumpInst failed to get source"); | ||
| 3133 | const loc = std.zig.findLineColumn(source, inst.src); | ||
| 3134 | if (inst.tag == .constant) { | ||
| 3135 | std.debug.print("constant ty={} val={} src={}:{}:{}\n", .{ | ||
| 3136 | inst.ty, | ||
| 3137 | inst.castTag(.constant).?.val, | ||
| 3138 | zir_module.subFilePath(), | ||
| 3139 | loc.line + 1, | ||
| 3140 | loc.column + 1, | ||
| 3141 | }); | ||
| 3142 | } else if (inst.deaths == 0) { | ||
| 3143 | std.debug.print("{} ty={} src={}:{}:{}\n", .{ | ||
| 3144 | @tagName(inst.tag), | ||
| 3145 | inst.ty, | ||
| 3146 | zir_module.subFilePath(), | ||
| 3147 | loc.line + 1, | ||
| 3148 | loc.column + 1, | ||
| 3149 | }); | ||
| 3150 | } else { | ||
| 3151 | std.debug.print("{} ty={} deaths={b} src={}:{}:{}\n", .{ | ||
| 3152 | @tagName(inst.tag), | ||
| 3153 | inst.ty, | ||
| 3154 | inst.deaths, | ||
| 3155 | zir_module.subFilePath(), | ||
| 3156 | loc.line + 1, | ||
| 3157 | loc.column + 1, | ||
| 3158 | }); | ||
| 3159 | } | ||
| 3160 | } | ||
| 3161 | |||
| 3162 | pub const PanicId = enum { | ||
| 3163 | unreach, | ||
| 3164 | unwrap_null, | ||
| 3165 | }; | ||
| 3166 | |||
| 3167 | pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic_id: PanicId) !void { | ||
| 3168 | const block_inst = try parent_block.arena.create(Inst.Block); | ||
| 3169 | block_inst.* = .{ | ||
| 3170 | .base = .{ | ||
| 3171 | .tag = Inst.Block.base_tag, | ||
| 3172 | .ty = Type.initTag(.void), | ||
| 3173 | .src = ok.src, | ||
| 3174 | }, | ||
| 3175 | .body = .{ | ||
| 3176 | .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the condbr. | ||
| 3177 | }, | ||
| 3178 | }; | ||
| 3179 | |||
| 3180 | const ok_body: ir.Body = .{ | ||
| 3181 | .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the brvoid. | ||
| 3182 | }; | ||
| 3183 | const brvoid = try parent_block.arena.create(Inst.BrVoid); | ||
| 3184 | brvoid.* = .{ | ||
| 3185 | .base = .{ | ||
| 3186 | .tag = .brvoid, | ||
| 3187 | .ty = Type.initTag(.noreturn), | ||
| 3188 | .src = ok.src, | ||
| 3189 | }, | ||
| 3190 | .block = block_inst, | ||
| 3191 | }; | ||
| 3192 | ok_body.instructions[0] = &brvoid.base; | ||
| 3193 | |||
| 3194 | var fail_block: Scope.Block = .{ | ||
| 3195 | .parent = parent_block, | ||
| 3196 | .func = parent_block.func, | ||
| 3197 | .decl = parent_block.decl, | ||
| 3198 | .instructions = .{}, | ||
| 3199 | .arena = parent_block.arena, | ||
| 3200 | .is_comptime = parent_block.is_comptime, | ||
| 3201 | }; | ||
| 3202 | defer fail_block.instructions.deinit(mod.gpa); | ||
| 3203 | |||
| 3204 | _ = try mod.safetyPanic(&fail_block, ok.src, panic_id); | ||
| 3205 | |||
| 3206 | const fail_body: ir.Body = .{ .instructions = try parent_block.arena.dupe(*Inst, fail_block.instructions.items) }; | ||
| 3207 | |||
| 3208 | const condbr = try parent_block.arena.create(Inst.CondBr); | ||
| 3209 | condbr.* = .{ | ||
| 3210 | .base = .{ | ||
| 3211 | .tag = .condbr, | ||
| 3212 | .ty = Type.initTag(.noreturn), | ||
| 3213 | .src = ok.src, | ||
| 3214 | }, | ||
| 3215 | .condition = ok, | ||
| 3216 | .then_body = ok_body, | ||
| 3217 | .else_body = fail_body, | ||
| 3218 | }; | ||
| 3219 | block_inst.body.instructions[0] = &condbr.base; | ||
| 3220 | |||
| 3221 | try parent_block.instructions.append(mod.gpa, &block_inst.base); | ||
| 3222 | } | ||
| 3223 | |||
| 3224 | pub fn safetyPanic(mod: *Module, block: *Scope.Block, src: usize, panic_id: PanicId) !*Inst { | ||
| 3225 | // TODO Once we have a panic function to call, call it here instead of breakpoint. | ||
| 3226 | _ = try mod.addNoOp(block, src, Type.initTag(.void), .breakpoint); | ||
| 3227 | return mod.addNoOp(block, src, Type.initTag(.noreturn), .unreach); | ||
| 3228 | } | ||
| 3229 | |||
| 3230 | pub fn getTarget(self: Module) Target { | ||
| 3231 | return self.comp.bin_file.options.target; | ||
| 3232 | } | ||
| 3233 | |||
| 3234 | pub fn optimizeMode(self: Module) std.builtin.Mode { | ||
| 3235 | return self.comp.bin_file.options.optimize_mode; | ||
| 3236 | } | ||
src-self-hosted/astgen.zig+1-1| ... | @@ -6,7 +6,7 @@ const Type = @import("type.zig").Type; | ... | @@ -6,7 +6,7 @@ const Type = @import("type.zig").Type; |
| 6 | const TypedValue = @import("TypedValue.zig"); | 6 | const TypedValue = @import("TypedValue.zig"); |
| 7 | const assert = std.debug.assert; | 7 | const assert = std.debug.assert; |
| 8 | const zir = @import("zir.zig"); | 8 | const zir = @import("zir.zig"); |
| 9 | const Module = @import("ZigModule.zig"); | 9 | const Module = @import("Module.zig"); |
| 10 | const ast = std.zig.ast; | 10 | const ast = std.zig.ast; |
| 11 | const trace = @import("tracy.zig").trace; | 11 | const trace = @import("tracy.zig").trace; |
| 12 | const Scope = Module.Scope; | 12 | const Scope = Module.Scope; |
src-self-hosted/codegen.zig+1-1| ... | @@ -7,7 +7,7 @@ const Type = @import("type.zig").Type; | ... | @@ -7,7 +7,7 @@ const Type = @import("type.zig").Type; |
| 7 | const Value = @import("value.zig").Value; | 7 | const Value = @import("value.zig").Value; |
| 8 | const TypedValue = @import("TypedValue.zig"); | 8 | const TypedValue = @import("TypedValue.zig"); |
| 9 | const link = @import("link.zig"); | 9 | const link = @import("link.zig"); |
| 10 | const Module = @import("ZigModule.zig"); | 10 | const Module = @import("Module.zig"); |
| 11 | const Compilation = @import("Compilation.zig"); | 11 | const Compilation = @import("Compilation.zig"); |
| 12 | const ErrorMsg = Compilation.ErrorMsg; | 12 | const ErrorMsg = Compilation.ErrorMsg; |
| 13 | const Target = std.Target; | 13 | const Target = std.Target; |
src-self-hosted/codegen/c.zig+1-1| ... | @@ -1,7 +1,7 @@ | ... | @@ -1,7 +1,7 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | const link = @import("../link.zig"); | 3 | const link = @import("../link.zig"); |
| 4 | const Module = @import("../ZigModule.zig"); | 4 | const Module = @import("../Module.zig"); |
| 5 | 5 | ||
| 6 | const Inst = @import("../ir.zig").Inst; | 6 | const Inst = @import("../ir.zig").Inst; |
| 7 | const Value = @import("../value.zig").Value; | 7 | const Value = @import("../value.zig").Value; |
src-self-hosted/codegen/wasm.zig+1-1| ... | @@ -5,7 +5,7 @@ const assert = std.debug.assert; | ... | @@ -5,7 +5,7 @@ const assert = std.debug.assert; |
| 5 | const leb = std.debug.leb; | 5 | const leb = std.debug.leb; |
| 6 | const mem = std.mem; | 6 | const mem = std.mem; |
| 7 | 7 | ||
| 8 | const Module = @import("../ZigModule.zig"); | 8 | const Module = @import("../Module.zig"); |
| 9 | const Decl = Module.Decl; | 9 | const Decl = Module.Decl; |
| 10 | const Inst = @import("../ir.zig").Inst; | 10 | const Inst = @import("../ir.zig").Inst; |
| 11 | const Type = @import("../type.zig").Type; | 11 | const Type = @import("../type.zig").Type; |
src-self-hosted/ir.zig+1-1| ... | @@ -1,7 +1,7 @@ | ... | @@ -1,7 +1,7 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const Value = @import("value.zig").Value; | 2 | const Value = @import("value.zig").Value; |
| 3 | const Type = @import("type.zig").Type; | 3 | const Type = @import("type.zig").Type; |
| 4 | const Module = @import("ZigModule.zig"); | 4 | const Module = @import("Module.zig"); |
| 5 | const assert = std.debug.assert; | 5 | const assert = std.debug.assert; |
| 6 | const codegen = @import("codegen.zig"); | 6 | const codegen = @import("codegen.zig"); |
| 7 | const ast = std.zig.ast; | 7 | const ast = std.zig.ast; |
src-self-hosted/link.zig+10-10| ... | @@ -1,7 +1,7 @@ | ... | @@ -1,7 +1,7 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const Allocator = std.mem.Allocator; | 2 | const Allocator = std.mem.Allocator; |
| 3 | const Compilation = @import("Compilation.zig"); | 3 | const Compilation = @import("Compilation.zig"); |
| 4 | const ZigModule = @import("ZigModule.zig"); | 4 | const Module = @import("Module.zig"); |
| 5 | const fs = std.fs; | 5 | const fs = std.fs; |
| 6 | const trace = @import("tracy.zig").trace; | 6 | const trace = @import("tracy.zig").trace; |
| 7 | const Package = @import("Package.zig"); | 7 | const Package = @import("Package.zig"); |
| ... | @@ -23,7 +23,7 @@ pub const Options = struct { | ... | @@ -23,7 +23,7 @@ pub const Options = struct { |
| 23 | optimize_mode: std.builtin.Mode, | 23 | optimize_mode: std.builtin.Mode, |
| 24 | root_name: []const u8, | 24 | root_name: []const u8, |
| 25 | /// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`. | 25 | /// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`. |
| 26 | zig_module: ?*ZigModule, | 26 | module: ?*Module, |
| 27 | dynamic_linker: ?[]const u8 = null, | 27 | dynamic_linker: ?[]const u8 = null, |
| 28 | /// Used for calculating how much space to reserve for symbols in case the binary file | 28 | /// Used for calculating how much space to reserve for symbols in case the binary file |
| 29 | /// does not already have a symbol table. | 29 | /// does not already have a symbol table. |
| ... | @@ -186,7 +186,7 @@ pub const File = struct { | ... | @@ -186,7 +186,7 @@ pub const File = struct { |
| 186 | 186 | ||
| 187 | /// May be called before or after updateDeclExports but must be called | 187 | /// May be called before or after updateDeclExports but must be called |
| 188 | /// after allocateDeclIndexes for any given Decl. | 188 | /// after allocateDeclIndexes for any given Decl. |
| 189 | pub fn updateDecl(base: *File, module: *ZigModule, decl: *ZigModule.Decl) !void { | 189 | pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void { |
| 190 | switch (base.tag) { | 190 | switch (base.tag) { |
| 191 | .coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl), | 191 | .coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl), |
| 192 | .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl), | 192 | .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl), |
| ... | @@ -196,7 +196,7 @@ pub const File = struct { | ... | @@ -196,7 +196,7 @@ pub const File = struct { |
| 196 | } | 196 | } |
| 197 | } | 197 | } |
| 198 | 198 | ||
| 199 | pub fn updateDeclLineNumber(base: *File, module: *ZigModule, decl: *ZigModule.Decl) !void { | 199 | pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) !void { |
| 200 | switch (base.tag) { | 200 | switch (base.tag) { |
| 201 | .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl), | 201 | .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl), |
| 202 | .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl), | 202 | .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl), |
| ... | @@ -207,7 +207,7 @@ pub const File = struct { | ... | @@ -207,7 +207,7 @@ pub const File = struct { |
| 207 | 207 | ||
| 208 | /// Must be called before any call to updateDecl or updateDeclExports for | 208 | /// Must be called before any call to updateDecl or updateDeclExports for |
| 209 | /// any given Decl. | 209 | /// any given Decl. |
| 210 | pub fn allocateDeclIndexes(base: *File, decl: *ZigModule.Decl) !void { | 210 | pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void { |
| 211 | switch (base.tag) { | 211 | switch (base.tag) { |
| 212 | .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl), | 212 | .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl), |
| 213 | .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl), | 213 | .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl), |
| ... | @@ -271,7 +271,7 @@ pub const File = struct { | ... | @@ -271,7 +271,7 @@ pub const File = struct { |
| 271 | }; | 271 | }; |
| 272 | } | 272 | } |
| 273 | 273 | ||
| 274 | pub fn freeDecl(base: *File, decl: *ZigModule.Decl) void { | 274 | pub fn freeDecl(base: *File, decl: *Module.Decl) void { |
| 275 | switch (base.tag) { | 275 | switch (base.tag) { |
| 276 | .coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl), | 276 | .coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl), |
| 277 | .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl), | 277 | .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl), |
| ... | @@ -295,9 +295,9 @@ pub const File = struct { | ... | @@ -295,9 +295,9 @@ pub const File = struct { |
| 295 | /// allocateDeclIndexes for any given Decl. | 295 | /// allocateDeclIndexes for any given Decl. |
| 296 | pub fn updateDeclExports( | 296 | pub fn updateDeclExports( |
| 297 | base: *File, | 297 | base: *File, |
| 298 | module: *ZigModule, | 298 | module: *Module, |
| 299 | decl: *const ZigModule.Decl, | 299 | decl: *const Module.Decl, |
| 300 | exports: []const *ZigModule.Export, | 300 | exports: []const *Module.Export, |
| 301 | ) !void { | 301 | ) !void { |
| 302 | switch (base.tag) { | 302 | switch (base.tag) { |
| 303 | .coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl, exports), | 303 | .coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl, exports), |
| ... | @@ -308,7 +308,7 @@ pub const File = struct { | ... | @@ -308,7 +308,7 @@ pub const File = struct { |
| 308 | } | 308 | } |
| 309 | } | 309 | } |
| 310 | 310 | ||
| 311 | pub fn getDeclVAddr(base: *File, decl: *const ZigModule.Decl) u64 { | 311 | pub fn getDeclVAddr(base: *File, decl: *const Module.Decl) u64 { |
| 312 | switch (base.tag) { | 312 | switch (base.tag) { |
| 313 | .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl), | 313 | .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl), |
| 314 | .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl), | 314 | .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl), |
src-self-hosted/link/C.zig+1-1| ... | @@ -2,7 +2,7 @@ const std = @import("std"); | ... | @@ -2,7 +2,7 @@ const std = @import("std"); |
| 2 | const mem = std.mem; | 2 | const mem = std.mem; |
| 3 | const assert = std.debug.assert; | 3 | const assert = std.debug.assert; |
| 4 | const Allocator = std.mem.Allocator; | 4 | const Allocator = std.mem.Allocator; |
| 5 | const Module = @import("../ZigModule.zig"); | 5 | const Module = @import("../Module.zig"); |
| 6 | const Compilation = @import("../Compilation.zig"); | 6 | const Compilation = @import("../Compilation.zig"); |
| 7 | const fs = std.fs; | 7 | const fs = std.fs; |
| 8 | const codegen = @import("../codegen/c.zig"); | 8 | const codegen = @import("../codegen/c.zig"); |
src-self-hosted/link/Coff.zig+1-1| ... | @@ -7,7 +7,7 @@ const assert = std.debug.assert; | ... | @@ -7,7 +7,7 @@ const assert = std.debug.assert; |
| 7 | const fs = std.fs; | 7 | const fs = std.fs; |
| 8 | 8 | ||
| 9 | const trace = @import("../tracy.zig").trace; | 9 | const trace = @import("../tracy.zig").trace; |
| 10 | const Module = @import("../ZigModule.zig"); | 10 | const Module = @import("../Module.zig"); |
| 11 | const Compilation = @import("../Compilation.zig"); | 11 | const Compilation = @import("../Compilation.zig"); |
| 12 | const codegen = @import("../codegen.zig"); | 12 | const codegen = @import("../codegen.zig"); |
| 13 | const link = @import("../link.zig"); | 13 | const link = @import("../link.zig"); |
src-self-hosted/link/Elf.zig+10-10| ... | @@ -3,7 +3,7 @@ const mem = std.mem; | ... | @@ -3,7 +3,7 @@ const mem = std.mem; |
| 3 | const assert = std.debug.assert; | 3 | const assert = std.debug.assert; |
| 4 | const Allocator = std.mem.Allocator; | 4 | const Allocator = std.mem.Allocator; |
| 5 | const ir = @import("../ir.zig"); | 5 | const ir = @import("../ir.zig"); |
| 6 | const Module = @import("../ZigModule.zig"); | 6 | const Module = @import("../Module.zig"); |
| 7 | const Compilation = @import("../Compilation.zig"); | 7 | const Compilation = @import("../Compilation.zig"); |
| 8 | const fs = std.fs; | 8 | const fs = std.fs; |
| 9 | const elf = std.elf; | 9 | const elf = std.elf; |
| ... | @@ -743,7 +743,7 @@ pub fn flush(self: *Elf, comp: *Compilation) !void { | ... | @@ -743,7 +743,7 @@ pub fn flush(self: *Elf, comp: *Compilation) !void { |
| 743 | fn flushInner(self: *Elf, comp: *Compilation) !void { | 743 | fn flushInner(self: *Elf, comp: *Compilation) !void { |
| 744 | // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the | 744 | // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the |
| 745 | // Zig source code. | 745 | // Zig source code. |
| 746 | const zig_module = self.base.options.zig_module orelse return error.LinkingWithoutZigSourceUnimplemented; | 746 | const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; |
| 747 | 747 | ||
| 748 | const target_endian = self.base.options.target.cpu.arch.endian(); | 748 | const target_endian = self.base.options.target.cpu.arch.endian(); |
| 749 | const foreign_endian = target_endian != std.Target.current.cpu.arch.endian(); | 749 | const foreign_endian = target_endian != std.Target.current.cpu.arch.endian(); |
| ... | @@ -866,8 +866,8 @@ fn flushInner(self: *Elf, comp: *Compilation) !void { | ... | @@ -866,8 +866,8 @@ fn flushInner(self: *Elf, comp: *Compilation) !void { |
| 866 | }, | 866 | }, |
| 867 | } | 867 | } |
| 868 | // Write the form for the compile unit, which must match the abbrev table above. | 868 | // Write the form for the compile unit, which must match the abbrev table above. |
| 869 | const name_strp = try self.makeDebugString(zig_module.root_pkg.root_src_path); | 869 | const name_strp = try self.makeDebugString(module.root_pkg.root_src_path); |
| 870 | const comp_dir_strp = try self.makeDebugString(zig_module.root_pkg.root_src_directory.path.?); | 870 | const comp_dir_strp = try self.makeDebugString(module.root_pkg.root_src_directory.path.?); |
| 871 | const producer_strp = try self.makeDebugString(link.producer_string); | 871 | const producer_strp = try self.makeDebugString(link.producer_string); |
| 872 | // Currently only one compilation unit is supported, so the address range is simply | 872 | // Currently only one compilation unit is supported, so the address range is simply |
| 873 | // identical to the main program header virtual address and memory size. | 873 | // identical to the main program header virtual address and memory size. |
| ... | @@ -1036,7 +1036,7 @@ fn flushInner(self: *Elf, comp: *Compilation) !void { | ... | @@ -1036,7 +1036,7 @@ fn flushInner(self: *Elf, comp: *Compilation) !void { |
| 1036 | 0, // include_directories (none except the compilation unit cwd) | 1036 | 0, // include_directories (none except the compilation unit cwd) |
| 1037 | }); | 1037 | }); |
| 1038 | // file_names[0] | 1038 | // file_names[0] |
| 1039 | di_buf.appendSliceAssumeCapacity(zig_module.root_pkg.root_src_path); // relative path name | 1039 | di_buf.appendSliceAssumeCapacity(module.root_pkg.root_src_path); // relative path name |
| 1040 | di_buf.appendSliceAssumeCapacity(&[_]u8{ | 1040 | di_buf.appendSliceAssumeCapacity(&[_]u8{ |
| 1041 | 0, // null byte for the relative path name | 1041 | 0, // null byte for the relative path name |
| 1042 | 0, // directory_index | 1042 | 0, // directory_index |
| ... | @@ -1230,7 +1230,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { | ... | @@ -1230,7 +1230,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1230 | 1230 | ||
| 1231 | // If there is no Zig code to compile, then we should skip flushing the output file because it | 1231 | // If there is no Zig code to compile, then we should skip flushing the output file because it |
| 1232 | // will not be part of the linker line anyway. | 1232 | // will not be part of the linker line anyway. |
| 1233 | const zig_module_obj_path: ?[]const u8 = if (self.base.options.zig_module) |module| blk: { | 1233 | const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { |
| 1234 | try self.flushInner(comp); | 1234 | try self.flushInner(comp); |
| 1235 | 1235 | ||
| 1236 | const obj_basename = self.base.intermediary_basename.?; | 1236 | const obj_basename = self.base.intermediary_basename.?; |
| ... | @@ -1270,7 +1270,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { | ... | @@ -1270,7 +1270,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1270 | .failure => return error.NotAllCSourceFilesAvailableToLink, | 1270 | .failure => return error.NotAllCSourceFilesAvailableToLink, |
| 1271 | .success => |success| _ = try ch.addFile(success.object_path, null), | 1271 | .success => |success| _ = try ch.addFile(success.object_path, null), |
| 1272 | }; | 1272 | }; |
| 1273 | try ch.addOptionalFile(zig_module_obj_path); | 1273 | try ch.addOptionalFile(module_obj_path); |
| 1274 | // We can skip hashing libc and libc++ components that we are in charge of building from Zig | 1274 | // We can skip hashing libc and libc++ components that we are in charge of building from Zig |
| 1275 | // installation sources because they are always a product of the compiler version + target information. | 1275 | // installation sources because they are always a product of the compiler version + target information. |
| 1276 | ch.hash.addOptional(self.base.options.stack_size_override); | 1276 | ch.hash.addOptional(self.base.options.stack_size_override); |
| ... | @@ -1500,7 +1500,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { | ... | @@ -1500,7 +1500,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1500 | .success => |success| try argv.append(success.object_path), | 1500 | .success => |success| try argv.append(success.object_path), |
| 1501 | }; | 1501 | }; |
| 1502 | 1502 | ||
| 1503 | if (zig_module_obj_path) |p| { | 1503 | if (module_obj_path) |p| { |
| 1504 | try argv.append(p); | 1504 | try argv.append(p); |
| 1505 | } | 1505 | } |
| 1506 | 1506 | ||
| ... | @@ -2837,8 +2837,8 @@ fn dbgLineNeededHeaderBytes(self: Elf) u32 { | ... | @@ -2837,8 +2837,8 @@ fn dbgLineNeededHeaderBytes(self: Elf) u32 { |
| 2837 | directory_count * 8 + file_name_count * 8 + | 2837 | directory_count * 8 + file_name_count * 8 + |
| 2838 | // These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like | 2838 | // These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like |
| 2839 | // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly. | 2839 | // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly. |
| 2840 | self.base.options.zig_module.?.root_pkg.root_src_directory.path.?.len + | 2840 | self.base.options.module.?.root_pkg.root_src_directory.path.?.len + |
| 2841 | self.base.options.zig_module.?.root_pkg.root_src_path.len); | 2841 | self.base.options.module.?.root_pkg.root_src_path.len); |
| 2842 | } | 2842 | } |
| 2843 | 2843 | ||
| 2844 | fn dbgInfoNeededHeaderBytes(self: Elf) u32 { | 2844 | fn dbgInfoNeededHeaderBytes(self: Elf) u32 { |
src-self-hosted/link/MachO.zig+1-1| ... | @@ -12,7 +12,7 @@ const mem = std.mem; | ... | @@ -12,7 +12,7 @@ const mem = std.mem; |
| 12 | const trace = @import("../tracy.zig").trace; | 12 | const trace = @import("../tracy.zig").trace; |
| 13 | const Type = @import("../type.zig").Type; | 13 | const Type = @import("../type.zig").Type; |
| 14 | 14 | ||
| 15 | const Module = @import("../ZigModule.zig"); | 15 | const Module = @import("../Module.zig"); |
| 16 | const Compilation = @import("../Compilation.zig"); | 16 | const Compilation = @import("../Compilation.zig"); |
| 17 | const link = @import("../link.zig"); | 17 | const link = @import("../link.zig"); |
| 18 | const File = link.File; | 18 | const File = link.File; |
src-self-hosted/link/Wasm.zig+2-2| ... | @@ -6,7 +6,7 @@ const assert = std.debug.assert; | ... | @@ -6,7 +6,7 @@ const assert = std.debug.assert; |
| 6 | const fs = std.fs; | 6 | const fs = std.fs; |
| 7 | const leb = std.debug.leb; | 7 | const leb = std.debug.leb; |
| 8 | 8 | ||
| 9 | const Module = @import("../ZigModule.zig"); | 9 | const Module = @import("../Module.zig"); |
| 10 | const Compilation = @import("../Compilation.zig"); | 10 | const Compilation = @import("../Compilation.zig"); |
| 11 | const codegen = @import("../codegen/wasm.zig"); | 11 | const codegen = @import("../codegen/wasm.zig"); |
| 12 | const link = @import("../link.zig"); | 12 | const link = @import("../link.zig"); |
| ... | @@ -165,7 +165,7 @@ pub fn flush(self: *Wasm, comp: *Compilation) !void { | ... | @@ -165,7 +165,7 @@ pub fn flush(self: *Wasm, comp: *Compilation) !void { |
| 165 | } | 165 | } |
| 166 | 166 | ||
| 167 | // Export section | 167 | // Export section |
| 168 | if (self.base.options.zig_module) |module| { | 168 | if (self.base.options.module) |module| { |
| 169 | const header_offset = try reserveVecSectionHeader(file); | 169 | const header_offset = try reserveVecSectionHeader(file); |
| 170 | const writer = file.writer(); | 170 | const writer = file.writer(); |
| 171 | var count: u32 = 0; | 171 | var count: u32 = 0; |
src-self-hosted/main.zig+2-2| ... | @@ -1232,9 +1232,9 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, zir_out_path: ?[]const u8) | ... | @@ -1232,9 +1232,9 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, zir_out_path: ?[]const u8) |
| 1232 | } | 1232 | } |
| 1233 | 1233 | ||
| 1234 | if (zir_out_path) |zop| { | 1234 | if (zir_out_path) |zop| { |
| 1235 | const zig_module = comp.bin_file.options.zig_module orelse | 1235 | const module = comp.bin_file.options.module orelse |
| 1236 | fatal("-femit-zir with no zig source code", .{}); | 1236 | fatal("-femit-zir with no zig source code", .{}); |
| 1237 | var new_zir_module = try zir.emit(gpa, zig_module); | 1237 | var new_zir_module = try zir.emit(gpa, module); |
| 1238 | defer new_zir_module.deinit(gpa); | 1238 | defer new_zir_module.deinit(gpa); |
| 1239 | 1239 | ||
| 1240 | const baf = try io.BufferedAtomicFile.create(gpa, fs.cwd(), zop, .{}); | 1240 | const baf = try io.BufferedAtomicFile.create(gpa, fs.cwd(), zop, .{}); |
src-self-hosted/test.zig+1-1| ... | @@ -549,7 +549,7 @@ pub const TestContext = struct { | ... | @@ -549,7 +549,7 @@ pub const TestContext = struct { |
| 549 | update_node.estimated_total_items = 5; | 549 | update_node.estimated_total_items = 5; |
| 550 | var emit_node = update_node.start("emit", null); | 550 | var emit_node = update_node.start("emit", null); |
| 551 | emit_node.activate(); | 551 | emit_node.activate(); |
| 552 | var new_zir_module = try zir.emit(allocator, comp.bin_file.options.zig_module.?); | 552 | var new_zir_module = try zir.emit(allocator, comp.bin_file.options.module.?); |
| 553 | defer new_zir_module.deinit(allocator); | 553 | defer new_zir_module.deinit(allocator); |
| 554 | emit_node.end(); | 554 | emit_node.end(); |
| 555 | 555 |
src-self-hosted/type.zig+1-1| ... | @@ -3,7 +3,7 @@ const Value = @import("value.zig").Value; | ... | @@ -3,7 +3,7 @@ const Value = @import("value.zig").Value; |
| 3 | const assert = std.debug.assert; | 3 | const assert = std.debug.assert; |
| 4 | const Allocator = std.mem.Allocator; | 4 | const Allocator = std.mem.Allocator; |
| 5 | const Target = std.Target; | 5 | const Target = std.Target; |
| 6 | const Module = @import("ZigModule.zig"); | 6 | const Module = @import("Module.zig"); |
| 7 | 7 | ||
| 8 | /// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication. | 8 | /// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication. |
| 9 | /// It's important for this type to be small. | 9 | /// It's important for this type to be small. |
src-self-hosted/value.zig+1-1| ... | @@ -6,7 +6,7 @@ const BigIntConst = std.math.big.int.Const; | ... | @@ -6,7 +6,7 @@ const BigIntConst = std.math.big.int.Const; |
| 6 | const BigIntMutable = std.math.big.int.Mutable; | 6 | const BigIntMutable = std.math.big.int.Mutable; |
| 7 | const Target = std.Target; | 7 | const Target = std.Target; |
| 8 | const Allocator = std.mem.Allocator; | 8 | const Allocator = std.mem.Allocator; |
| 9 | const Module = @import("ZigModule.zig"); | 9 | const Module = @import("Module.zig"); |
| 10 | 10 | ||
| 11 | /// This is the raw data, with no bookkeeping, no memory awareness, | 11 | /// This is the raw data, with no bookkeeping, no memory awareness, |
| 12 | /// no de-duplication, and no type system awareness. | 12 | /// no de-duplication, and no type system awareness. |
src-self-hosted/zir.zig+1-1| ... | @@ -10,7 +10,7 @@ const Type = @import("type.zig").Type; | ... | @@ -10,7 +10,7 @@ const Type = @import("type.zig").Type; |
| 10 | const Value = @import("value.zig").Value; | 10 | const Value = @import("value.zig").Value; |
| 11 | const TypedValue = @import("TypedValue.zig"); | 11 | const TypedValue = @import("TypedValue.zig"); |
| 12 | const ir = @import("ir.zig"); | 12 | const ir = @import("ir.zig"); |
| 13 | const IrModule = @import("ZigModule.zig"); | 13 | const IrModule = @import("Module.zig"); |
| 14 | 14 | ||
| 15 | /// This struct is relevent only for the ZIR Module text format. It is not used for | 15 | /// This struct is relevent only for the ZIR Module text format. It is not used for |
| 16 | /// semantic analysis of Zig source code. | 16 | /// semantic analysis of Zig source code. |
src-self-hosted/zir_sema.zig+1-1| ... | @@ -16,7 +16,7 @@ const TypedValue = @import("TypedValue.zig"); | ... | @@ -16,7 +16,7 @@ const TypedValue = @import("TypedValue.zig"); |
| 16 | const assert = std.debug.assert; | 16 | const assert = std.debug.assert; |
| 17 | const ir = @import("ir.zig"); | 17 | const ir = @import("ir.zig"); |
| 18 | const zir = @import("zir.zig"); | 18 | const zir = @import("zir.zig"); |
| 19 | const Module = @import("ZigModule.zig"); | 19 | const Module = @import("Module.zig"); |
| 20 | const Inst = ir.Inst; | 20 | const Inst = ir.Inst; |
| 21 | const Body = ir.Body; | 21 | const Body = ir.Body; |
| 22 | const trace = @import("tracy.zig").trace; | 22 | const trace = @import("tracy.zig").trace; |