| author | |
| committer | |
| log | 55ba335e0ffc2af76bf0743d98f5a959ccce0409 |
| tree | f9f705b5f33d0d54c52cdee9e38e40424ab9fd52 |
| parent | 2ee3cc453c4cefa3519f6a6238d4721364d829ae |
Introduce `Module.ensureFuncBodyAnalyzed` and corresponding `Sema`
function. This mirrors `ensureDeclAnalyzed` except also waits until the
function body has been semantically analyzed, meaning that inferred
error sets will have been populated.
Resolving error sets can now emit a "unable to resolve inferred error
set" error instead of producing an incorrect error set type. Resolving
error sets now calls `ensureFuncBodyAnalyzed`. Closes #11046.
`coerceInMemoryAllowedErrorSets` now does a lot more work to avoid
resolving an inferred error set if possible. Same with
`wrapErrorUnionSet`.
Inferred error set types no longer check the `func` field to determine if
they are equal. That was incorrect because an inline or comptime function
call produces a unique error set which has the same `*Module.Fn` value for
this field. Instead we use the `*Module.Fn.InferredErrorSet` pointers to
test equality of inferred error sets.8 files changed, 271 insertions(+), 195 deletions(-)
src/Compilation.zig+28-106| ... | @@ -15,7 +15,6 @@ const Package = @import("Package.zig"); | ... | @@ -15,7 +15,6 @@ const Package = @import("Package.zig"); |
| 15 | const link = @import("link.zig"); | 15 | const link = @import("link.zig"); |
| 16 | const tracy = @import("tracy.zig"); | 16 | const tracy = @import("tracy.zig"); |
| 17 | const trace = tracy.trace; | 17 | const trace = tracy.trace; |
| 18 | const Liveness = @import("Liveness.zig"); | ||
| 19 | const build_options = @import("build_options"); | 18 | const build_options = @import("build_options"); |
| 20 | const LibCInstallation = @import("libc_installation.zig").LibCInstallation; | 19 | const LibCInstallation = @import("libc_installation.zig").LibCInstallation; |
| 21 | const glibc = @import("glibc.zig"); | 20 | const glibc = @import("glibc.zig"); |
| ... | @@ -2702,12 +2701,12 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress | ... | @@ -2702,12 +2701,12 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress |
| 2702 | => return, | 2701 | => return, |
| 2703 | 2702 | ||
| 2704 | .complete, .codegen_failure_retryable => { | 2703 | .complete, .codegen_failure_retryable => { |
| 2705 | const named_frame = tracy.namedFrame("codegen_decl"); | ||
| 2706 | defer named_frame.end(); | ||
| 2707 | |||
| 2708 | if (build_options.omit_stage2) | 2704 | if (build_options.omit_stage2) |
| 2709 | @panic("sadly stage2 is omitted from this build to save memory on the CI server"); | 2705 | @panic("sadly stage2 is omitted from this build to save memory on the CI server"); |
| 2710 | 2706 | ||
| 2707 | const named_frame = tracy.namedFrame("codegen_decl"); | ||
| 2708 | defer named_frame.end(); | ||
| 2709 | |||
| 2711 | const module = comp.bin_file.options.module.?; | 2710 | const module = comp.bin_file.options.module.?; |
| 2712 | assert(decl.has_tv); | 2711 | assert(decl.has_tv); |
| 2713 | 2712 | ||
| ... | @@ -2722,100 +2721,18 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress | ... | @@ -2722,100 +2721,18 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress |
| 2722 | return; | 2721 | return; |
| 2723 | }, | 2722 | }, |
| 2724 | }, | 2723 | }, |
| 2725 | .codegen_func => |func| switch (func.owner_decl.analysis) { | 2724 | .codegen_func => |func| { |
| 2726 | .unreferenced => unreachable, | 2725 | if (build_options.omit_stage2) |
| 2727 | .in_progress => unreachable, | 2726 | @panic("sadly stage2 is omitted from this build to save memory on the CI server"); |
| 2728 | .outdated => unreachable, | ||
| 2729 | |||
| 2730 | .file_failure, | ||
| 2731 | .sema_failure, | ||
| 2732 | .codegen_failure, | ||
| 2733 | .dependency_failure, | ||
| 2734 | .sema_failure_retryable, | ||
| 2735 | => return, | ||
| 2736 | |||
| 2737 | .complete, .codegen_failure_retryable => { | ||
| 2738 | if (build_options.omit_stage2) | ||
| 2739 | @panic("sadly stage2 is omitted from this build to save memory on the CI server"); | ||
| 2740 | switch (func.state) { | ||
| 2741 | .sema_failure, .dependency_failure => return, | ||
| 2742 | .queued => {}, | ||
| 2743 | .in_progress => unreachable, | ||
| 2744 | .inline_only => unreachable, // don't queue work for this | ||
| 2745 | .success => unreachable, // don't queue it twice | ||
| 2746 | } | ||
| 2747 | |||
| 2748 | const gpa = comp.gpa; | ||
| 2749 | const module = comp.bin_file.options.module.?; | ||
| 2750 | const decl = func.owner_decl; | ||
| 2751 | |||
| 2752 | var tmp_arena = std.heap.ArenaAllocator.init(gpa); | ||
| 2753 | defer tmp_arena.deinit(); | ||
| 2754 | const sema_arena = tmp_arena.allocator(); | ||
| 2755 | |||
| 2756 | const sema_frame = tracy.namedFrame("sema"); | ||
| 2757 | var sema_frame_ended = false; | ||
| 2758 | errdefer if (!sema_frame_ended) sema_frame.end(); | ||
| 2759 | |||
| 2760 | var air = module.analyzeFnBody(decl, func, sema_arena) catch |err| switch (err) { | ||
| 2761 | error.AnalysisFail => { | ||
| 2762 | if (func.state == .in_progress) { | ||
| 2763 | // If this decl caused the compile error, the analysis field would | ||
| 2764 | // be changed to indicate it was this Decl's fault. Because this | ||
| 2765 | // did not happen, we infer here that it was a dependency failure. | ||
| 2766 | func.state = .dependency_failure; | ||
| 2767 | } | ||
| 2768 | return; | ||
| 2769 | }, | ||
| 2770 | error.OutOfMemory => return error.OutOfMemory, | ||
| 2771 | }; | ||
| 2772 | defer air.deinit(gpa); | ||
| 2773 | |||
| 2774 | sema_frame.end(); | ||
| 2775 | sema_frame_ended = true; | ||
| 2776 | |||
| 2777 | if (comp.bin_file.options.emit == null) return; | ||
| 2778 | |||
| 2779 | const liveness_frame = tracy.namedFrame("liveness"); | ||
| 2780 | var liveness_frame_ended = false; | ||
| 2781 | errdefer if (!liveness_frame_ended) liveness_frame.end(); | ||
| 2782 | |||
| 2783 | log.debug("analyze liveness of {s}", .{decl.name}); | ||
| 2784 | var liveness = try Liveness.analyze(gpa, air); | ||
| 2785 | defer liveness.deinit(gpa); | ||
| 2786 | |||
| 2787 | liveness_frame.end(); | ||
| 2788 | liveness_frame_ended = true; | ||
| 2789 | |||
| 2790 | if (builtin.mode == .Debug and comp.verbose_air) { | ||
| 2791 | std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name}); | ||
| 2792 | @import("print_air.zig").dump(gpa, air, liveness); | ||
| 2793 | std.debug.print("# End Function AIR: {s}\n\n", .{decl.name}); | ||
| 2794 | } | ||
| 2795 | 2727 | ||
| 2796 | const named_frame = tracy.namedFrame("codegen"); | 2728 | const named_frame = tracy.namedFrame("codegen_func"); |
| 2797 | defer named_frame.end(); | 2729 | defer named_frame.end(); |
| 2798 | 2730 | ||
| 2799 | comp.bin_file.updateFunc(module, func, air, liveness) catch |err| switch (err) { | 2731 | const module = comp.bin_file.options.module.?; |
| 2800 | error.OutOfMemory => return error.OutOfMemory, | 2732 | module.ensureFuncBodyAnalyzed(func) catch |err| switch (err) { |
| 2801 | error.AnalysisFail => { | 2733 | error.OutOfMemory => return error.OutOfMemory, |
| 2802 | decl.analysis = .codegen_failure; | 2734 | error.AnalysisFail => return, |
| 2803 | return; | 2735 | }; |
| 2804 | }, | ||
| 2805 | else => { | ||
| 2806 | try module.failed_decls.ensureUnusedCapacity(gpa, 1); | ||
| 2807 | module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create( | ||
| 2808 | gpa, | ||
| 2809 | decl.srcLoc(), | ||
| 2810 | "unable to codegen: {s}", | ||
| 2811 | .{@errorName(err)}, | ||
| 2812 | )); | ||
| 2813 | decl.analysis = .codegen_failure_retryable; | ||
| 2814 | return; | ||
| 2815 | }, | ||
| 2816 | }; | ||
| 2817 | return; | ||
| 2818 | }, | ||
| 2819 | }, | 2736 | }, |
| 2820 | .emit_h_decl => |decl| switch (decl.analysis) { | 2737 | .emit_h_decl => |decl| switch (decl.analysis) { |
| 2821 | .unreferenced => unreachable, | 2738 | .unreferenced => unreachable, |
| ... | @@ -2831,11 +2748,12 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress | ... | @@ -2831,11 +2748,12 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress |
| 2831 | // emit-h only requires semantic analysis of the Decl to be complete, | 2748 | // emit-h only requires semantic analysis of the Decl to be complete, |
| 2832 | // it does not depend on machine code generation to succeed. | 2749 | // it does not depend on machine code generation to succeed. |
| 2833 | .codegen_failure, .codegen_failure_retryable, .complete => { | 2750 | .codegen_failure, .codegen_failure_retryable, .complete => { |
| 2751 | if (build_options.omit_stage2) | ||
| 2752 | @panic("sadly stage2 is omitted from this build to save memory on the CI server"); | ||
| 2753 | |||
| 2834 | const named_frame = tracy.namedFrame("emit_h_decl"); | 2754 | const named_frame = tracy.namedFrame("emit_h_decl"); |
| 2835 | defer named_frame.end(); | 2755 | defer named_frame.end(); |
| 2836 | 2756 | ||
| 2837 | if (build_options.omit_stage2) | ||
| 2838 | @panic("sadly stage2 is omitted from this build to save memory on the CI server"); | ||
| 2839 | const gpa = comp.gpa; | 2757 | const gpa = comp.gpa; |
| 2840 | const module = comp.bin_file.options.module.?; | 2758 | const module = comp.bin_file.options.module.?; |
| 2841 | const emit_h = module.emit_h.?; | 2759 | const emit_h = module.emit_h.?; |
| ... | @@ -2871,11 +2789,12 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress | ... | @@ -2871,11 +2789,12 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress |
| 2871 | }, | 2789 | }, |
| 2872 | }, | 2790 | }, |
| 2873 | .analyze_decl => |decl| { | 2791 | .analyze_decl => |decl| { |
| 2792 | if (build_options.omit_stage2) | ||
| 2793 | @panic("sadly stage2 is omitted from this build to save memory on the CI server"); | ||
| 2794 | |||
| 2874 | const named_frame = tracy.namedFrame("analyze_decl"); | 2795 | const named_frame = tracy.namedFrame("analyze_decl"); |
| 2875 | defer named_frame.end(); | 2796 | defer named_frame.end(); |
| 2876 | 2797 | ||
| 2877 | if (build_options.omit_stage2) | ||
| 2878 | @panic("sadly stage2 is omitted from this build to save memory on the CI server"); | ||
| 2879 | const module = comp.bin_file.options.module.?; | 2798 | const module = comp.bin_file.options.module.?; |
| 2880 | module.ensureDeclAnalyzed(decl) catch |err| switch (err) { | 2799 | module.ensureDeclAnalyzed(decl) catch |err| switch (err) { |
| 2881 | error.OutOfMemory => return error.OutOfMemory, | 2800 | error.OutOfMemory => return error.OutOfMemory, |
| ... | @@ -2883,11 +2802,12 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress | ... | @@ -2883,11 +2802,12 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress |
| 2883 | }; | 2802 | }; |
| 2884 | }, | 2803 | }, |
| 2885 | .update_embed_file => |embed_file| { | 2804 | .update_embed_file => |embed_file| { |
| 2805 | if (build_options.omit_stage2) | ||
| 2806 | @panic("sadly stage2 is omitted from this build to save memory on the CI server"); | ||
| 2807 | |||
| 2886 | const named_frame = tracy.namedFrame("update_embed_file"); | 2808 | const named_frame = tracy.namedFrame("update_embed_file"); |
| 2887 | defer named_frame.end(); | 2809 | defer named_frame.end(); |
| 2888 | 2810 | ||
| 2889 | if (build_options.omit_stage2) | ||
| 2890 | @panic("sadly stage2 is omitted from this build to save memory on the CI server"); | ||
| 2891 | const module = comp.bin_file.options.module.?; | 2811 | const module = comp.bin_file.options.module.?; |
| 2892 | module.updateEmbedFile(embed_file) catch |err| switch (err) { | 2812 | module.updateEmbedFile(embed_file) catch |err| switch (err) { |
| 2893 | error.OutOfMemory => return error.OutOfMemory, | 2813 | error.OutOfMemory => return error.OutOfMemory, |
| ... | @@ -2895,11 +2815,12 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress | ... | @@ -2895,11 +2815,12 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress |
| 2895 | }; | 2815 | }; |
| 2896 | }, | 2816 | }, |
| 2897 | .update_line_number => |decl| { | 2817 | .update_line_number => |decl| { |
| 2818 | if (build_options.omit_stage2) | ||
| 2819 | @panic("sadly stage2 is omitted from this build to save memory on the CI server"); | ||
| 2820 | |||
| 2898 | const named_frame = tracy.namedFrame("update_line_number"); | 2821 | const named_frame = tracy.namedFrame("update_line_number"); |
| 2899 | defer named_frame.end(); | 2822 | defer named_frame.end(); |
| 2900 | 2823 | ||
| 2901 | if (build_options.omit_stage2) | ||
| 2902 | @panic("sadly stage2 is omitted from this build to save memory on the CI server"); | ||
| 2903 | const gpa = comp.gpa; | 2824 | const gpa = comp.gpa; |
| 2904 | const module = comp.bin_file.options.module.?; | 2825 | const module = comp.bin_file.options.module.?; |
| 2905 | comp.bin_file.updateDeclLineNumber(module, decl) catch |err| { | 2826 | comp.bin_file.updateDeclLineNumber(module, decl) catch |err| { |
| ... | @@ -2914,11 +2835,12 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress | ... | @@ -2914,11 +2835,12 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress |
| 2914 | }; | 2835 | }; |
| 2915 | }, | 2836 | }, |
| 2916 | .analyze_pkg => |pkg| { | 2837 | .analyze_pkg => |pkg| { |
| 2838 | if (build_options.omit_stage2) | ||
| 2839 | @panic("sadly stage2 is omitted from this build to save memory on the CI server"); | ||
| 2840 | |||
| 2917 | const named_frame = tracy.namedFrame("analyze_pkg"); | 2841 | const named_frame = tracy.namedFrame("analyze_pkg"); |
| 2918 | defer named_frame.end(); | 2842 | defer named_frame.end(); |
| 2919 | 2843 | ||
| 2920 | if (build_options.omit_stage2) | ||
| 2921 | @panic("sadly stage2 is omitted from this build to save memory on the CI server"); | ||
| 2922 | const module = comp.bin_file.options.module.?; | 2844 | const module = comp.bin_file.options.module.?; |
| 2923 | module.semaPkg(pkg) catch |err| switch (err) { | 2845 | module.semaPkg(pkg) catch |err| switch (err) { |
| 2924 | error.CurrentWorkingDirectoryUnlinked, | 2846 | error.CurrentWorkingDirectoryUnlinked, |
src/Module.zig+106-15| ... | @@ -3,6 +3,7 @@ | ... | @@ -3,6 +3,7 @@ |
| 3 | //! there is or is not any zig source code, respectively. | 3 | //! there is or is not any zig source code, respectively. |
| 4 | 4 | ||
| 5 | const std = @import("std"); | 5 | const std = @import("std"); |
| 6 | const builtin = @import("builtin"); | ||
| 6 | const mem = std.mem; | 7 | const mem = std.mem; |
| 7 | const Allocator = std.mem.Allocator; | 8 | const Allocator = std.mem.Allocator; |
| 8 | const ArrayListUnmanaged = std.ArrayListUnmanaged; | 9 | const ArrayListUnmanaged = std.ArrayListUnmanaged; |
| ... | @@ -28,6 +29,7 @@ const AstGen = @import("AstGen.zig"); | ... | @@ -28,6 +29,7 @@ const AstGen = @import("AstGen.zig"); |
| 28 | const Sema = @import("Sema.zig"); | 29 | const Sema = @import("Sema.zig"); |
| 29 | const target_util = @import("target.zig"); | 30 | const target_util = @import("target.zig"); |
| 30 | const build_options = @import("build_options"); | 31 | const build_options = @import("build_options"); |
| 32 | const Liveness = @import("Liveness.zig"); | ||
| 31 | 33 | ||
| 32 | /// General-purpose allocator. Used for both temporary and long-term storage. | 34 | /// General-purpose allocator. Used for both temporary and long-term storage. |
| 33 | gpa: Allocator, | 35 | gpa: Allocator, |
| ... | @@ -1438,8 +1440,11 @@ pub const Fn = struct { | ... | @@ -1438,8 +1440,11 @@ pub const Fn = struct { |
| 1438 | is_cold: bool = false, | 1440 | is_cold: bool = false, |
| 1439 | is_noinline: bool = false, | 1441 | is_noinline: bool = false, |
| 1440 | 1442 | ||
| 1441 | /// Any inferred error sets that this function owns, both it's own inferred error set and | 1443 | /// Any inferred error sets that this function owns, both its own inferred error set and |
| 1442 | /// inferred error sets of any inline/comptime functions called. | 1444 | /// inferred error sets of any inline/comptime functions called. Not to be confused |
| 1445 | /// with inferred error sets of generic instantiations of this function, which are | ||
| 1446 | /// *not* tracked here - they are tracked in the new `Fn` object created for the | ||
| 1447 | /// instantiations. | ||
| 1443 | inferred_error_sets: InferredErrorSetList = .{}, | 1448 | inferred_error_sets: InferredErrorSetList = .{}, |
| 1444 | 1449 | ||
| 1445 | pub const Analysis = enum { | 1450 | pub const Analysis = enum { |
| ... | @@ -1457,28 +1462,29 @@ pub const Fn = struct { | ... | @@ -1457,28 +1462,29 @@ pub const Fn = struct { |
| 1457 | }; | 1462 | }; |
| 1458 | 1463 | ||
| 1459 | /// This struct is used to keep track of any dependencies related to functions instances | 1464 | /// This struct is used to keep track of any dependencies related to functions instances |
| 1460 | /// that return inferred error sets. Note that a function may be associated to multiple different error sets, | 1465 | /// that return inferred error sets. Note that a function may be associated to |
| 1461 | /// for example an inferred error set which this function returns, but also any inferred error sets | 1466 | /// multiple different error sets, for example an inferred error set which |
| 1462 | /// of called inline or comptime functions. | 1467 | /// this function returns, but also any inferred error sets of called inline |
| 1468 | /// or comptime functions. | ||
| 1463 | pub const InferredErrorSet = struct { | 1469 | pub const InferredErrorSet = struct { |
| 1464 | /// The function from which this error set originates. | 1470 | /// The function from which this error set originates. |
| 1465 | /// Note: may be the function itself. | ||
| 1466 | func: *Fn, | 1471 | func: *Fn, |
| 1467 | 1472 | ||
| 1468 | /// All currently known errors that this error set contains. This includes direct additions | 1473 | /// All currently known errors that this error set contains. This includes |
| 1469 | /// via `return error.Foo;`, and possibly also errors that are returned from any dependent functions. | 1474 | /// direct additions via `return error.Foo;`, and possibly also errors that |
| 1470 | /// When the inferred error set is fully resolved, this map contains all the errors that the function might return. | 1475 | /// are returned from any dependent functions. When the inferred error set is |
| 1476 | /// fully resolved, this map contains all the errors that the function might return. | ||
| 1471 | errors: ErrorSet.NameMap = .{}, | 1477 | errors: ErrorSet.NameMap = .{}, |
| 1472 | 1478 | ||
| 1473 | /// Other inferred error sets which this inferred error set should include. | 1479 | /// Other inferred error sets which this inferred error set should include. |
| 1474 | inferred_error_sets: std.AutoHashMapUnmanaged(*InferredErrorSet, void) = .{}, | 1480 | inferred_error_sets: std.AutoHashMapUnmanaged(*InferredErrorSet, void) = .{}, |
| 1475 | 1481 | ||
| 1476 | /// Whether the function returned anyerror. This is true if either of the dependent functions | 1482 | /// Whether the function returned anyerror. This is true if either of |
| 1477 | /// returns anyerror. | 1483 | /// the dependent functions returns anyerror. |
| 1478 | is_anyerror: bool = false, | 1484 | is_anyerror: bool = false, |
| 1479 | 1485 | ||
| 1480 | /// Whether this error set is already fully resolved. If true, resolving can skip resolving any dependents | 1486 | /// Whether this error set is already fully resolved. If true, resolving |
| 1481 | /// of this inferred error set. | 1487 | /// can skip resolving any dependents of this inferred error set. |
| 1482 | is_resolved: bool = false, | 1488 | is_resolved: bool = false, |
| 1483 | 1489 | ||
| 1484 | pub fn addErrorSet(self: *InferredErrorSet, gpa: Allocator, err_set_ty: Type) !void { | 1490 | pub fn addErrorSet(self: *InferredErrorSet, gpa: Allocator, err_set_ty: Type) !void { |
| ... | @@ -1494,8 +1500,8 @@ pub const Fn = struct { | ... | @@ -1494,8 +1500,8 @@ pub const Fn = struct { |
| 1494 | try self.errors.put(gpa, name, {}); | 1500 | try self.errors.put(gpa, name, {}); |
| 1495 | }, | 1501 | }, |
| 1496 | .error_set_inferred => { | 1502 | .error_set_inferred => { |
| 1497 | const set = err_set_ty.castTag(.error_set_inferred).?.data; | 1503 | const ies = err_set_ty.castTag(.error_set_inferred).?.data; |
| 1498 | try self.inferred_error_sets.put(gpa, set, {}); | 1504 | try self.inferred_error_sets.put(gpa, ies, {}); |
| 1499 | }, | 1505 | }, |
| 1500 | .error_set_merged => { | 1506 | .error_set_merged => { |
| 1501 | const names = err_set_ty.castTag(.error_set_merged).?.data.keys(); | 1507 | const names = err_set_ty.castTag(.error_set_merged).?.data.keys(); |
| ... | @@ -3441,6 +3447,10 @@ pub fn mapOldZirToNew( | ... | @@ -3441,6 +3447,10 @@ pub fn mapOldZirToNew( |
| 3441 | } | 3447 | } |
| 3442 | } | 3448 | } |
| 3443 | 3449 | ||
| 3450 | /// This ensures that the Decl will have a Type and Value populated. | ||
| 3451 | /// However the resolution status of the Type may not be fully resolved. | ||
| 3452 | /// For example an inferred error set is not resolved until after `analyzeFnBody`. | ||
| 3453 | /// is called. | ||
| 3444 | pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void { | 3454 | pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void { |
| 3445 | const tracy = trace(@src()); | 3455 | const tracy = trace(@src()); |
| 3446 | defer tracy.end(); | 3456 | defer tracy.end(); |
| ... | @@ -3533,6 +3543,87 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void { | ... | @@ -3533,6 +3543,87 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void { |
| 3533 | } | 3543 | } |
| 3534 | } | 3544 | } |
| 3535 | 3545 | ||
| 3546 | pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void { | ||
| 3547 | const tracy = trace(@src()); | ||
| 3548 | defer tracy.end(); | ||
| 3549 | |||
| 3550 | switch (func.owner_decl.analysis) { | ||
| 3551 | .unreferenced => unreachable, | ||
| 3552 | .in_progress => unreachable, | ||
| 3553 | .outdated => unreachable, | ||
| 3554 | |||
| 3555 | .file_failure, | ||
| 3556 | .sema_failure, | ||
| 3557 | .codegen_failure, | ||
| 3558 | .dependency_failure, | ||
| 3559 | .sema_failure_retryable, | ||
| 3560 | => return error.AnalysisFail, | ||
| 3561 | |||
| 3562 | .complete, .codegen_failure_retryable => { | ||
| 3563 | switch (func.state) { | ||
| 3564 | .sema_failure, .dependency_failure => return error.AnalysisFail, | ||
| 3565 | .queued => {}, | ||
| 3566 | .in_progress => unreachable, | ||
| 3567 | .inline_only => unreachable, // don't queue work for this | ||
| 3568 | .success => return, | ||
| 3569 | } | ||
| 3570 | |||
| 3571 | const gpa = mod.gpa; | ||
| 3572 | const decl = func.owner_decl; | ||
| 3573 | |||
| 3574 | var tmp_arena = std.heap.ArenaAllocator.init(gpa); | ||
| 3575 | defer tmp_arena.deinit(); | ||
| 3576 | const sema_arena = tmp_arena.allocator(); | ||
| 3577 | |||
| 3578 | var air = mod.analyzeFnBody(decl, func, sema_arena) catch |err| switch (err) { | ||
| 3579 | error.AnalysisFail => { | ||
| 3580 | if (func.state == .in_progress) { | ||
| 3581 | // If this decl caused the compile error, the analysis field would | ||
| 3582 | // be changed to indicate it was this Decl's fault. Because this | ||
| 3583 | // did not happen, we infer here that it was a dependency failure. | ||
| 3584 | func.state = .dependency_failure; | ||
| 3585 | } | ||
| 3586 | return error.AnalysisFail; | ||
| 3587 | }, | ||
| 3588 | error.OutOfMemory => return error.OutOfMemory, | ||
| 3589 | }; | ||
| 3590 | defer air.deinit(gpa); | ||
| 3591 | |||
| 3592 | if (mod.comp.bin_file.options.emit == null) return; | ||
| 3593 | |||
| 3594 | log.debug("analyze liveness of {s}", .{decl.name}); | ||
| 3595 | var liveness = try Liveness.analyze(gpa, air); | ||
| 3596 | defer liveness.deinit(gpa); | ||
| 3597 | |||
| 3598 | if (builtin.mode == .Debug and mod.comp.verbose_air) { | ||
| 3599 | std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name}); | ||
| 3600 | @import("print_air.zig").dump(gpa, air, liveness); | ||
| 3601 | std.debug.print("# End Function AIR: {s}\n\n", .{decl.name}); | ||
| 3602 | } | ||
| 3603 | |||
| 3604 | mod.comp.bin_file.updateFunc(mod, func, air, liveness) catch |err| switch (err) { | ||
| 3605 | error.OutOfMemory => return error.OutOfMemory, | ||
| 3606 | error.AnalysisFail => { | ||
| 3607 | decl.analysis = .codegen_failure; | ||
| 3608 | return; | ||
| 3609 | }, | ||
| 3610 | else => { | ||
| 3611 | try mod.failed_decls.ensureUnusedCapacity(gpa, 1); | ||
| 3612 | mod.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create( | ||
| 3613 | gpa, | ||
| 3614 | decl.srcLoc(), | ||
| 3615 | "unable to codegen: {s}", | ||
| 3616 | .{@errorName(err)}, | ||
| 3617 | )); | ||
| 3618 | decl.analysis = .codegen_failure_retryable; | ||
| 3619 | return; | ||
| 3620 | }, | ||
| 3621 | }; | ||
| 3622 | return; | ||
| 3623 | }, | ||
| 3624 | } | ||
| 3625 | } | ||
| 3626 | |||
| 3536 | pub fn updateEmbedFile(mod: *Module, embed_file: *EmbedFile) SemaError!void { | 3627 | pub fn updateEmbedFile(mod: *Module, embed_file: *EmbedFile) SemaError!void { |
| 3537 | const tracy = trace(@src()); | 3628 | const tracy = trace(@src()); |
| 3538 | defer tracy.end(); | 3629 | defer tracy.end(); |
src/Sema.zig+105-65| ... | @@ -5346,14 +5346,14 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr | ... | @@ -5346,14 +5346,14 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr |
| 5346 | } | 5346 | } |
| 5347 | 5347 | ||
| 5348 | if (lhs_ty.castTag(.error_set_inferred)) |payload| { | 5348 | if (lhs_ty.castTag(.error_set_inferred)) |payload| { |
| 5349 | try sema.resolveInferredErrorSet(payload.data); | 5349 | try sema.resolveInferredErrorSet(block, src, payload.data); |
| 5350 | // isAnyError might have changed from a false negative to a true positive after resolution. | 5350 | // isAnyError might have changed from a false negative to a true positive after resolution. |
| 5351 | if (lhs_ty.isAnyError()) { | 5351 | if (lhs_ty.isAnyError()) { |
| 5352 | return Air.Inst.Ref.anyerror_type; | 5352 | return Air.Inst.Ref.anyerror_type; |
| 5353 | } | 5353 | } |
| 5354 | } | 5354 | } |
| 5355 | if (rhs_ty.castTag(.error_set_inferred)) |payload| { | 5355 | if (rhs_ty.castTag(.error_set_inferred)) |payload| { |
| 5356 | try sema.resolveInferredErrorSet(payload.data); | 5356 | try sema.resolveInferredErrorSet(block, src, payload.data); |
| 5357 | // isAnyError might have changed from a false negative to a true positive after resolution. | 5357 | // isAnyError might have changed from a false negative to a true positive after resolution. |
| 5358 | if (rhs_ty.isAnyError()) { | 5358 | if (rhs_ty.isAnyError()) { |
| 5359 | return Air.Inst.Ref.anyerror_type; | 5359 | return Air.Inst.Ref.anyerror_type; |
| ... | @@ -6927,7 +6927,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError | ... | @@ -6927,7 +6927,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError |
| 6927 | } | 6927 | } |
| 6928 | } | 6928 | } |
| 6929 | 6929 | ||
| 6930 | try sema.resolveInferredErrorSetTy(operand_ty); | 6930 | try sema.resolveInferredErrorSetTy(block, src, operand_ty); |
| 6931 | 6931 | ||
| 6932 | if (operand_ty.isAnyError()) { | 6932 | if (operand_ty.isAnyError()) { |
| 6933 | if (special_prong != .@"else") { | 6933 | if (special_prong != .@"else") { |
| ... | @@ -10437,7 +10437,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai | ... | @@ -10437,7 +10437,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 10437 | }; | 10437 | }; |
| 10438 | 10438 | ||
| 10439 | // If the error set is inferred it has to be resolved at this point | 10439 | // If the error set is inferred it has to be resolved at this point |
| 10440 | try sema.resolveInferredErrorSetTy(ty); | 10440 | try sema.resolveInferredErrorSetTy(block, src, ty); |
| 10441 | 10441 | ||
| 10442 | // Build our list of Error values | 10442 | // Build our list of Error values |
| 10443 | // Optional value is only null if anyerror | 10443 | // Optional value is only null if anyerror |
| ... | @@ -12627,7 +12627,7 @@ fn zirErrSetCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! | ... | @@ -12627,7 +12627,7 @@ fn zirErrSetCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! |
| 12627 | try sema.checkErrorSetType(block, operand_src, operand_ty); | 12627 | try sema.checkErrorSetType(block, operand_src, operand_ty); |
| 12628 | 12628 | ||
| 12629 | if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| { | 12629 | if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| { |
| 12630 | try sema.resolveInferredErrorSetTy(dest_ty); | 12630 | try sema.resolveInferredErrorSetTy(block, src, dest_ty); |
| 12631 | 12631 | ||
| 12632 | if (!dest_ty.isAnyError()) { | 12632 | if (!dest_ty.isAnyError()) { |
| 12633 | const error_name = val.castTag(.@"error").?.data.name; | 12633 | const error_name = val.castTag(.@"error").?.data.name; |
| ... | @@ -16616,7 +16616,7 @@ fn coerceInMemoryAllowed( | ... | @@ -16616,7 +16616,7 @@ fn coerceInMemoryAllowed( |
| 16616 | 16616 | ||
| 16617 | // Error Sets | 16617 | // Error Sets |
| 16618 | if (dest_tag == .ErrorSet and src_tag == .ErrorSet) { | 16618 | if (dest_tag == .ErrorSet and src_tag == .ErrorSet) { |
| 16619 | return try sema.coerceInMemoryAllowedErrorSets(dest_ty, src_ty); | 16619 | return try sema.coerceInMemoryAllowedErrorSets(block, dest_ty, src_ty, dest_src, src_src); |
| 16620 | } | 16620 | } |
| 16621 | 16621 | ||
| 16622 | // Arrays | 16622 | // Arrays |
| ... | @@ -16646,8 +16646,11 @@ fn coerceInMemoryAllowed( | ... | @@ -16646,8 +16646,11 @@ fn coerceInMemoryAllowed( |
| 16646 | 16646 | ||
| 16647 | fn coerceInMemoryAllowedErrorSets( | 16647 | fn coerceInMemoryAllowedErrorSets( |
| 16648 | sema: *Sema, | 16648 | sema: *Sema, |
| 16649 | block: *Block, | ||
| 16649 | dest_ty: Type, | 16650 | dest_ty: Type, |
| 16650 | src_ty: Type, | 16651 | src_ty: Type, |
| 16652 | dest_src: LazySrcLoc, | ||
| 16653 | src_src: LazySrcLoc, | ||
| 16651 | ) !InMemoryCoercionResult { | 16654 | ) !InMemoryCoercionResult { |
| 16652 | // Coercion to `anyerror`. Note that this check can return false negatives | 16655 | // Coercion to `anyerror`. Note that this check can return false negatives |
| 16653 | // in case the error sets did not get resolved. | 16656 | // in case the error sets did not get resolved. |
| ... | @@ -16655,24 +16658,43 @@ fn coerceInMemoryAllowedErrorSets( | ... | @@ -16655,24 +16658,43 @@ fn coerceInMemoryAllowedErrorSets( |
| 16655 | return .ok; | 16658 | return .ok; |
| 16656 | } | 16659 | } |
| 16657 | 16660 | ||
| 16658 | // If both are inferred error sets of functions, and | 16661 | if (dest_ty.castTag(.error_set_inferred)) |dst_payload| { |
| 16659 | // the dest includes the source function, the coercion is OK. | 16662 | const dst_ies = dst_payload.data; |
| 16660 | // This check is important because it works without forcing a full resolution | 16663 | // We will make an effort to return `ok` without resolving either error set, to |
| 16661 | // of inferred error sets. | 16664 | // avoid unnecessary "unable to resolve error set" dependency loop errors. |
| 16662 | if (src_ty.castTag(.error_set_inferred)) |src_payload| { | 16665 | switch (src_ty.tag()) { |
| 16663 | if (dest_ty.castTag(.error_set_inferred)) |dst_payload| { | 16666 | .error_set_inferred => { |
| 16664 | const src_func = src_payload.data.func; | 16667 | // If both are inferred error sets of functions, and |
| 16665 | const dst_func = dst_payload.data.func; | 16668 | // the dest includes the source function, the coercion is OK. |
| 16666 | 16669 | // This check is important because it works without forcing a full resolution | |
| 16667 | if (src_func == dst_func or dst_payload.data.inferred_error_sets.contains(src_payload.data)) { | 16670 | // of inferred error sets. |
| 16668 | return .ok; | 16671 | const src_ies = src_ty.castTag(.error_set_inferred).?.data; |
| 16669 | } | 16672 | |
| 16670 | return .no_match; | 16673 | if (dst_ies.inferred_error_sets.contains(src_ies)) { |
| 16674 | return .ok; | ||
| 16675 | } | ||
| 16676 | }, | ||
| 16677 | .error_set_single => { | ||
| 16678 | const name = src_ty.castTag(.error_set_single).?.data; | ||
| 16679 | if (dst_ies.errors.contains(name)) return .ok; | ||
| 16680 | }, | ||
| 16681 | .error_set_merged => { | ||
| 16682 | const names = src_ty.castTag(.error_set_merged).?.data.keys(); | ||
| 16683 | for (names) |name| { | ||
| 16684 | if (!dst_ies.errors.contains(name)) break; | ||
| 16685 | } else return .ok; | ||
| 16686 | }, | ||
| 16687 | .error_set => { | ||
| 16688 | const names = src_ty.castTag(.error_set).?.data.names.keys(); | ||
| 16689 | for (names) |name| { | ||
| 16690 | if (!dst_ies.errors.contains(name)) break; | ||
| 16691 | } else return .ok; | ||
| 16692 | }, | ||
| 16693 | .anyerror => {}, | ||
| 16694 | else => unreachable, | ||
| 16671 | } | 16695 | } |
| 16672 | } | ||
| 16673 | 16696 | ||
| 16674 | if (dest_ty.castTag(.error_set_inferred)) |payload| { | 16697 | try sema.resolveInferredErrorSet(block, dest_src, dst_payload.data); |
| 16675 | try sema.resolveInferredErrorSet(payload.data); | ||
| 16676 | // isAnyError might have changed from a false negative to a true positive after resolution. | 16698 | // isAnyError might have changed from a false negative to a true positive after resolution. |
| 16677 | if (dest_ty.isAnyError()) { | 16699 | if (dest_ty.isAnyError()) { |
| 16678 | return .ok; | 16700 | return .ok; |
| ... | @@ -16683,7 +16705,7 @@ fn coerceInMemoryAllowedErrorSets( | ... | @@ -16683,7 +16705,7 @@ fn coerceInMemoryAllowedErrorSets( |
| 16683 | .error_set_inferred => { | 16705 | .error_set_inferred => { |
| 16684 | const src_data = src_ty.castTag(.error_set_inferred).?.data; | 16706 | const src_data = src_ty.castTag(.error_set_inferred).?.data; |
| 16685 | 16707 | ||
| 16686 | try sema.resolveInferredErrorSet(src_data); | 16708 | try sema.resolveInferredErrorSet(block, src_src, src_data); |
| 16687 | // src anyerror status might have changed after the resolution. | 16709 | // src anyerror status might have changed after the resolution. |
| 16688 | if (src_ty.isAnyError()) { | 16710 | if (src_ty.isAnyError()) { |
| 16689 | // dest_ty.isAnyError() == true is already checked for at this point. | 16711 | // dest_ty.isAnyError() == true is already checked for at this point. |
| ... | @@ -17969,6 +17991,17 @@ fn ensureDeclAnalyzed(sema: *Sema, decl: *Decl) CompileError!void { | ... | @@ -17969,6 +17991,17 @@ fn ensureDeclAnalyzed(sema: *Sema, decl: *Decl) CompileError!void { |
| 17969 | }; | 17991 | }; |
| 17970 | } | 17992 | } |
| 17971 | 17993 | ||
| 17994 | fn ensureFuncBodyAnalyzed(sema: *Sema, func: *Module.Fn) CompileError!void { | ||
| 17995 | sema.mod.ensureFuncBodyAnalyzed(func) catch |err| { | ||
| 17996 | if (sema.owner_func) |owner_func| { | ||
| 17997 | owner_func.state = .dependency_failure; | ||
| 17998 | } else { | ||
| 17999 | sema.owner_decl.analysis = .dependency_failure; | ||
| 18000 | } | ||
| 18001 | return err; | ||
| 18002 | }; | ||
| 18003 | } | ||
| 18004 | |||
| 17972 | fn refValue(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type, val: Value) !Value { | 18005 | fn refValue(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type, val: Value) !Value { |
| 17973 | var anon_decl = try block.startAnonDecl(src); | 18006 | var anon_decl = try block.startAnonDecl(src); |
| 17974 | defer anon_decl.deinit(); | 18007 | defer anon_decl.deinit(); |
| ... | @@ -18635,10 +18668,16 @@ fn wrapErrorUnionSet( | ... | @@ -18635,10 +18668,16 @@ fn wrapErrorUnionSet( |
| 18635 | }, | 18668 | }, |
| 18636 | .error_set_inferred => ok: { | 18669 | .error_set_inferred => ok: { |
| 18637 | const expected_name = val.castTag(.@"error").?.data.name; | 18670 | const expected_name = val.castTag(.@"error").?.data.name; |
| 18638 | const data = dest_err_set_ty.castTag(.error_set_inferred).?.data; | 18671 | const ies = dest_err_set_ty.castTag(.error_set_inferred).?.data; |
| 18639 | try sema.resolveInferredErrorSet(data); | 18672 | |
| 18640 | if (data.is_anyerror) break :ok; | 18673 | // We carefully do this in an order that avoids unnecessarily |
| 18641 | if (data.errors.contains(expected_name)) break :ok; | 18674 | // resolving the destination error set type. |
| 18675 | if (ies.is_anyerror) break :ok; | ||
| 18676 | if (ies.errors.contains(expected_name)) break :ok; | ||
| 18677 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, dest_err_set_ty, inst_ty, inst_src, inst_src)) { | ||
| 18678 | break :ok; | ||
| 18679 | } | ||
| 18680 | |||
| 18642 | return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty); | 18681 | return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty); |
| 18643 | }, | 18682 | }, |
| 18644 | .error_set_merged => { | 18683 | .error_set_merged => { |
| ... | @@ -18794,10 +18833,10 @@ fn resolvePeerTypes( | ... | @@ -18794,10 +18833,10 @@ fn resolvePeerTypes( |
| 18794 | // If neither is a superset, merge errors. | 18833 | // If neither is a superset, merge errors. |
| 18795 | const chosen_set_ty = err_set_ty orelse chosen_ty; | 18834 | const chosen_set_ty = err_set_ty orelse chosen_ty; |
| 18796 | 18835 | ||
| 18797 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(chosen_set_ty, candidate_ty)) { | 18836 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_ty, src, src)) { |
| 18798 | continue; | 18837 | continue; |
| 18799 | } | 18838 | } |
| 18800 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(candidate_ty, chosen_set_ty)) { | 18839 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_ty, chosen_set_ty, src, src)) { |
| 18801 | err_set_ty = null; | 18840 | err_set_ty = null; |
| 18802 | chosen = candidate; | 18841 | chosen = candidate; |
| 18803 | chosen_i = candidate_i + 1; | 18842 | chosen_i = candidate_i + 1; |
| ... | @@ -18810,10 +18849,10 @@ fn resolvePeerTypes( | ... | @@ -18810,10 +18849,10 @@ fn resolvePeerTypes( |
| 18810 | .ErrorUnion => { | 18849 | .ErrorUnion => { |
| 18811 | const chosen_set_ty = err_set_ty orelse chosen_ty.errorUnionSet(); | 18850 | const chosen_set_ty = err_set_ty orelse chosen_ty.errorUnionSet(); |
| 18812 | 18851 | ||
| 18813 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(chosen_set_ty, candidate_ty)) { | 18852 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_ty, src, src)) { |
| 18814 | continue; | 18853 | continue; |
| 18815 | } | 18854 | } |
| 18816 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(candidate_ty, chosen_set_ty)) { | 18855 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_ty, chosen_set_ty, src, src)) { |
| 18817 | err_set_ty = candidate_ty; | 18856 | err_set_ty = candidate_ty; |
| 18818 | continue; | 18857 | continue; |
| 18819 | } | 18858 | } |
| ... | @@ -18823,10 +18862,10 @@ fn resolvePeerTypes( | ... | @@ -18823,10 +18862,10 @@ fn resolvePeerTypes( |
| 18823 | }, | 18862 | }, |
| 18824 | else => { | 18863 | else => { |
| 18825 | if (err_set_ty) |chosen_set_ty| { | 18864 | if (err_set_ty) |chosen_set_ty| { |
| 18826 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(chosen_set_ty, candidate_ty)) { | 18865 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_ty, src, src)) { |
| 18827 | continue; | 18866 | continue; |
| 18828 | } | 18867 | } |
| 18829 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(candidate_ty, chosen_set_ty)) { | 18868 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_ty, chosen_set_ty, src, src)) { |
| 18830 | err_set_ty = candidate_ty; | 18869 | err_set_ty = candidate_ty; |
| 18831 | continue; | 18870 | continue; |
| 18832 | } | 18871 | } |
| ... | @@ -18844,9 +18883,9 @@ fn resolvePeerTypes( | ... | @@ -18844,9 +18883,9 @@ fn resolvePeerTypes( |
| 18844 | const chosen_set_ty = err_set_ty orelse chosen_ty; | 18883 | const chosen_set_ty = err_set_ty orelse chosen_ty; |
| 18845 | const candidate_set_ty = candidate_ty.errorUnionSet(); | 18884 | const candidate_set_ty = candidate_ty.errorUnionSet(); |
| 18846 | 18885 | ||
| 18847 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(chosen_set_ty, candidate_set_ty)) { | 18886 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_set_ty, src, src)) { |
| 18848 | err_set_ty = chosen_set_ty; | 18887 | err_set_ty = chosen_set_ty; |
| 18849 | } else if (.ok == try sema.coerceInMemoryAllowedErrorSets(candidate_set_ty, chosen_set_ty)) { | 18888 | } else if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_set_ty, chosen_set_ty, src, src)) { |
| 18850 | err_set_ty = null; | 18889 | err_set_ty = null; |
| 18851 | } else { | 18890 | } else { |
| 18852 | err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, candidate_set_ty); | 18891 | err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, candidate_set_ty); |
| ... | @@ -18875,9 +18914,9 @@ fn resolvePeerTypes( | ... | @@ -18875,9 +18914,9 @@ fn resolvePeerTypes( |
| 18875 | const chosen_set_ty = err_set_ty orelse chosen_ty.errorUnionSet(); | 18914 | const chosen_set_ty = err_set_ty orelse chosen_ty.errorUnionSet(); |
| 18876 | const candidate_set_ty = chosen_ty.errorUnionSet(); | 18915 | const candidate_set_ty = chosen_ty.errorUnionSet(); |
| 18877 | 18916 | ||
| 18878 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(chosen_set_ty, candidate_set_ty)) { | 18917 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_set_ty, src, src)) { |
| 18879 | err_set_ty = chosen_set_ty; | 18918 | err_set_ty = chosen_set_ty; |
| 18880 | } else if (.ok == try sema.coerceInMemoryAllowedErrorSets(candidate_set_ty, chosen_set_ty)) { | 18919 | } else if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_set_ty, chosen_set_ty, src, src)) { |
| 18881 | err_set_ty = candidate_set_ty; | 18920 | err_set_ty = candidate_set_ty; |
| 18882 | } else { | 18921 | } else { |
| 18883 | err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, candidate_set_ty); | 18922 | err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, candidate_set_ty); |
| ... | @@ -18889,9 +18928,9 @@ fn resolvePeerTypes( | ... | @@ -18889,9 +18928,9 @@ fn resolvePeerTypes( |
| 18889 | else => { | 18928 | else => { |
| 18890 | if (err_set_ty) |chosen_set_ty| { | 18929 | if (err_set_ty) |chosen_set_ty| { |
| 18891 | const candidate_set_ty = candidate_ty.errorUnionSet(); | 18930 | const candidate_set_ty = candidate_ty.errorUnionSet(); |
| 18892 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(chosen_set_ty, candidate_set_ty)) { | 18931 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_set_ty, src, src)) { |
| 18893 | err_set_ty = chosen_set_ty; | 18932 | err_set_ty = chosen_set_ty; |
| 18894 | } else if (.ok == try sema.coerceInMemoryAllowedErrorSets(candidate_set_ty, chosen_set_ty)) { | 18933 | } else if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_set_ty, chosen_set_ty, src, src)) { |
| 18895 | err_set_ty = null; | 18934 | err_set_ty = null; |
| 18896 | } else { | 18935 | } else { |
| 18897 | err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, candidate_set_ty); | 18936 | err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, candidate_set_ty); |
| ... | @@ -19458,43 +19497,44 @@ fn resolveBuiltinTypeFields( | ... | @@ -19458,43 +19497,44 @@ fn resolveBuiltinTypeFields( |
| 19458 | return sema.resolveTypeFields(block, src, resolved_ty); | 19497 | return sema.resolveTypeFields(block, src, resolved_ty); |
| 19459 | } | 19498 | } |
| 19460 | 19499 | ||
| 19461 | fn resolveInferredErrorSet(sema: *Sema, inferred_error_set: *Module.Fn.InferredErrorSet) CompileError!void { | 19500 | fn resolveInferredErrorSet( |
| 19462 | // Ensuring that a particular decl is analyzed does not neccesarily mean that | 19501 | sema: *Sema, |
| 19463 | // it's error set is inferred, so traverse all of them to get the complete | 19502 | block: *Block, |
| 19464 | // picture. | 19503 | src: LazySrcLoc, |
| 19465 | // Note: We want to skip re-resolving the current function, as recursion | 19504 | ies: *Module.Fn.InferredErrorSet, |
| 19466 | // doesn't change the error set. We can just check for state == .in_progress for this. | 19505 | ) CompileError!void { |
| 19467 | // TODO: Is that correct? | 19506 | if (ies.is_resolved) return; |
| 19468 | 19507 | ||
| 19469 | if (inferred_error_set.is_resolved) { | 19508 | if (ies.func.state == .in_progress) { |
| 19470 | return; | 19509 | return sema.fail(block, src, "unable to resolve inferred error set", .{}); |
| 19471 | } | 19510 | } |
| 19472 | inferred_error_set.is_resolved = true; | ||
| 19473 | 19511 | ||
| 19474 | var it = inferred_error_set.inferred_error_sets.keyIterator(); | 19512 | // To ensure that all dependencies are properly added to the set. |
| 19475 | while (it.next()) |other_error_set_ptr| { | 19513 | try sema.ensureFuncBodyAnalyzed(ies.func); |
| 19476 | const func = other_error_set_ptr.*.func; | ||
| 19477 | const decl = func.*.owner_decl; | ||
| 19478 | 19514 | ||
| 19479 | if (func.*.state == .in_progress) { | 19515 | ies.is_resolved = true; |
| 19480 | // Recursion, doesn't alter current error set, keep going. | ||
| 19481 | continue; | ||
| 19482 | } | ||
| 19483 | 19516 | ||
| 19484 | try sema.ensureDeclAnalyzed(decl); // To ensure that all dependencies are properly added to the set. | 19517 | var it = ies.inferred_error_sets.keyIterator(); |
| 19485 | try sema.resolveInferredErrorSet(other_error_set_ptr.*); | 19518 | while (it.next()) |other_error_set_ptr| { |
| 19519 | const other_ies: *Module.Fn.InferredErrorSet = other_error_set_ptr.*; | ||
| 19520 | try sema.resolveInferredErrorSet(block, src, other_ies); | ||
| 19486 | 19521 | ||
| 19487 | for (other_error_set_ptr.*.errors.keys()) |key| { | 19522 | for (other_ies.errors.keys()) |key| { |
| 19488 | try inferred_error_set.errors.put(sema.gpa, key, {}); | 19523 | try ies.errors.put(sema.gpa, key, {}); |
| 19489 | } | 19524 | } |
| 19490 | if (other_error_set_ptr.*.is_anyerror) | 19525 | if (other_ies.is_anyerror) |
| 19491 | inferred_error_set.is_anyerror = true; | 19526 | ies.is_anyerror = true; |
| 19492 | } | 19527 | } |
| 19493 | } | 19528 | } |
| 19494 | 19529 | ||
| 19495 | fn resolveInferredErrorSetTy(sema: *Sema, ty: Type) CompileError!void { | 19530 | fn resolveInferredErrorSetTy( |
| 19531 | sema: *Sema, | ||
| 19532 | block: *Block, | ||
| 19533 | src: LazySrcLoc, | ||
| 19534 | ty: Type, | ||
| 19535 | ) CompileError!void { | ||
| 19496 | if (ty.castTag(.error_set_inferred)) |inferred| { | 19536 | if (ty.castTag(.error_set_inferred)) |inferred| { |
| 19497 | try sema.resolveInferredErrorSet(inferred.data); | 19537 | try sema.resolveInferredErrorSet(block, src, inferred.data); |
| 19498 | } | 19538 | } |
| 19499 | } | 19539 | } |
| 19500 | 19540 |
src/type.zig+5-5| ... | @@ -559,9 +559,9 @@ pub const Type = extern union { | ... | @@ -559,9 +559,9 @@ pub const Type = extern union { |
| 559 | .error_set_inferred => { | 559 | .error_set_inferred => { |
| 560 | // Inferred error sets are only equal if both are inferred | 560 | // Inferred error sets are only equal if both are inferred |
| 561 | // and they originate from the exact same function. | 561 | // and they originate from the exact same function. |
| 562 | const a_set = a.castTag(.error_set_inferred).?.data; | 562 | const a_ies = a.castTag(.error_set_inferred).?.data; |
| 563 | const b_set = (b.castTag(.error_set_inferred) orelse return false).data; | 563 | const b_ies = (b.castTag(.error_set_inferred) orelse return false).data; |
| 564 | return a_set.func == b_set.func; | 564 | return a_ies == b_ies; |
| 565 | }, | 565 | }, |
| 566 | 566 | ||
| 567 | .anyerror => { | 567 | .anyerror => { |
| ... | @@ -983,10 +983,10 @@ pub const Type = extern union { | ... | @@ -983,10 +983,10 @@ pub const Type = extern union { |
| 983 | 983 | ||
| 984 | .error_set_inferred => { | 984 | .error_set_inferred => { |
| 985 | // inferred error sets are compared using their data pointer | 985 | // inferred error sets are compared using their data pointer |
| 986 | const set = ty.castTag(.error_set_inferred).?.data; | 986 | const ies: *Module.Fn.InferredErrorSet = ty.castTag(.error_set_inferred).?.data; |
| 987 | std.hash.autoHash(hasher, std.builtin.TypeId.ErrorSet); | 987 | std.hash.autoHash(hasher, std.builtin.TypeId.ErrorSet); |
| 988 | std.hash.autoHash(hasher, Tag.error_set_inferred); | 988 | std.hash.autoHash(hasher, Tag.error_set_inferred); |
| 989 | std.hash.autoHash(hasher, set.func); | 989 | std.hash.autoHash(hasher, ies); |
| 990 | }, | 990 | }, |
| 991 | 991 | ||
| 992 | .@"opaque" => { | 992 | .@"opaque" => { |
test/behavior.zig+1| ... | @@ -61,6 +61,7 @@ test { | ... | @@ -61,6 +61,7 @@ test { |
| 61 | _ = @import("behavior/bugs/7250.zig"); | 61 | _ = @import("behavior/bugs/7250.zig"); |
| 62 | _ = @import("behavior/bugs/11100.zig"); | 62 | _ = @import("behavior/bugs/11100.zig"); |
| 63 | _ = @import("behavior/bugs/10970.zig"); | 63 | _ = @import("behavior/bugs/10970.zig"); |
| 64 | _ = @import("behavior/bugs/11046.zig"); | ||
| 64 | _ = @import("behavior/call.zig"); | 65 | _ = @import("behavior/call.zig"); |
| 65 | _ = @import("behavior/cast.zig"); | 66 | _ = @import("behavior/cast.zig"); |
| 66 | _ = @import("behavior/comptime_memory.zig"); | 67 | _ = @import("behavior/comptime_memory.zig"); |
test/behavior/array.zig-3| ... | @@ -538,9 +538,6 @@ test "type coercion of anon struct literal to array" { | ... | @@ -538,9 +538,6 @@ test "type coercion of anon struct literal to array" { |
| 538 | try expect(arr1[1] == 56); | 538 | try expect(arr1[1] == 56); |
| 539 | try expect(arr1[2] == 54); | 539 | try expect(arr1[2] == 54); |
| 540 | 540 | ||
| 541 | if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO | ||
| 542 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 543 | |||
| 544 | var x2: U = .{ .a = 42 }; | 541 | var x2: U = .{ .a = 42 }; |
| 545 | const t2 = .{ x2, .{ .b = true }, .{ .c = "hello" } }; | 542 | const t2 = .{ x2, .{ .b = true }, .{ .c = "hello" } }; |
| 546 | var arr2: [3]U = t2; | 543 | var arr2: [3]U = t2; |
test/behavior/bugs/11046.zig created+21| ... | @@ -0,0 +1,21 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | |||
| 3 | fn foo() !void { | ||
| 4 | var a = true; | ||
| 5 | if (a) return error.Foo; | ||
| 6 | return error.Bar; | ||
| 7 | } | ||
| 8 | fn bar() !void { | ||
| 9 | try foo(); | ||
| 10 | } | ||
| 11 | |||
| 12 | test "fixed" { | ||
| 13 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 14 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 15 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 16 | |||
| 17 | bar() catch |err| switch (err) { | ||
| 18 | error.Foo => {}, // error: expected (inferred error set of bar), found error{Foo} | ||
| 19 | error.Bar => {}, | ||
| 20 | }; | ||
| 21 | } | ||
test/behavior/error.zig+5-1| ... | @@ -476,7 +476,11 @@ test "function pointer with return type that is error union with payload which i | ... | @@ -476,7 +476,11 @@ test "function pointer with return type that is error union with payload which i |
| 476 | } | 476 | } |
| 477 | 477 | ||
| 478 | test "return result loc as peer result loc in inferred error set function" { | 478 | test "return result loc as peer result loc in inferred error set function" { |
| 479 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | 479 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 480 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 481 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 482 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 483 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 480 | 484 | ||
| 481 | const S = struct { | 485 | const S = struct { |
| 482 | fn doTheTest() !void { | 486 | fn doTheTest() !void { |