| 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 | 15 | const link = @import("link.zig"); |
| 16 | 16 | const tracy = @import("tracy.zig"); |
| 17 | 17 | const trace = tracy.trace; |
| 18 | const Liveness = @import("Liveness.zig"); | |
| 19 | 18 | const build_options = @import("build_options"); |
| 20 | 19 | const LibCInstallation = @import("libc_installation.zig").LibCInstallation; |
| 21 | 20 | const glibc = @import("glibc.zig"); |
| ... | ... | @@ -2702,12 +2701,12 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress |
| 2702 | 2701 | => return, |
| 2703 | 2702 | |
| 2704 | 2703 | .complete, .codegen_failure_retryable => { |
| 2705 | const named_frame = tracy.namedFrame("codegen_decl"); | |
| 2706 | defer named_frame.end(); | |
| 2707 | ||
| 2708 | 2704 | if (build_options.omit_stage2) |
| 2709 | 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 | 2710 | const module = comp.bin_file.options.module.?; |
| 2712 | 2711 | assert(decl.has_tv); |
| 2713 | 2712 | |
| ... | ... | @@ -2722,100 +2721,18 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress |
| 2722 | 2721 | return; |
| 2723 | 2722 | }, |
| 2724 | 2723 | }, |
| 2725 | .codegen_func => |func| switch (func.owner_decl.analysis) { | |
| 2726 | .unreferenced => unreachable, | |
| 2727 | .in_progress => unreachable, | |
| 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 | } | |
| 2724 | .codegen_func => |func| { | |
| 2725 | if (build_options.omit_stage2) | |
| 2726 | @panic("sadly stage2 is omitted from this build to save memory on the CI server"); | |
| 2795 | 2727 | |
| 2796 | const named_frame = tracy.namedFrame("codegen"); | |
| 2797 | defer named_frame.end(); | |
| 2728 | const named_frame = tracy.namedFrame("codegen_func"); | |
| 2729 | defer named_frame.end(); | |
| 2798 | 2730 | |
| 2799 | comp.bin_file.updateFunc(module, func, air, liveness) catch |err| switch (err) { | |
| 2800 | error.OutOfMemory => return error.OutOfMemory, | |
| 2801 | error.AnalysisFail => { | |
| 2802 | decl.analysis = .codegen_failure; | |
| 2803 | return; | |
| 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 | }, | |
| 2731 | const module = comp.bin_file.options.module.?; | |
| 2732 | module.ensureFuncBodyAnalyzed(func) catch |err| switch (err) { | |
| 2733 | error.OutOfMemory => return error.OutOfMemory, | |
| 2734 | error.AnalysisFail => return, | |
| 2735 | }; | |
| 2819 | 2736 | }, |
| 2820 | 2737 | .emit_h_decl => |decl| switch (decl.analysis) { |
| 2821 | 2738 | .unreferenced => unreachable, |
| ... | ... | @@ -2831,11 +2748,12 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress |
| 2831 | 2748 | // emit-h only requires semantic analysis of the Decl to be complete, |
| 2832 | 2749 | // it does not depend on machine code generation to succeed. |
| 2833 | 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 | 2754 | const named_frame = tracy.namedFrame("emit_h_decl"); |
| 2835 | 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 | 2757 | const gpa = comp.gpa; |
| 2840 | 2758 | const module = comp.bin_file.options.module.?; |
| 2841 | 2759 | const emit_h = module.emit_h.?; |
| ... | ... | @@ -2871,11 +2789,12 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress |
| 2871 | 2789 | }, |
| 2872 | 2790 | }, |
| 2873 | 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 | 2795 | const named_frame = tracy.namedFrame("analyze_decl"); |
| 2875 | 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 | 2798 | const module = comp.bin_file.options.module.?; |
| 2880 | 2799 | module.ensureDeclAnalyzed(decl) catch |err| switch (err) { |
| 2881 | 2800 | error.OutOfMemory => return error.OutOfMemory, |
| ... | ... | @@ -2883,11 +2802,12 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress |
| 2883 | 2802 | }; |
| 2884 | 2803 | }, |
| 2885 | 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 | 2808 | const named_frame = tracy.namedFrame("update_embed_file"); |
| 2887 | 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 | 2811 | const module = comp.bin_file.options.module.?; |
| 2892 | 2812 | module.updateEmbedFile(embed_file) catch |err| switch (err) { |
| 2893 | 2813 | error.OutOfMemory => return error.OutOfMemory, |
| ... | ... | @@ -2895,11 +2815,12 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress |
| 2895 | 2815 | }; |
| 2896 | 2816 | }, |
| 2897 | 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 | 2821 | const named_frame = tracy.namedFrame("update_line_number"); |
| 2899 | 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 | 2824 | const gpa = comp.gpa; |
| 2904 | 2825 | const module = comp.bin_file.options.module.?; |
| 2905 | 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 | 2835 | }; |
| 2915 | 2836 | }, |
| 2916 | 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 | 2841 | const named_frame = tracy.namedFrame("analyze_pkg"); |
| 2918 | 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 | 2844 | const module = comp.bin_file.options.module.?; |
| 2923 | 2845 | module.semaPkg(pkg) catch |err| switch (err) { |
| 2924 | 2846 | error.CurrentWorkingDirectoryUnlinked, |
src/Module.zig+106-15| ... | ... | @@ -3,6 +3,7 @@ |
| 3 | 3 | //! there is or is not any zig source code, respectively. |
| 4 | 4 | |
| 5 | 5 | const std = @import("std"); |
| 6 | const builtin = @import("builtin"); | |
| 6 | 7 | const mem = std.mem; |
| 7 | 8 | const Allocator = std.mem.Allocator; |
| 8 | 9 | const ArrayListUnmanaged = std.ArrayListUnmanaged; |
| ... | ... | @@ -28,6 +29,7 @@ const AstGen = @import("AstGen.zig"); |
| 28 | 29 | const Sema = @import("Sema.zig"); |
| 29 | 30 | const target_util = @import("target.zig"); |
| 30 | 31 | const build_options = @import("build_options"); |
| 32 | const Liveness = @import("Liveness.zig"); | |
| 31 | 33 | |
| 32 | 34 | /// General-purpose allocator. Used for both temporary and long-term storage. |
| 33 | 35 | gpa: Allocator, |
| ... | ... | @@ -1438,8 +1440,11 @@ pub const Fn = struct { |
| 1438 | 1440 | is_cold: bool = false, |
| 1439 | 1441 | is_noinline: bool = false, |
| 1440 | 1442 | |
| 1441 | /// Any inferred error sets that this function owns, both it's own inferred error set and | |
| 1442 | /// inferred error sets of any inline/comptime functions called. | |
| 1443 | /// Any inferred error sets that this function owns, both its own inferred error set and | |
| 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 | 1448 | inferred_error_sets: InferredErrorSetList = .{}, |
| 1444 | 1449 | |
| 1445 | 1450 | pub const Analysis = enum { |
| ... | ... | @@ -1457,28 +1462,29 @@ pub const Fn = struct { |
| 1457 | 1462 | }; |
| 1458 | 1463 | |
| 1459 | 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, | |
| 1461 | /// for example an inferred error set which this function returns, but also any inferred error sets | |
| 1462 | /// of called inline or comptime functions. | |
| 1465 | /// that return inferred error sets. Note that a function may be associated to | |
| 1466 | /// multiple different error sets, for example an inferred error set which | |
| 1467 | /// this function returns, but also any inferred error sets of called inline | |
| 1468 | /// or comptime functions. | |
| 1463 | 1469 | pub const InferredErrorSet = struct { |
| 1464 | 1470 | /// The function from which this error set originates. |
| 1465 | /// Note: may be the function itself. | |
| 1466 | 1471 | func: *Fn, |
| 1467 | 1472 | |
| 1468 | /// All currently known errors that this error set contains. This includes direct additions | |
| 1469 | /// via `return error.Foo;`, and possibly also errors that are returned from any dependent functions. | |
| 1470 | /// When the inferred error set is fully resolved, this map contains all the errors that the function might return. | |
| 1473 | /// All currently known errors that this error set contains. This includes | |
| 1474 | /// direct additions via `return error.Foo;`, and possibly also errors that | |
| 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 | 1477 | errors: ErrorSet.NameMap = .{}, |
| 1472 | 1478 | |
| 1473 | 1479 | /// Other inferred error sets which this inferred error set should include. |
| 1474 | 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 | |
| 1477 | /// returns anyerror. | |
| 1482 | /// Whether the function returned anyerror. This is true if either of | |
| 1483 | /// the dependent functions returns anyerror. | |
| 1478 | 1484 | is_anyerror: bool = false, |
| 1479 | 1485 | |
| 1480 | /// Whether this error set is already fully resolved. If true, resolving can skip resolving any dependents | |
| 1481 | /// of this inferred error set. | |
| 1486 | /// Whether this error set is already fully resolved. If true, resolving | |
| 1487 | /// can skip resolving any dependents of this inferred error set. | |
| 1482 | 1488 | is_resolved: bool = false, |
| 1483 | 1489 | |
| 1484 | 1490 | pub fn addErrorSet(self: *InferredErrorSet, gpa: Allocator, err_set_ty: Type) !void { |
| ... | ... | @@ -1494,8 +1500,8 @@ pub const Fn = struct { |
| 1494 | 1500 | try self.errors.put(gpa, name, {}); |
| 1495 | 1501 | }, |
| 1496 | 1502 | .error_set_inferred => { |
| 1497 | const set = err_set_ty.castTag(.error_set_inferred).?.data; | |
| 1498 | try self.inferred_error_sets.put(gpa, set, {}); | |
| 1503 | const ies = err_set_ty.castTag(.error_set_inferred).?.data; | |
| 1504 | try self.inferred_error_sets.put(gpa, ies, {}); | |
| 1499 | 1505 | }, |
| 1500 | 1506 | .error_set_merged => { |
| 1501 | 1507 | const names = err_set_ty.castTag(.error_set_merged).?.data.keys(); |
| ... | ... | @@ -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 | 3454 | pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void { |
| 3445 | 3455 | const tracy = trace(@src()); |
| 3446 | 3456 | defer tracy.end(); |
| ... | ... | @@ -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 | 3627 | pub fn updateEmbedFile(mod: *Module, embed_file: *EmbedFile) SemaError!void { |
| 3537 | 3628 | const tracy = trace(@src()); |
| 3538 | 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 | 5346 | } |
| 5347 | 5347 | |
| 5348 | 5348 | if (lhs_ty.castTag(.error_set_inferred)) |payload| { |
| 5349 | try sema.resolveInferredErrorSet(payload.data); | |
| 5349 | try sema.resolveInferredErrorSet(block, src, payload.data); | |
| 5350 | 5350 | // isAnyError might have changed from a false negative to a true positive after resolution. |
| 5351 | 5351 | if (lhs_ty.isAnyError()) { |
| 5352 | 5352 | return Air.Inst.Ref.anyerror_type; |
| 5353 | 5353 | } |
| 5354 | 5354 | } |
| 5355 | 5355 | if (rhs_ty.castTag(.error_set_inferred)) |payload| { |
| 5356 | try sema.resolveInferredErrorSet(payload.data); | |
| 5356 | try sema.resolveInferredErrorSet(block, src, payload.data); | |
| 5357 | 5357 | // isAnyError might have changed from a false negative to a true positive after resolution. |
| 5358 | 5358 | if (rhs_ty.isAnyError()) { |
| 5359 | 5359 | return Air.Inst.Ref.anyerror_type; |
| ... | ... | @@ -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 | 6932 | if (operand_ty.isAnyError()) { |
| 6933 | 6933 | if (special_prong != .@"else") { |
| ... | ... | @@ -10437,7 +10437,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 10437 | 10437 | }; |
| 10438 | 10438 | |
| 10439 | 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 | 10442 | // Build our list of Error values |
| 10443 | 10443 | // Optional value is only null if anyerror |
| ... | ... | @@ -12627,7 +12627,7 @@ fn zirErrSetCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! |
| 12627 | 12627 | try sema.checkErrorSetType(block, operand_src, operand_ty); |
| 12628 | 12628 | |
| 12629 | 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 | 12632 | if (!dest_ty.isAnyError()) { |
| 12633 | 12633 | const error_name = val.castTag(.@"error").?.data.name; |
| ... | ... | @@ -16616,7 +16616,7 @@ fn coerceInMemoryAllowed( |
| 16616 | 16616 | |
| 16617 | 16617 | // Error Sets |
| 16618 | 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 | 16622 | // Arrays |
| ... | ... | @@ -16646,8 +16646,11 @@ fn coerceInMemoryAllowed( |
| 16646 | 16646 | |
| 16647 | 16647 | fn coerceInMemoryAllowedErrorSets( |
| 16648 | 16648 | sema: *Sema, |
| 16649 | block: *Block, | |
| 16649 | 16650 | dest_ty: Type, |
| 16650 | 16651 | src_ty: Type, |
| 16652 | dest_src: LazySrcLoc, | |
| 16653 | src_src: LazySrcLoc, | |
| 16651 | 16654 | ) !InMemoryCoercionResult { |
| 16652 | 16655 | // Coercion to `anyerror`. Note that this check can return false negatives |
| 16653 | 16656 | // in case the error sets did not get resolved. |
| ... | ... | @@ -16655,24 +16658,43 @@ fn coerceInMemoryAllowedErrorSets( |
| 16655 | 16658 | return .ok; |
| 16656 | 16659 | } |
| 16657 | 16660 | |
| 16658 | // If both are inferred error sets of functions, and | |
| 16659 | // the dest includes the source function, the coercion is OK. | |
| 16660 | // This check is important because it works without forcing a full resolution | |
| 16661 | // of inferred error sets. | |
| 16662 | if (src_ty.castTag(.error_set_inferred)) |src_payload| { | |
| 16663 | if (dest_ty.castTag(.error_set_inferred)) |dst_payload| { | |
| 16664 | const src_func = src_payload.data.func; | |
| 16665 | const dst_func = dst_payload.data.func; | |
| 16666 | ||
| 16667 | if (src_func == dst_func or dst_payload.data.inferred_error_sets.contains(src_payload.data)) { | |
| 16668 | return .ok; | |
| 16669 | } | |
| 16670 | return .no_match; | |
| 16661 | if (dest_ty.castTag(.error_set_inferred)) |dst_payload| { | |
| 16662 | const dst_ies = dst_payload.data; | |
| 16663 | // We will make an effort to return `ok` without resolving either error set, to | |
| 16664 | // avoid unnecessary "unable to resolve error set" dependency loop errors. | |
| 16665 | switch (src_ty.tag()) { | |
| 16666 | .error_set_inferred => { | |
| 16667 | // If both are inferred error sets of functions, and | |
| 16668 | // the dest includes the source function, the coercion is OK. | |
| 16669 | // This check is important because it works without forcing a full resolution | |
| 16670 | // of inferred error sets. | |
| 16671 | const src_ies = src_ty.castTag(.error_set_inferred).?.data; | |
| 16672 | ||
| 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| { | |
| 16675 | try sema.resolveInferredErrorSet(payload.data); | |
| 16697 | try sema.resolveInferredErrorSet(block, dest_src, dst_payload.data); | |
| 16676 | 16698 | // isAnyError might have changed from a false negative to a true positive after resolution. |
| 16677 | 16699 | if (dest_ty.isAnyError()) { |
| 16678 | 16700 | return .ok; |
| ... | ... | @@ -16683,7 +16705,7 @@ fn coerceInMemoryAllowedErrorSets( |
| 16683 | 16705 | .error_set_inferred => { |
| 16684 | 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 | 16709 | // src anyerror status might have changed after the resolution. |
| 16688 | 16710 | if (src_ty.isAnyError()) { |
| 16689 | 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 | 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 | 18005 | fn refValue(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type, val: Value) !Value { |
| 17973 | 18006 | var anon_decl = try block.startAnonDecl(src); |
| 17974 | 18007 | defer anon_decl.deinit(); |
| ... | ... | @@ -18635,10 +18668,16 @@ fn wrapErrorUnionSet( |
| 18635 | 18668 | }, |
| 18636 | 18669 | .error_set_inferred => ok: { |
| 18637 | 18670 | const expected_name = val.castTag(.@"error").?.data.name; |
| 18638 | const data = dest_err_set_ty.castTag(.error_set_inferred).?.data; | |
| 18639 | try sema.resolveInferredErrorSet(data); | |
| 18640 | if (data.is_anyerror) break :ok; | |
| 18641 | if (data.errors.contains(expected_name)) break :ok; | |
| 18671 | const ies = dest_err_set_ty.castTag(.error_set_inferred).?.data; | |
| 18672 | ||
| 18673 | // We carefully do this in an order that avoids unnecessarily | |
| 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 | 18681 | return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty); |
| 18643 | 18682 | }, |
| 18644 | 18683 | .error_set_merged => { |
| ... | ... | @@ -18794,10 +18833,10 @@ fn resolvePeerTypes( |
| 18794 | 18833 | // If neither is a superset, merge errors. |
| 18795 | 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 | 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 | 18840 | err_set_ty = null; |
| 18802 | 18841 | chosen = candidate; |
| 18803 | 18842 | chosen_i = candidate_i + 1; |
| ... | ... | @@ -18810,10 +18849,10 @@ fn resolvePeerTypes( |
| 18810 | 18849 | .ErrorUnion => { |
| 18811 | 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 | 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 | 18856 | err_set_ty = candidate_ty; |
| 18818 | 18857 | continue; |
| 18819 | 18858 | } |
| ... | ... | @@ -18823,10 +18862,10 @@ fn resolvePeerTypes( |
| 18823 | 18862 | }, |
| 18824 | 18863 | else => { |
| 18825 | 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 | 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 | 18869 | err_set_ty = candidate_ty; |
| 18831 | 18870 | continue; |
| 18832 | 18871 | } |
| ... | ... | @@ -18844,9 +18883,9 @@ fn resolvePeerTypes( |
| 18844 | 18883 | const chosen_set_ty = err_set_ty orelse chosen_ty; |
| 18845 | 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 | 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 | 18889 | err_set_ty = null; |
| 18851 | 18890 | } else { |
| 18852 | 18891 | err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, candidate_set_ty); |
| ... | ... | @@ -18875,9 +18914,9 @@ fn resolvePeerTypes( |
| 18875 | 18914 | const chosen_set_ty = err_set_ty orelse chosen_ty.errorUnionSet(); |
| 18876 | 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 | 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 | 18920 | err_set_ty = candidate_set_ty; |
| 18882 | 18921 | } else { |
| 18883 | 18922 | err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, candidate_set_ty); |
| ... | ... | @@ -18889,9 +18928,9 @@ fn resolvePeerTypes( |
| 18889 | 18928 | else => { |
| 18890 | 18929 | if (err_set_ty) |chosen_set_ty| { |
| 18891 | 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 | 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 | 18934 | err_set_ty = null; |
| 18896 | 18935 | } else { |
| 18897 | 18936 | err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, candidate_set_ty); |
| ... | ... | @@ -19458,43 +19497,44 @@ fn resolveBuiltinTypeFields( |
| 19458 | 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 { | |
| 19462 | // Ensuring that a particular decl is analyzed does not neccesarily mean that | |
| 19463 | // it's error set is inferred, so traverse all of them to get the complete | |
| 19464 | // picture. | |
| 19465 | // Note: We want to skip re-resolving the current function, as recursion | |
| 19466 | // doesn't change the error set. We can just check for state == .in_progress for this. | |
| 19467 | // TODO: Is that correct? | |
| 19500 | fn resolveInferredErrorSet( | |
| 19501 | sema: *Sema, | |
| 19502 | block: *Block, | |
| 19503 | src: LazySrcLoc, | |
| 19504 | ies: *Module.Fn.InferredErrorSet, | |
| 19505 | ) CompileError!void { | |
| 19506 | if (ies.is_resolved) return; | |
| 19468 | 19507 | |
| 19469 | if (inferred_error_set.is_resolved) { | |
| 19470 | return; | |
| 19508 | if (ies.func.state == .in_progress) { | |
| 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(); | |
| 19475 | while (it.next()) |other_error_set_ptr| { | |
| 19476 | const func = other_error_set_ptr.*.func; | |
| 19477 | const decl = func.*.owner_decl; | |
| 19512 | // To ensure that all dependencies are properly added to the set. | |
| 19513 | try sema.ensureFuncBodyAnalyzed(ies.func); | |
| 19478 | 19514 | |
| 19479 | if (func.*.state == .in_progress) { | |
| 19480 | // Recursion, doesn't alter current error set, keep going. | |
| 19481 | continue; | |
| 19482 | } | |
| 19515 | ies.is_resolved = true; | |
| 19483 | 19516 | |
| 19484 | try sema.ensureDeclAnalyzed(decl); // To ensure that all dependencies are properly added to the set. | |
| 19485 | try sema.resolveInferredErrorSet(other_error_set_ptr.*); | |
| 19517 | var it = ies.inferred_error_sets.keyIterator(); | |
| 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| { | |
| 19488 | try inferred_error_set.errors.put(sema.gpa, key, {}); | |
| 19522 | for (other_ies.errors.keys()) |key| { | |
| 19523 | try ies.errors.put(sema.gpa, key, {}); | |
| 19489 | 19524 | } |
| 19490 | if (other_error_set_ptr.*.is_anyerror) | |
| 19491 | inferred_error_set.is_anyerror = true; | |
| 19525 | if (other_ies.is_anyerror) | |
| 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 | 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 | 559 | .error_set_inferred => { |
| 560 | 560 | // Inferred error sets are only equal if both are inferred |
| 561 | 561 | // and they originate from the exact same function. |
| 562 | const a_set = a.castTag(.error_set_inferred).?.data; | |
| 563 | const b_set = (b.castTag(.error_set_inferred) orelse return false).data; | |
| 564 | return a_set.func == b_set.func; | |
| 562 | const a_ies = a.castTag(.error_set_inferred).?.data; | |
| 563 | const b_ies = (b.castTag(.error_set_inferred) orelse return false).data; | |
| 564 | return a_ies == b_ies; | |
| 565 | 565 | }, |
| 566 | 566 | |
| 567 | 567 | .anyerror => { |
| ... | ... | @@ -983,10 +983,10 @@ pub const Type = extern union { |
| 983 | 983 | |
| 984 | 984 | .error_set_inferred => { |
| 985 | 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 | 987 | std.hash.autoHash(hasher, std.builtin.TypeId.ErrorSet); |
| 988 | 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 | 992 | .@"opaque" => { |
test/behavior.zig+1| ... | ... | @@ -61,6 +61,7 @@ test { |
| 61 | 61 | _ = @import("behavior/bugs/7250.zig"); |
| 62 | 62 | _ = @import("behavior/bugs/11100.zig"); |
| 63 | 63 | _ = @import("behavior/bugs/10970.zig"); |
| 64 | _ = @import("behavior/bugs/11046.zig"); | |
| 64 | 65 | _ = @import("behavior/call.zig"); |
| 65 | 66 | _ = @import("behavior/cast.zig"); |
| 66 | 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 | 538 | try expect(arr1[1] == 56); |
| 539 | 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 | 541 | var x2: U = .{ .a = 42 }; |
| 545 | 542 | const t2 = .{ x2, .{ .b = true }, .{ .c = "hello" } }; |
| 546 | 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 | 476 | } |
| 477 | 477 | |
| 478 | 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 | 485 | const S = struct { |
| 482 | 486 | fn doTheTest() !void { |