authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-08-18 12:56:04+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-08-18 12:56:04+01:00
log4929cf23cedef2ca8a8cfb2f9379d136681a6f37
tree04156c9a729b5029a3682ff25fef088156addfdd
parent2b05e85107dd1c637ab40f8b145b232d18e8d6c6
parentf0374fe3f04925a6e686077c2ffcb51b8eafc926
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #21063 from mlugg/incremental

Incremental compilation progress

19 files changed, 3171 insertions(+), 966 deletions(-)

lib/std/bounded_array.zig+5
...@@ -72,6 +72,11 @@ pub fn BoundedArrayAligned(...@@ -72,6 +72,11 @@ pub fn BoundedArrayAligned(
72 self.len = @intCast(len);72 self.len = @intCast(len);
73 }73 }
7474
75 /// Remove all elements from the slice.
76 pub fn clear(self: *Self) void {
77 self.len = 0;
78 }
79
75 /// Copy the content of an existing slice.80 /// Copy the content of an existing slice.
76 pub fn fromSlice(m: []const T) error{Overflow}!Self {81 pub fn fromSlice(m: []const T) error{Overflow}!Self {
77 var list = try init(m.len);82 var list = try init(m.len);
lib/std/zig/Zir.zig+606-54
...@@ -603,7 +603,7 @@ pub const Inst = struct {...@@ -603,7 +603,7 @@ pub const Inst = struct {
603 /// Uses the `un_node` field.603 /// Uses the `un_node` field.
604 typeof,604 typeof,
605 /// Implements `@TypeOf` for one operand.605 /// Implements `@TypeOf` for one operand.
606 /// Uses the `pl_node` field.606 /// Uses the `pl_node` field. Payload is `Block`.
607 typeof_builtin,607 typeof_builtin,
608 /// Given a value, look at the type of it, which must be an integer type.608 /// Given a value, look at the type of it, which must be an integer type.
609 /// Returns the integer type for the RHS of a shift operation.609 /// Returns the integer type for the RHS of a shift operation.
...@@ -2727,6 +2727,9 @@ pub const Inst = struct {...@@ -2727,6 +2727,9 @@ pub const Inst = struct {
2727 field_name_start: NullTerminatedString,2727 field_name_start: NullTerminatedString,
2728 };2728 };
27292729
2730 /// There is a body of instructions at `extra[body_index..][0..body_len]`.
2731 /// Trailing:
2732 /// 0. operand: Ref // for each `operands_len`
2730 pub const TypeOfPeer = struct {2733 pub const TypeOfPeer = struct {
2731 src_node: i32,2734 src_node: i32,
2732 body_len: u32,2735 body_len: u32,
...@@ -2844,6 +2847,40 @@ pub const Inst = struct {...@@ -2844,6 +2847,40 @@ pub const Inst = struct {
2844 src_line: u32,2847 src_line: u32,
2845 };2848 };
28462849
2850 /// Trailing:
2851 /// 0. multi_cases_len: u32 // if `has_multi_cases`
2852 /// 1. err_capture_inst: u32 // if `any_uses_err_capture`
2853 /// 2. non_err_body {
2854 /// info: ProngInfo,
2855 /// inst: Index // for every `info.body_len`
2856 /// }
2857 /// 3. else_body { // if `has_else`
2858 /// info: ProngInfo,
2859 /// inst: Index // for every `info.body_len`
2860 /// }
2861 /// 4. scalar_cases: { // for every `scalar_cases_len`
2862 /// item: Ref,
2863 /// info: ProngInfo,
2864 /// inst: Index // for every `info.body_len`
2865 /// }
2866 /// 5. multi_cases: { // for every `multi_cases_len`
2867 /// items_len: u32,
2868 /// ranges_len: u32,
2869 /// info: ProngInfo,
2870 /// item: Ref // for every `items_len`
2871 /// ranges: { // for every `ranges_len`
2872 /// item_first: Ref,
2873 /// item_last: Ref,
2874 /// }
2875 /// inst: Index // for every `info.body_len`
2876 /// }
2877 ///
2878 /// When analyzing a case body, the switch instruction itself refers to the
2879 /// captured error, or to the success value in `non_err_body`. Whether this
2880 /// is captured by reference or by value depends on whether the `byref` bit
2881 /// is set for the corresponding body. `err_capture_inst` refers to the error
2882 /// capture outside of the `switch`, i.e. `err` in
2883 /// `x catch |err| switch (err) { ... }`.
2847 pub const SwitchBlockErrUnion = struct {2884 pub const SwitchBlockErrUnion = struct {
2848 operand: Ref,2885 operand: Ref,
2849 bits: Bits,2886 bits: Bits,
...@@ -3153,7 +3190,7 @@ pub const Inst = struct {...@@ -3153,7 +3190,7 @@ pub const Inst = struct {
3153 /// 1. captures_len: u32 // if has_captures_len3190 /// 1. captures_len: u32 // if has_captures_len
3154 /// 2. body_len: u32, // if has_body_len3191 /// 2. body_len: u32, // if has_body_len
3155 /// 3. fields_len: u32, // if has_fields_len3192 /// 3. fields_len: u32, // if has_fields_len
3156 /// 4. decls_len: u37, // if has_decls_len3193 /// 4. decls_len: u32, // if has_decls_len
3157 /// 5. capture: Capture // for every captures_len3194 /// 5. capture: Capture // for every captures_len
3158 /// 6. decl: Index, // for every decls_len; points to a `declaration` instruction3195 /// 6. decl: Index, // for every decls_len; points to a `declaration` instruction
3159 /// 7. inst: Index // for every body_len3196 /// 7. inst: Index // for every body_len
...@@ -3624,33 +3661,492 @@ pub fn declIterator(zir: Zir, decl_inst: Zir.Inst.Index) DeclIterator {...@@ -3624,33 +3661,492 @@ pub fn declIterator(zir: Zir, decl_inst: Zir.Inst.Index) DeclIterator {
3624 }3661 }
3625}3662}
36263663
3627/// The iterator would have to allocate memory anyway to iterate. So here we populate3664/// Find all type declarations, recursively, within a `declaration` instruction. Does not recurse through
3628/// an ArrayList as the result.3665/// said type declarations' declarations; to find all declarations, call this function on the declarations
3629pub fn findDecls(zir: Zir, list: *std.ArrayList(Inst.Index), decl_inst: Zir.Inst.Index) !void {3666/// of the discovered types recursively.
3667/// The iterator would have to allocate memory anyway to iterate, so an `ArrayList` is populated as the result.
3668pub fn findDecls(zir: Zir, gpa: Allocator, list: *std.ArrayListUnmanaged(Inst.Index), decl_inst: Zir.Inst.Index) !void {
3630 list.clearRetainingCapacity();3669 list.clearRetainingCapacity();
3631 const declaration, const extra_end = zir.getDeclaration(decl_inst);3670 const declaration, const extra_end = zir.getDeclaration(decl_inst);
3632 const bodies = declaration.getBodies(extra_end, zir);3671 const bodies = declaration.getBodies(extra_end, zir);
36333672
3634 try zir.findDeclsBody(list, bodies.value_body);3673 // `defer` instructions duplicate the same body arbitrarily many times, but we only want to traverse
3635 if (bodies.align_body) |b| try zir.findDeclsBody(list, b);3674 // their contents once per defer. So, we store the extra index of the body here to deduplicate.
3636 if (bodies.linksection_body) |b| try zir.findDeclsBody(list, b);3675 var found_defers: std.AutoHashMapUnmanaged(u32, void) = .{};
3637 if (bodies.addrspace_body) |b| try zir.findDeclsBody(list, b);3676 defer found_defers.deinit(gpa);
3677
3678 try zir.findDeclsBody(gpa, list, &found_defers, bodies.value_body);
3679 if (bodies.align_body) |b| try zir.findDeclsBody(gpa, list, &found_defers, b);
3680 if (bodies.linksection_body) |b| try zir.findDeclsBody(gpa, list, &found_defers, b);
3681 if (bodies.addrspace_body) |b| try zir.findDeclsBody(gpa, list, &found_defers, b);
3638}3682}
36393683
3640fn findDeclsInner(3684fn findDeclsInner(
3641 zir: Zir,3685 zir: Zir,
3642 list: *std.ArrayList(Inst.Index),3686 gpa: Allocator,
3687 list: *std.ArrayListUnmanaged(Inst.Index),
3688 defers: *std.AutoHashMapUnmanaged(u32, void),
3643 inst: Inst.Index,3689 inst: Inst.Index,
3644) Allocator.Error!void {3690) Allocator.Error!void {
3645 const tags = zir.instructions.items(.tag);3691 const tags = zir.instructions.items(.tag);
3646 const datas = zir.instructions.items(.data);3692 const datas = zir.instructions.items(.data);
36473693
3648 switch (tags[@intFromEnum(inst)]) {3694 switch (tags[@intFromEnum(inst)]) {
3695 .declaration => unreachable,
3696
3697 // Boring instruction tags first. These have no body and are not declarations or type declarations.
3698 .add,
3699 .addwrap,
3700 .add_sat,
3701 .add_unsafe,
3702 .sub,
3703 .subwrap,
3704 .sub_sat,
3705 .mul,
3706 .mulwrap,
3707 .mul_sat,
3708 .div_exact,
3709 .div_floor,
3710 .div_trunc,
3711 .mod,
3712 .rem,
3713 .mod_rem,
3714 .shl,
3715 .shl_exact,
3716 .shl_sat,
3717 .shr,
3718 .shr_exact,
3719 .param_anytype,
3720 .param_anytype_comptime,
3721 .array_cat,
3722 .array_mul,
3723 .array_type,
3724 .array_type_sentinel,
3725 .vector_type,
3726 .elem_type,
3727 .indexable_ptr_elem_type,
3728 .vector_elem_type,
3729 .indexable_ptr_len,
3730 .anyframe_type,
3731 .as_node,
3732 .as_shift_operand,
3733 .bit_and,
3734 .bitcast,
3735 .bit_not,
3736 .bit_or,
3737 .bool_not,
3738 .bool_br_and,
3739 .bool_br_or,
3740 .@"break",
3741 .break_inline,
3742 .check_comptime_control_flow,
3743 .builtin_call,
3744 .cmp_lt,
3745 .cmp_lte,
3746 .cmp_eq,
3747 .cmp_gte,
3748 .cmp_gt,
3749 .cmp_neq,
3750 .error_set_decl,
3751 .dbg_stmt,
3752 .dbg_var_ptr,
3753 .dbg_var_val,
3754 .decl_ref,
3755 .decl_val,
3756 .load,
3757 .div,
3758 .elem_ptr_node,
3759 .elem_ptr,
3760 .elem_val_node,
3761 .elem_val,
3762 .elem_val_imm,
3763 .ensure_result_used,
3764 .ensure_result_non_error,
3765 .ensure_err_union_payload_void,
3766 .error_union_type,
3767 .error_value,
3768 .@"export",
3769 .export_value,
3770 .field_ptr,
3771 .field_val,
3772 .field_ptr_named,
3773 .field_val_named,
3774 .import,
3775 .int,
3776 .int_big,
3777 .float,
3778 .float128,
3779 .int_type,
3780 .is_non_null,
3781 .is_non_null_ptr,
3782 .is_non_err,
3783 .is_non_err_ptr,
3784 .ret_is_non_err,
3785 .repeat,
3786 .repeat_inline,
3787 .for_len,
3788 .merge_error_sets,
3789 .ref,
3790 .ret_node,
3791 .ret_load,
3792 .ret_implicit,
3793 .ret_err_value,
3794 .ret_err_value_code,
3795 .ret_ptr,
3796 .ret_type,
3797 .ptr_type,
3798 .slice_start,
3799 .slice_end,
3800 .slice_sentinel,
3801 .slice_length,
3802 .store_node,
3803 .store_to_inferred_ptr,
3804 .str,
3805 .negate,
3806 .negate_wrap,
3807 .typeof,
3808 .typeof_log2_int_type,
3809 .@"unreachable",
3810 .xor,
3811 .optional_type,
3812 .optional_payload_safe,
3813 .optional_payload_unsafe,
3814 .optional_payload_safe_ptr,
3815 .optional_payload_unsafe_ptr,
3816 .err_union_payload_unsafe,
3817 .err_union_payload_unsafe_ptr,
3818 .err_union_code,
3819 .err_union_code_ptr,
3820 .enum_literal,
3821 .validate_deref,
3822 .validate_destructure,
3823 .field_type_ref,
3824 .opt_eu_base_ptr_init,
3825 .coerce_ptr_elem_ty,
3826 .validate_ref_ty,
3827 .struct_init_empty,
3828 .struct_init_empty_result,
3829 .struct_init_empty_ref_result,
3830 .struct_init_anon,
3831 .struct_init,
3832 .struct_init_ref,
3833 .validate_struct_init_ty,
3834 .validate_struct_init_result_ty,
3835 .validate_ptr_struct_init,
3836 .struct_init_field_type,
3837 .struct_init_field_ptr,
3838 .array_init_anon,
3839 .array_init,
3840 .array_init_ref,
3841 .validate_array_init_ty,
3842 .validate_array_init_result_ty,
3843 .validate_array_init_ref_ty,
3844 .validate_ptr_array_init,
3845 .array_init_elem_type,
3846 .array_init_elem_ptr,
3847 .union_init,
3848 .type_info,
3849 .size_of,
3850 .bit_size_of,
3851 .int_from_ptr,
3852 .compile_error,
3853 .set_eval_branch_quota,
3854 .int_from_enum,
3855 .align_of,
3856 .int_from_bool,
3857 .embed_file,
3858 .error_name,
3859 .panic,
3860 .trap,
3861 .set_runtime_safety,
3862 .sqrt,
3863 .sin,
3864 .cos,
3865 .tan,
3866 .exp,
3867 .exp2,
3868 .log,
3869 .log2,
3870 .log10,
3871 .abs,
3872 .floor,
3873 .ceil,
3874 .trunc,
3875 .round,
3876 .tag_name,
3877 .type_name,
3878 .frame_type,
3879 .frame_size,
3880 .int_from_float,
3881 .float_from_int,
3882 .ptr_from_int,
3883 .enum_from_int,
3884 .float_cast,
3885 .int_cast,
3886 .ptr_cast,
3887 .truncate,
3888 .has_decl,
3889 .has_field,
3890 .clz,
3891 .ctz,
3892 .pop_count,
3893 .byte_swap,
3894 .bit_reverse,
3895 .bit_offset_of,
3896 .offset_of,
3897 .splat,
3898 .reduce,
3899 .shuffle,
3900 .atomic_load,
3901 .atomic_rmw,
3902 .atomic_store,
3903 .mul_add,
3904 .memcpy,
3905 .memset,
3906 .min,
3907 .max,
3908 .alloc,
3909 .alloc_mut,
3910 .alloc_comptime_mut,
3911 .alloc_inferred,
3912 .alloc_inferred_mut,
3913 .alloc_inferred_comptime,
3914 .alloc_inferred_comptime_mut,
3915 .resolve_inferred_alloc,
3916 .make_ptr_const,
3917 .@"resume",
3918 .@"await",
3919 .save_err_ret_index,
3920 .restore_err_ret_index_unconditional,
3921 .restore_err_ret_index_fn_entry,
3922 => return,
3923
3924 .extended => {
3925 const extended = datas[@intFromEnum(inst)].extended;
3926 switch (extended.opcode) {
3927 .value_placeholder => unreachable,
3928
3929 // Once again, we start with the boring tags.
3930 .variable,
3931 .this,
3932 .ret_addr,
3933 .builtin_src,
3934 .error_return_trace,
3935 .frame,
3936 .frame_address,
3937 .alloc,
3938 .builtin_extern,
3939 .@"asm",
3940 .asm_expr,
3941 .compile_log,
3942 .min_multi,
3943 .max_multi,
3944 .add_with_overflow,
3945 .sub_with_overflow,
3946 .mul_with_overflow,
3947 .shl_with_overflow,
3948 .c_undef,
3949 .c_include,
3950 .c_define,
3951 .wasm_memory_size,
3952 .wasm_memory_grow,
3953 .prefetch,
3954 .fence,
3955 .set_float_mode,
3956 .set_align_stack,
3957 .set_cold,
3958 .error_cast,
3959 .await_nosuspend,
3960 .breakpoint,
3961 .disable_instrumentation,
3962 .select,
3963 .int_from_error,
3964 .error_from_int,
3965 .builtin_async_call,
3966 .cmpxchg,
3967 .c_va_arg,
3968 .c_va_copy,
3969 .c_va_end,
3970 .c_va_start,
3971 .ptr_cast_full,
3972 .ptr_cast_no_dest,
3973 .work_item_id,
3974 .work_group_size,
3975 .work_group_id,
3976 .in_comptime,
3977 .restore_err_ret_index,
3978 .closure_get,
3979 .field_parent_ptr,
3980 => return,
3981
3982 // `@TypeOf` has a body.
3983 .typeof_peer => {
3984 const extra = zir.extraData(Zir.Inst.TypeOfPeer, extended.operand);
3985 const body = zir.bodySlice(extra.data.body_index, extra.data.body_len);
3986 try zir.findDeclsBody(gpa, list, defers, body);
3987 },
3988
3989 // Reifications and opaque declarations need tracking, but have no body.
3990 .reify, .opaque_decl => return list.append(gpa, inst),
3991
3992 // Struct declarations need tracking and have bodies.
3993 .struct_decl => {
3994 try list.append(gpa, inst);
3995
3996 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
3997 const extra = zir.extraData(Zir.Inst.StructDecl, extended.operand);
3998 var extra_index = extra.end;
3999 const captures_len = if (small.has_captures_len) blk: {
4000 const captures_len = zir.extra[extra_index];
4001 extra_index += 1;
4002 break :blk captures_len;
4003 } else 0;
4004 const fields_len = if (small.has_fields_len) blk: {
4005 const fields_len = zir.extra[extra_index];
4006 extra_index += 1;
4007 break :blk fields_len;
4008 } else 0;
4009 const decls_len = if (small.has_decls_len) blk: {
4010 const decls_len = zir.extra[extra_index];
4011 extra_index += 1;
4012 break :blk decls_len;
4013 } else 0;
4014 extra_index += captures_len;
4015 if (small.has_backing_int) {
4016 const backing_int_body_len = zir.extra[extra_index];
4017 extra_index += 1;
4018 if (backing_int_body_len == 0) {
4019 extra_index += 1; // backing_int_ref
4020 } else {
4021 const body = zir.bodySlice(extra_index, backing_int_body_len);
4022 extra_index += backing_int_body_len;
4023 try zir.findDeclsBody(gpa, list, defers, body);
4024 }
4025 }
4026 extra_index += decls_len;
4027
4028 // This ZIR is structured in a slightly awkward way, so we have to split up the iteration.
4029 // `extra_index` iterates `flags` (bags of bits).
4030 // `fields_extra_index` iterates `fields`.
4031 // We accumulate the total length of bodies into `total_bodies_len`. This is sufficient because
4032 // the bodies are packed together in `extra` and we only need to traverse their instructions (we
4033 // don't really care about the structure).
4034
4035 const bits_per_field = 4;
4036 const fields_per_u32 = 32 / bits_per_field;
4037 const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
4038 var cur_bit_bag: u32 = undefined;
4039
4040 var fields_extra_index = extra_index + bit_bags_count;
4041 var total_bodies_len: u32 = 0;
4042
4043 for (0..fields_len) |field_i| {
4044 if (field_i % fields_per_u32 == 0) {
4045 cur_bit_bag = zir.extra[extra_index];
4046 extra_index += 1;
4047 }
4048
4049 const has_align = @as(u1, @truncate(cur_bit_bag)) != 0;
4050 cur_bit_bag >>= 1;
4051 const has_init = @as(u1, @truncate(cur_bit_bag)) != 0;
4052 cur_bit_bag >>= 2; // also skip `is_comptime`; we don't care
4053 const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0;
4054 cur_bit_bag >>= 1;
4055
4056 fields_extra_index += @intFromBool(!small.is_tuple); // field_name
4057 fields_extra_index += 1; // doc_comment
4058
4059 if (has_type_body) {
4060 const field_type_body_len = zir.extra[fields_extra_index];
4061 total_bodies_len += field_type_body_len;
4062 }
4063 fields_extra_index += 1; // field_type or field_type_body_len
4064
4065 if (has_align) {
4066 const align_body_len = zir.extra[fields_extra_index];
4067 fields_extra_index += 1;
4068 total_bodies_len += align_body_len;
4069 }
4070
4071 if (has_init) {
4072 const init_body_len = zir.extra[fields_extra_index];
4073 fields_extra_index += 1;
4074 total_bodies_len += init_body_len;
4075 }
4076 }
4077
4078 // Now, `fields_extra_index` points to `bodies`. Let's treat this as one big body.
4079 const merged_bodies = zir.bodySlice(fields_extra_index, total_bodies_len);
4080 try zir.findDeclsBody(gpa, list, defers, merged_bodies);
4081 },
4082
4083 // Union declarations need tracking and have a body.
4084 .union_decl => {
4085 try list.append(gpa, inst);
4086
4087 const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small);
4088 const extra = zir.extraData(Zir.Inst.UnionDecl, extended.operand);
4089 var extra_index = extra.end;
4090 extra_index += @intFromBool(small.has_tag_type);
4091 const captures_len = if (small.has_captures_len) blk: {
4092 const captures_len = zir.extra[extra_index];
4093 extra_index += 1;
4094 break :blk captures_len;
4095 } else 0;
4096 const body_len = if (small.has_body_len) blk: {
4097 const body_len = zir.extra[extra_index];
4098 extra_index += 1;
4099 break :blk body_len;
4100 } else 0;
4101 extra_index += @intFromBool(small.has_fields_len);
4102 const decls_len = if (small.has_decls_len) blk: {
4103 const decls_len = zir.extra[extra_index];
4104 extra_index += 1;
4105 break :blk decls_len;
4106 } else 0;
4107 extra_index += captures_len;
4108 extra_index += decls_len;
4109 const body = zir.bodySlice(extra_index, body_len);
4110 try zir.findDeclsBody(gpa, list, defers, body);
4111 },
4112
4113 // Enum declarations need tracking and have a body.
4114 .enum_decl => {
4115 try list.append(gpa, inst);
4116
4117 const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small);
4118 const extra = zir.extraData(Zir.Inst.EnumDecl, extended.operand);
4119 var extra_index = extra.end;
4120 extra_index += @intFromBool(small.has_tag_type);
4121 const captures_len = if (small.has_captures_len) blk: {
4122 const captures_len = zir.extra[extra_index];
4123 extra_index += 1;
4124 break :blk captures_len;
4125 } else 0;
4126 const body_len = if (small.has_body_len) blk: {
4127 const body_len = zir.extra[extra_index];
4128 extra_index += 1;
4129 break :blk body_len;
4130 } else 0;
4131 extra_index += @intFromBool(small.has_fields_len);
4132 const decls_len = if (small.has_decls_len) blk: {
4133 const decls_len = zir.extra[extra_index];
4134 extra_index += 1;
4135 break :blk decls_len;
4136 } else 0;
4137 extra_index += captures_len;
4138 extra_index += decls_len;
4139 const body = zir.bodySlice(extra_index, body_len);
4140 try zir.findDeclsBody(gpa, list, defers, body);
4141 },
4142 }
4143 },
4144
3649 // Functions instructions are interesting and have a body.4145 // Functions instructions are interesting and have a body.
3650 .func,4146 .func,
3651 .func_inferred,4147 .func_inferred,
3652 => {4148 => {
3653 try list.append(inst);4149 try list.append(gpa, inst);
36544150
3655 const inst_data = datas[@intFromEnum(inst)].pl_node;4151 const inst_data = datas[@intFromEnum(inst)].pl_node;
3656 const extra = zir.extraData(Inst.Func, inst_data.payload_index);4152 const extra = zir.extraData(Inst.Func, inst_data.payload_index);
...@@ -3661,14 +4157,14 @@ fn findDeclsInner(...@@ -3661,14 +4157,14 @@ fn findDeclsInner(
3661 else => {4157 else => {
3662 const body = zir.bodySlice(extra_index, extra.data.ret_body_len);4158 const body = zir.bodySlice(extra_index, extra.data.ret_body_len);
3663 extra_index += body.len;4159 extra_index += body.len;
3664 try zir.findDeclsBody(list, body);4160 try zir.findDeclsBody(gpa, list, defers, body);
3665 },4161 },
3666 }4162 }
3667 const body = zir.bodySlice(extra_index, extra.data.body_len);4163 const body = zir.bodySlice(extra_index, extra.data.body_len);
3668 return zir.findDeclsBody(list, body);4164 return zir.findDeclsBody(gpa, list, defers, body);
3669 },4165 },
3670 .func_fancy => {4166 .func_fancy => {
3671 try list.append(inst);4167 try list.append(gpa, inst);
36724168
3673 const inst_data = datas[@intFromEnum(inst)].pl_node;4169 const inst_data = datas[@intFromEnum(inst)].pl_node;
3674 const extra = zir.extraData(Inst.FuncFancy, inst_data.payload_index);4170 const extra = zir.extraData(Inst.FuncFancy, inst_data.payload_index);
...@@ -3679,7 +4175,7 @@ fn findDeclsInner(...@@ -3679,7 +4175,7 @@ fn findDeclsInner(
3679 const body_len = zir.extra[extra_index];4175 const body_len = zir.extra[extra_index];
3680 extra_index += 1;4176 extra_index += 1;
3681 const body = zir.bodySlice(extra_index, body_len);4177 const body = zir.bodySlice(extra_index, body_len);
3682 try zir.findDeclsBody(list, body);4178 try zir.findDeclsBody(gpa, list, defers, body);
3683 extra_index += body.len;4179 extra_index += body.len;
3684 } else if (extra.data.bits.has_align_ref) {4180 } else if (extra.data.bits.has_align_ref) {
3685 extra_index += 1;4181 extra_index += 1;
...@@ -3689,7 +4185,7 @@ fn findDeclsInner(...@@ -3689,7 +4185,7 @@ fn findDeclsInner(
3689 const body_len = zir.extra[extra_index];4185 const body_len = zir.extra[extra_index];
3690 extra_index += 1;4186 extra_index += 1;
3691 const body = zir.bodySlice(extra_index, body_len);4187 const body = zir.bodySlice(extra_index, body_len);
3692 try zir.findDeclsBody(list, body);4188 try zir.findDeclsBody(gpa, list, defers, body);
3693 extra_index += body.len;4189 extra_index += body.len;
3694 } else if (extra.data.bits.has_addrspace_ref) {4190 } else if (extra.data.bits.has_addrspace_ref) {
3695 extra_index += 1;4191 extra_index += 1;
...@@ -3699,7 +4195,7 @@ fn findDeclsInner(...@@ -3699,7 +4195,7 @@ fn findDeclsInner(
3699 const body_len = zir.extra[extra_index];4195 const body_len = zir.extra[extra_index];
3700 extra_index += 1;4196 extra_index += 1;
3701 const body = zir.bodySlice(extra_index, body_len);4197 const body = zir.bodySlice(extra_index, body_len);
3702 try zir.findDeclsBody(list, body);4198 try zir.findDeclsBody(gpa, list, defers, body);
3703 extra_index += body.len;4199 extra_index += body.len;
3704 } else if (extra.data.bits.has_section_ref) {4200 } else if (extra.data.bits.has_section_ref) {
3705 extra_index += 1;4201 extra_index += 1;
...@@ -3709,7 +4205,7 @@ fn findDeclsInner(...@@ -3709,7 +4205,7 @@ fn findDeclsInner(
3709 const body_len = zir.extra[extra_index];4205 const body_len = zir.extra[extra_index];
3710 extra_index += 1;4206 extra_index += 1;
3711 const body = zir.bodySlice(extra_index, body_len);4207 const body = zir.bodySlice(extra_index, body_len);
3712 try zir.findDeclsBody(list, body);4208 try zir.findDeclsBody(gpa, list, defers, body);
3713 extra_index += body.len;4209 extra_index += body.len;
3714 } else if (extra.data.bits.has_cc_ref) {4210 } else if (extra.data.bits.has_cc_ref) {
3715 extra_index += 1;4211 extra_index += 1;
...@@ -3719,7 +4215,7 @@ fn findDeclsInner(...@@ -3719,7 +4215,7 @@ fn findDeclsInner(
3719 const body_len = zir.extra[extra_index];4215 const body_len = zir.extra[extra_index];
3720 extra_index += 1;4216 extra_index += 1;
3721 const body = zir.bodySlice(extra_index, body_len);4217 const body = zir.bodySlice(extra_index, body_len);
3722 try zir.findDeclsBody(list, body);4218 try zir.findDeclsBody(gpa, list, defers, body);
3723 extra_index += body.len;4219 extra_index += body.len;
3724 } else if (extra.data.bits.has_ret_ty_ref) {4220 } else if (extra.data.bits.has_ret_ty_ref) {
3725 extra_index += 1;4221 extra_index += 1;
...@@ -3728,62 +4224,99 @@ fn findDeclsInner(...@@ -3728,62 +4224,99 @@ fn findDeclsInner(
3728 extra_index += @intFromBool(extra.data.bits.has_any_noalias);4224 extra_index += @intFromBool(extra.data.bits.has_any_noalias);
37294225
3730 const body = zir.bodySlice(extra_index, extra.data.body_len);4226 const body = zir.bodySlice(extra_index, extra.data.body_len);
3731 return zir.findDeclsBody(list, body);4227 return zir.findDeclsBody(gpa, list, defers, body);
3732 },
3733 .extended => {
3734 const extended = datas[@intFromEnum(inst)].extended;
3735 switch (extended.opcode) {
3736
3737 // Decl instructions are interesting but have no body.
3738 // TODO yes they do have a body actually. recurse over them just like block instructions.
3739 .struct_decl,
3740 .union_decl,
3741 .enum_decl,
3742 .opaque_decl,
3743 .reify,
3744 => return list.append(inst),
3745
3746 else => return,
3747 }
3748 },4228 },
37494229
3750 // Block instructions, recurse over the bodies.4230 // Block instructions, recurse over the bodies.
37514231
3752 .block, .block_comptime, .block_inline => {4232 .block,
4233 .block_comptime,
4234 .block_inline,
4235 .c_import,
4236 .typeof_builtin,
4237 .loop,
4238 => {
3753 const inst_data = datas[@intFromEnum(inst)].pl_node;4239 const inst_data = datas[@intFromEnum(inst)].pl_node;
3754 const extra = zir.extraData(Inst.Block, inst_data.payload_index);4240 const extra = zir.extraData(Inst.Block, inst_data.payload_index);
3755 const body = zir.bodySlice(extra.end, extra.data.body_len);4241 const body = zir.bodySlice(extra.end, extra.data.body_len);
3756 return zir.findDeclsBody(list, body);4242 return zir.findDeclsBody(gpa, list, defers, body);
3757 },4243 },
3758 .condbr, .condbr_inline => {4244 .condbr, .condbr_inline => {
3759 const inst_data = datas[@intFromEnum(inst)].pl_node;4245 const inst_data = datas[@intFromEnum(inst)].pl_node;
3760 const extra = zir.extraData(Inst.CondBr, inst_data.payload_index);4246 const extra = zir.extraData(Inst.CondBr, inst_data.payload_index);
3761 const then_body = zir.bodySlice(extra.end, extra.data.then_body_len);4247 const then_body = zir.bodySlice(extra.end, extra.data.then_body_len);
3762 const else_body = zir.bodySlice(extra.end + then_body.len, extra.data.else_body_len);4248 const else_body = zir.bodySlice(extra.end + then_body.len, extra.data.else_body_len);
3763 try zir.findDeclsBody(list, then_body);4249 try zir.findDeclsBody(gpa, list, defers, then_body);
3764 try zir.findDeclsBody(list, else_body);4250 try zir.findDeclsBody(gpa, list, defers, else_body);
3765 },4251 },
3766 .@"try", .try_ptr => {4252 .@"try", .try_ptr => {
3767 const inst_data = datas[@intFromEnum(inst)].pl_node;4253 const inst_data = datas[@intFromEnum(inst)].pl_node;
3768 const extra = zir.extraData(Inst.Try, inst_data.payload_index);4254 const extra = zir.extraData(Inst.Try, inst_data.payload_index);
3769 const body = zir.bodySlice(extra.end, extra.data.body_len);4255 const body = zir.bodySlice(extra.end, extra.data.body_len);
3770 try zir.findDeclsBody(list, body);4256 try zir.findDeclsBody(gpa, list, defers, body);
3771 },4257 },
3772 .switch_block => return findDeclsSwitch(zir, list, inst),4258 .switch_block, .switch_block_ref => return zir.findDeclsSwitch(gpa, list, defers, inst, .normal),
4259 .switch_block_err_union => return zir.findDeclsSwitch(gpa, list, defers, inst, .err_union),
37734260
3774 .suspend_block => @panic("TODO iterate suspend block"),4261 .suspend_block => @panic("TODO iterate suspend block"),
37754262
3776 else => return, // Regular instruction, not interesting.4263 .param, .param_comptime => {
4264 const inst_data = datas[@intFromEnum(inst)].pl_tok;
4265 const extra = zir.extraData(Inst.Param, inst_data.payload_index);
4266 const body = zir.bodySlice(extra.end, extra.data.body_len);
4267 try zir.findDeclsBody(gpa, list, defers, body);
4268 },
4269
4270 inline .call, .field_call => |tag| {
4271 const inst_data = datas[@intFromEnum(inst)].pl_node;
4272 const extra = zir.extraData(switch (tag) {
4273 .call => Inst.Call,
4274 .field_call => Inst.FieldCall,
4275 else => unreachable,
4276 }, inst_data.payload_index);
4277 // It's easiest to just combine all the arg bodies into one body, like we do above for `struct_decl`.
4278 const args_len = extra.data.flags.args_len;
4279 if (args_len > 0) {
4280 const first_arg_start_off = args_len;
4281 const final_arg_end_off = zir.extra[extra.end + args_len - 1];
4282 const args_body = zir.bodySlice(extra.end + first_arg_start_off, final_arg_end_off - first_arg_start_off);
4283 try zir.findDeclsBody(gpa, list, defers, args_body);
4284 }
4285 },
4286 .@"defer" => {
4287 const inst_data = datas[@intFromEnum(inst)].@"defer";
4288 const gop = try defers.getOrPut(gpa, inst_data.index);
4289 if (!gop.found_existing) {
4290 const body = zir.bodySlice(inst_data.index, inst_data.len);
4291 try zir.findDeclsBody(gpa, list, defers, body);
4292 }
4293 },
4294 .defer_err_code => {
4295 const inst_data = datas[@intFromEnum(inst)].defer_err_code;
4296 const extra = zir.extraData(Inst.DeferErrCode, inst_data.payload_index).data;
4297 const gop = try defers.getOrPut(gpa, extra.index);
4298 if (!gop.found_existing) {
4299 const body = zir.bodySlice(extra.index, extra.len);
4300 try zir.findDeclsBody(gpa, list, defers, body);
4301 }
4302 },
3777 }4303 }
3778}4304}
37794305
3780fn findDeclsSwitch(4306fn findDeclsSwitch(
3781 zir: Zir,4307 zir: Zir,
3782 list: *std.ArrayList(Inst.Index),4308 gpa: Allocator,
4309 list: *std.ArrayListUnmanaged(Inst.Index),
4310 defers: *std.AutoHashMapUnmanaged(u32, void),
3783 inst: Inst.Index,4311 inst: Inst.Index,
4312 /// Distinguishes between `switch_block[_ref]` and `switch_block_err_union`.
4313 comptime kind: enum { normal, err_union },
3784) Allocator.Error!void {4314) Allocator.Error!void {
3785 const inst_data = zir.instructions.items(.data)[@intFromEnum(inst)].pl_node;4315 const inst_data = zir.instructions.items(.data)[@intFromEnum(inst)].pl_node;
3786 const extra = zir.extraData(Inst.SwitchBlock, inst_data.payload_index);4316 const extra = zir.extraData(switch (kind) {
4317 .normal => Inst.SwitchBlock,
4318 .err_union => Inst.SwitchBlockErrUnion,
4319 }, inst_data.payload_index);
37874320
3788 var extra_index: usize = extra.end;4321 var extra_index: usize = extra.end;
37894322
...@@ -3793,18 +4326,35 @@ fn findDeclsSwitch(...@@ -3793,18 +4326,35 @@ fn findDeclsSwitch(
3793 break :blk multi_cases_len;4326 break :blk multi_cases_len;
3794 } else 0;4327 } else 0;
37954328
3796 if (extra.data.bits.any_has_tag_capture) {4329 if (switch (kind) {
4330 .normal => extra.data.bits.any_has_tag_capture,
4331 .err_union => extra.data.bits.any_uses_err_capture,
4332 }) {
3797 extra_index += 1;4333 extra_index += 1;
3798 }4334 }
37994335
3800 const special_prong = extra.data.bits.specialProng();4336 const has_special = switch (kind) {
3801 if (special_prong != .none) {4337 .normal => extra.data.bits.specialProng() != .none,
4338 .err_union => has_special: {
4339 // Handle `non_err_body` first.
4340 const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);
4341 extra_index += 1;
4342 const body = zir.bodySlice(extra_index, prong_info.body_len);
4343 extra_index += body.len;
4344
4345 try zir.findDeclsBody(gpa, list, defers, body);
4346
4347 break :has_special extra.data.bits.has_else;
4348 },
4349 };
4350
4351 if (has_special) {
3802 const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);4352 const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);
3803 extra_index += 1;4353 extra_index += 1;
3804 const body = zir.bodySlice(extra_index, prong_info.body_len);4354 const body = zir.bodySlice(extra_index, prong_info.body_len);
3805 extra_index += body.len;4355 extra_index += body.len;
38064356
3807 try zir.findDeclsBody(list, body);4357 try zir.findDeclsBody(gpa, list, defers, body);
3808 }4358 }
38094359
3810 {4360 {
...@@ -3816,7 +4366,7 @@ fn findDeclsSwitch(...@@ -3816,7 +4366,7 @@ fn findDeclsSwitch(
3816 const body = zir.bodySlice(extra_index, prong_info.body_len);4366 const body = zir.bodySlice(extra_index, prong_info.body_len);
3817 extra_index += body.len;4367 extra_index += body.len;
38184368
3819 try zir.findDeclsBody(list, body);4369 try zir.findDeclsBody(gpa, list, defers, body);
3820 }4370 }
3821 }4371 }
3822 {4372 {
...@@ -3833,18 +4383,20 @@ fn findDeclsSwitch(...@@ -3833,18 +4383,20 @@ fn findDeclsSwitch(
3833 const body = zir.bodySlice(extra_index, prong_info.body_len);4383 const body = zir.bodySlice(extra_index, prong_info.body_len);
3834 extra_index += body.len;4384 extra_index += body.len;
38354385
3836 try zir.findDeclsBody(list, body);4386 try zir.findDeclsBody(gpa, list, defers, body);
3837 }4387 }
3838 }4388 }
3839}4389}
38404390
3841fn findDeclsBody(4391fn findDeclsBody(
3842 zir: Zir,4392 zir: Zir,
3843 list: *std.ArrayList(Inst.Index),4393 gpa: Allocator,
4394 list: *std.ArrayListUnmanaged(Inst.Index),
4395 defers: *std.AutoHashMapUnmanaged(u32, void),
3844 body: []const Inst.Index,4396 body: []const Inst.Index,
3845) Allocator.Error!void {4397) Allocator.Error!void {
3846 for (body) |member| {4398 for (body) |member| {
3847 try zir.findDeclsInner(list, member);4399 try zir.findDeclsInner(gpa, list, defers, member);
3848 }4400 }
3849}4401}
38504402
...@@ -4042,7 +4594,7 @@ pub fn getAssociatedSrcHash(zir: Zir, inst: Zir.Inst.Index) ?std.zig.SrcHash {...@@ -4042,7 +4594,7 @@ pub fn getAssociatedSrcHash(zir: Zir, inst: Zir.Inst.Index) ?std.zig.SrcHash {
4042 return null;4594 return null;
4043 }4595 }
4044 const extra_index = extra.end +4596 const extra_index = extra.end +
4045 1 +4597 extra.data.ret_body_len +
4046 extra.data.body_len +4598 extra.data.body_len +
4047 @typeInfo(Inst.Func.SrcLocs).Struct.fields.len;4599 @typeInfo(Inst.Func.SrcLocs).Struct.fields.len;
4048 return @bitCast([4]u32{4600 return @bitCast([4]u32{
src/Compilation.zig+74-104
...@@ -2264,13 +2264,19 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {...@@ -2264,13 +2264,19 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
2264 }2264 }
2265 }2265 }
22662266
2267 zcu.analysis_roots.clear();
2268
2267 try comp.queueJob(.{ .analyze_mod = std_mod });2269 try comp.queueJob(.{ .analyze_mod = std_mod });
2268 if (comp.config.is_test) {2270 zcu.analysis_roots.appendAssumeCapacity(std_mod);
2271
2272 if (comp.config.is_test and zcu.main_mod != std_mod) {
2269 try comp.queueJob(.{ .analyze_mod = zcu.main_mod });2273 try comp.queueJob(.{ .analyze_mod = zcu.main_mod });
2274 zcu.analysis_roots.appendAssumeCapacity(zcu.main_mod);
2270 }2275 }
22712276
2272 if (zcu.root_mod.deps.get("compiler_rt")) |compiler_rt_mod| {2277 if (zcu.root_mod.deps.get("compiler_rt")) |compiler_rt_mod| {
2273 try comp.queueJob(.{ .analyze_mod = compiler_rt_mod });2278 try comp.queueJob(.{ .analyze_mod = compiler_rt_mod });
2279 zcu.analysis_roots.appendAssumeCapacity(compiler_rt_mod);
2274 }2280 }
2275 }2281 }
22762282
...@@ -2294,7 +2300,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {...@@ -2294,7 +2300,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
2294 zcu.intern_pool.dumpGenericInstances(gpa);2300 zcu.intern_pool.dumpGenericInstances(gpa);
2295 }2301 }
22962302
2297 if (comp.config.is_test and comp.totalErrorCount() == 0) {2303 if (comp.config.is_test) {
2298 // The `test_functions` decl has been intentionally postponed until now,2304 // The `test_functions` decl has been intentionally postponed until now,
2299 // at which point we must populate it with the list of test functions that2305 // at which point we must populate it with the list of test functions that
2300 // have been discovered and not filtered out.2306 // have been discovered and not filtered out.
...@@ -2304,7 +2310,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {...@@ -2304,7 +2310,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
2304 try pt.processExports();2310 try pt.processExports();
2305 }2311 }
23062312
2307 if (comp.totalErrorCount() != 0) {2313 if (try comp.totalErrorCount() != 0) {
2308 // Skip flushing and keep source files loaded for error reporting.2314 // Skip flushing and keep source files loaded for error reporting.
2309 comp.link_error_flags = .{};2315 comp.link_error_flags = .{};
2310 return;2316 return;
...@@ -2388,7 +2394,8 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {...@@ -2388,7 +2394,8 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
2388 }2394 }
23892395
2390 try flush(comp, arena, .main, main_progress_node);2396 try flush(comp, arena, .main, main_progress_node);
2391 if (comp.totalErrorCount() != 0) return;2397
2398 if (try comp.totalErrorCount() != 0) return;
23922399
2393 // Failure here only means an unnecessary cache miss.2400 // Failure here only means an unnecessary cache miss.
2394 man.writeManifest() catch |err| {2401 man.writeManifest() catch |err| {
...@@ -2405,7 +2412,6 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {...@@ -2405,7 +2412,6 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
2405 },2412 },
2406 .incremental => {2413 .incremental => {
2407 try flush(comp, arena, .main, main_progress_node);2414 try flush(comp, arena, .main, main_progress_node);
2408 if (comp.totalErrorCount() != 0) return;
2409 },2415 },
2410 }2416 }
2411}2417}
...@@ -3041,82 +3047,6 @@ fn addBuf(list: *std.ArrayList(std.posix.iovec_const), buf: []const u8) void {...@@ -3041,82 +3047,6 @@ fn addBuf(list: *std.ArrayList(std.posix.iovec_const), buf: []const u8) void {
3041 list.appendAssumeCapacity(.{ .base = buf.ptr, .len = buf.len });3047 list.appendAssumeCapacity(.{ .base = buf.ptr, .len = buf.len });
3042}3048}
30433049
3044/// This function is temporally single-threaded.
3045pub fn totalErrorCount(comp: *Compilation) u32 {
3046 var total: usize =
3047 comp.misc_failures.count() +
3048 @intFromBool(comp.alloc_failure_occurred) +
3049 comp.lld_errors.items.len;
3050
3051 for (comp.failed_c_objects.values()) |bundle| {
3052 total += bundle.diags.len;
3053 }
3054
3055 for (comp.failed_win32_resources.values()) |errs| {
3056 total += errs.errorMessageCount();
3057 }
3058
3059 if (comp.module) |zcu| {
3060 const ip = &zcu.intern_pool;
3061
3062 total += zcu.failed_exports.count();
3063 total += zcu.failed_embed_files.count();
3064
3065 for (zcu.failed_files.keys(), zcu.failed_files.values()) |file, error_msg| {
3066 if (error_msg) |_| {
3067 total += 1;
3068 } else {
3069 assert(file.zir_loaded);
3070 const payload_index = file.zir.extra[@intFromEnum(Zir.ExtraIndex.compile_errors)];
3071 assert(payload_index != 0);
3072 const header = file.zir.extraData(Zir.Inst.CompileErrors, payload_index);
3073 total += header.data.items_len;
3074 }
3075 }
3076
3077 // Skip errors for Decls within files that failed parsing.
3078 // When a parse error is introduced, we keep all the semantic analysis for
3079 // the previous parse success, including compile errors, but we cannot
3080 // emit them until the file succeeds parsing.
3081 for (zcu.failed_analysis.keys()) |anal_unit| {
3082 const file_index = switch (anal_unit.unwrap()) {
3083 .cau => |cau| zcu.namespacePtr(ip.getCau(cau).namespace).file_scope,
3084 .func => |ip_index| zcu.funcInfo(ip_index).zir_body_inst.resolveFull(ip).file,
3085 };
3086 if (zcu.fileByIndex(file_index).okToReportErrors()) {
3087 total += 1;
3088 if (zcu.cimport_errors.get(anal_unit)) |errors| {
3089 total += errors.errorMessageCount();
3090 }
3091 }
3092 }
3093
3094 if (zcu.intern_pool.global_error_set.getNamesFromMainThread().len > zcu.error_limit) {
3095 total += 1;
3096 }
3097
3098 for (zcu.failed_codegen.keys()) |_| {
3099 total += 1;
3100 }
3101 }
3102
3103 // The "no entry point found" error only counts if there are no semantic analysis errors.
3104 if (total == 0) {
3105 total += @intFromBool(comp.link_error_flags.no_entry_point_found);
3106 }
3107 total += @intFromBool(comp.link_error_flags.missing_libc);
3108 total += comp.link_errors.items.len;
3109
3110 // Compile log errors only count if there are no other errors.
3111 if (total == 0) {
3112 if (comp.module) |zcu| {
3113 total += @intFromBool(zcu.compile_log_sources.count() != 0);
3114 }
3115 }
3116
3117 return @as(u32, @intCast(total));
3118}
3119
3120/// This function is temporally single-threaded.3050/// This function is temporally single-threaded.
3121pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {3051pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
3122 const gpa = comp.gpa;3052 const gpa = comp.gpa;
...@@ -3159,12 +3089,13 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -3159,12 +3089,13 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
3159 .msg = try bundle.addString("memory allocation failure"),3089 .msg = try bundle.addString("memory allocation failure"),
3160 });3090 });
3161 }3091 }
3092
3093 var all_references: ?std.AutoHashMapUnmanaged(InternPool.AnalUnit, ?Zcu.ResolvedReference) = null;
3094 defer if (all_references) |*a| a.deinit(gpa);
3095
3162 if (comp.module) |zcu| {3096 if (comp.module) |zcu| {
3163 const ip = &zcu.intern_pool;3097 const ip = &zcu.intern_pool;
31643098
3165 var all_references = try zcu.resolveReferences();
3166 defer all_references.deinit(gpa);
3167
3168 for (zcu.failed_files.keys(), zcu.failed_files.values()) |file, error_msg| {3099 for (zcu.failed_files.keys(), zcu.failed_files.values()) |file, error_msg| {
3169 if (error_msg) |msg| {3100 if (error_msg) |msg| {
3170 try addModuleErrorMsg(zcu, &bundle, msg.*, &all_references);3101 try addModuleErrorMsg(zcu, &bundle, msg.*, &all_references);
...@@ -3190,8 +3121,14 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -3190,8 +3121,14 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
3190 pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool {3121 pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool {
3191 if (ctx.err.*) |_| return lhs_index < rhs_index;3122 if (ctx.err.*) |_| return lhs_index < rhs_index;
3192 const errors = ctx.zcu.failed_analysis.values();3123 const errors = ctx.zcu.failed_analysis.values();
3193 const lhs_src_loc = errors[lhs_index].src_loc.upgrade(ctx.zcu);3124 const lhs_src_loc = errors[lhs_index].src_loc.upgradeOrLost(ctx.zcu) orelse {
3194 const rhs_src_loc = errors[rhs_index].src_loc.upgrade(ctx.zcu);3125 // LHS source location lost, so should never be referenced. Just sort it to the end.
3126 return false;
3127 };
3128 const rhs_src_loc = errors[rhs_index].src_loc.upgradeOrLost(ctx.zcu) orelse {
3129 // RHS source location lost, so should never be referenced. Just sort it to the end.
3130 return true;
3131 };
3195 return if (lhs_src_loc.file_scope != rhs_src_loc.file_scope) std.mem.order(3132 return if (lhs_src_loc.file_scope != rhs_src_loc.file_scope) std.mem.order(
3196 u8,3133 u8,
3197 lhs_src_loc.file_scope.sub_file_path,3134 lhs_src_loc.file_scope.sub_file_path,
...@@ -3212,9 +3149,16 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -3212,9 +3149,16 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
3212 if (err) |e| return e;3149 if (err) |e| return e;
3213 }3150 }
3214 for (zcu.failed_analysis.keys(), zcu.failed_analysis.values()) |anal_unit, error_msg| {3151 for (zcu.failed_analysis.keys(), zcu.failed_analysis.values()) |anal_unit, error_msg| {
3152 if (comp.incremental) {
3153 if (all_references == null) {
3154 all_references = try zcu.resolveReferences();
3155 }
3156 if (!all_references.?.contains(anal_unit)) continue;
3157 }
3158
3215 const file_index = switch (anal_unit.unwrap()) {3159 const file_index = switch (anal_unit.unwrap()) {
3216 .cau => |cau| zcu.namespacePtr(ip.getCau(cau).namespace).file_scope,3160 .cau => |cau| zcu.namespacePtr(ip.getCau(cau).namespace).file_scope,
3217 .func => |ip_index| zcu.funcInfo(ip_index).zir_body_inst.resolveFull(ip).file,3161 .func => |ip_index| (zcu.funcInfo(ip_index).zir_body_inst.resolveFull(ip) orelse continue).file,
3218 };3162 };
32193163
3220 // Skip errors for AnalUnits within files that had a parse failure.3164 // Skip errors for AnalUnits within files that had a parse failure.
...@@ -3243,7 +3187,8 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -3243,7 +3187,8 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
3243 }3187 }
3244 }3188 }
3245 }3189 }
3246 for (zcu.failed_codegen.values()) |error_msg| {3190 for (zcu.failed_codegen.keys(), zcu.failed_codegen.values()) |nav, error_msg| {
3191 if (!zcu.navFileScope(nav).okToReportErrors()) continue;
3247 try addModuleErrorMsg(zcu, &bundle, error_msg.*, &all_references);3192 try addModuleErrorMsg(zcu, &bundle, error_msg.*, &all_references);
3248 }3193 }
3249 for (zcu.failed_exports.values()) |value| {3194 for (zcu.failed_exports.values()) |value| {
...@@ -3304,9 +3249,6 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -3304,9 +3249,6 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
33043249
3305 if (comp.module) |zcu| {3250 if (comp.module) |zcu| {
3306 if (bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) {3251 if (bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) {
3307 var all_references = try zcu.resolveReferences();
3308 defer all_references.deinit(gpa);
3309
3310 const values = zcu.compile_log_sources.values();3252 const values = zcu.compile_log_sources.values();
3311 // First one will be the error; subsequent ones will be notes.3253 // First one will be the error; subsequent ones will be notes.
3312 const src_loc = values[0].src();3254 const src_loc = values[0].src();
...@@ -3328,12 +3270,30 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -3328,12 +3270,30 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
3328 }3270 }
3329 }3271 }
33303272
3331 assert(comp.totalErrorCount() == bundle.root_list.items.len);3273 if (comp.module) |zcu| {
3274 if (comp.incremental and bundle.root_list.items.len == 0) {
3275 const should_have_error = for (zcu.transitive_failed_analysis.keys()) |failed_unit| {
3276 if (all_references == null) {
3277 all_references = try zcu.resolveReferences();
3278 }
3279 if (all_references.?.contains(failed_unit)) break true;
3280 } else false;
3281 if (should_have_error) {
3282 @panic("referenced transitive analysis errors, but none actually emitted");
3283 }
3284 }
3285 }
33323286
3333 const compile_log_text = if (comp.module) |m| m.compile_log_text.items else "";3287 const compile_log_text = if (comp.module) |m| m.compile_log_text.items else "";
3334 return bundle.toOwnedBundle(compile_log_text);3288 return bundle.toOwnedBundle(compile_log_text);
3335}3289}
33363290
3291fn totalErrorCount(comp: *Compilation) !u32 {
3292 var errors = try comp.getAllErrorsAlloc();
3293 defer errors.deinit(comp.gpa);
3294 return errors.errorMessageCount();
3295}
3296
3337pub const ErrorNoteHashContext = struct {3297pub const ErrorNoteHashContext = struct {
3338 eb: *const ErrorBundle.Wip,3298 eb: *const ErrorBundle.Wip,
33393299
...@@ -3384,7 +3344,7 @@ pub fn addModuleErrorMsg(...@@ -3384,7 +3344,7 @@ pub fn addModuleErrorMsg(
3384 mod: *Zcu,3344 mod: *Zcu,
3385 eb: *ErrorBundle.Wip,3345 eb: *ErrorBundle.Wip,
3386 module_err_msg: Zcu.ErrorMsg,3346 module_err_msg: Zcu.ErrorMsg,
3387 all_references: *const std.AutoHashMapUnmanaged(InternPool.AnalUnit, Zcu.ResolvedReference),3347 all_references: *?std.AutoHashMapUnmanaged(InternPool.AnalUnit, ?Zcu.ResolvedReference),
3388) !void {3348) !void {
3389 const gpa = eb.gpa;3349 const gpa = eb.gpa;
3390 const ip = &mod.intern_pool;3350 const ip = &mod.intern_pool;
...@@ -3408,13 +3368,18 @@ pub fn addModuleErrorMsg(...@@ -3408,13 +3368,18 @@ pub fn addModuleErrorMsg(
3408 defer ref_traces.deinit(gpa);3368 defer ref_traces.deinit(gpa);
34093369
3410 if (module_err_msg.reference_trace_root.unwrap()) |rt_root| {3370 if (module_err_msg.reference_trace_root.unwrap()) |rt_root| {
3371 if (all_references.* == null) {
3372 all_references.* = try mod.resolveReferences();
3373 }
3374
3411 var seen: std.AutoHashMapUnmanaged(InternPool.AnalUnit, void) = .{};3375 var seen: std.AutoHashMapUnmanaged(InternPool.AnalUnit, void) = .{};
3412 defer seen.deinit(gpa);3376 defer seen.deinit(gpa);
34133377
3414 const max_references = mod.comp.reference_trace orelse Sema.default_reference_trace_len;3378 const max_references = mod.comp.reference_trace orelse Sema.default_reference_trace_len;
34153379
3416 var referenced_by = rt_root;3380 var referenced_by = rt_root;
3417 while (all_references.get(referenced_by)) |ref| {3381 while (all_references.*.?.get(referenced_by)) |maybe_ref| {
3382 const ref = maybe_ref orelse break;
3418 const gop = try seen.getOrPut(gpa, ref.referencer);3383 const gop = try seen.getOrPut(gpa, ref.referencer);
3419 if (gop.found_existing) break;3384 if (gop.found_existing) break;
3420 if (ref_traces.items.len < max_references) {3385 if (ref_traces.items.len < max_references) {
...@@ -3423,6 +3388,7 @@ pub fn addModuleErrorMsg(...@@ -3423,6 +3388,7 @@ pub fn addModuleErrorMsg(
3423 const span = try src.span(gpa);3388 const span = try src.span(gpa);
3424 const loc = std.zig.findLineColumn(source.bytes, span.main);3389 const loc = std.zig.findLineColumn(source.bytes, span.main);
3425 const rt_file_path = try src.file_scope.fullPath(gpa);3390 const rt_file_path = try src.file_scope.fullPath(gpa);
3391 defer gpa.free(rt_file_path);
3426 const name = switch (ref.referencer.unwrap()) {3392 const name = switch (ref.referencer.unwrap()) {
3427 .cau => |cau| switch (ip.getCau(cau).owner.unwrap()) {3393 .cau => |cau| switch (ip.getCau(cau).owner.unwrap()) {
3428 .nav => |nav| ip.getNav(nav).name.toSlice(ip),3394 .nav => |nav| ip.getNav(nav).name.toSlice(ip),
...@@ -3537,6 +3503,8 @@ pub fn performAllTheWork(...@@ -3537,6 +3503,8 @@ pub fn performAllTheWork(
3537 mod.sema_prog_node = std.Progress.Node.none;3503 mod.sema_prog_node = std.Progress.Node.none;
3538 mod.codegen_prog_node.end();3504 mod.codegen_prog_node.end();
3539 mod.codegen_prog_node = std.Progress.Node.none;3505 mod.codegen_prog_node = std.Progress.Node.none;
3506
3507 mod.generation += 1;
3540 };3508 };
3541 try comp.performAllTheWorkInner(main_progress_node);3509 try comp.performAllTheWorkInner(main_progress_node);
3542 if (!InternPool.single_threaded) if (comp.codegen_work.job_error) |job_error| return job_error;3510 if (!InternPool.single_threaded) if (comp.codegen_work.job_error) |job_error| return job_error;
...@@ -3608,10 +3576,9 @@ fn performAllTheWorkInner(...@@ -3608,10 +3576,9 @@ fn performAllTheWorkInner(
3608 // Pre-load these things from our single-threaded context since they3576 // Pre-load these things from our single-threaded context since they
3609 // will be needed by the worker threads.3577 // will be needed by the worker threads.
3610 const path_digest = zcu.filePathDigest(file_index);3578 const path_digest = zcu.filePathDigest(file_index);
3611 const old_root_type = zcu.fileRootType(file_index);
3612 const file = zcu.fileByIndex(file_index);3579 const file = zcu.fileByIndex(file_index);
3613 comp.thread_pool.spawnWgId(&astgen_wait_group, workerAstGenFile, .{3580 comp.thread_pool.spawnWgId(&astgen_wait_group, workerAstGenFile, .{
3614 comp, file, file_index, path_digest, old_root_type, zir_prog_node, &astgen_wait_group, .root,3581 comp, file, file_index, path_digest, zir_prog_node, &astgen_wait_group, .root,
3615 });3582 });
3616 }3583 }
3617 }3584 }
...@@ -3649,11 +3616,15 @@ fn performAllTheWorkInner(...@@ -3649,11 +3616,15 @@ fn performAllTheWorkInner(
3649 }3616 }
3650 try reportMultiModuleErrors(pt);3617 try reportMultiModuleErrors(pt);
3651 try zcu.flushRetryableFailures();3618 try zcu.flushRetryableFailures();
3619
3652 zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0);3620 zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0);
3653 zcu.codegen_prog_node = main_progress_node.start("Code Generation", 0);3621 zcu.codegen_prog_node = main_progress_node.start("Code Generation", 0);
3654 }3622 }
36553623
3656 if (!InternPool.single_threaded) comp.thread_pool.spawnWgId(&work_queue_wait_group, codegenThread, .{comp});3624 if (!InternPool.single_threaded) {
3625 comp.codegen_work.done = false; // may be `true` from a prior update
3626 comp.thread_pool.spawnWgId(&work_queue_wait_group, codegenThread, .{comp});
3627 }
3657 defer if (!InternPool.single_threaded) {3628 defer if (!InternPool.single_threaded) {
3658 {3629 {
3659 comp.codegen_work.mutex.lock();3630 comp.codegen_work.mutex.lock();
...@@ -4283,7 +4254,6 @@ fn workerAstGenFile(...@@ -4283,7 +4254,6 @@ fn workerAstGenFile(
4283 file: *Zcu.File,4254 file: *Zcu.File,
4284 file_index: Zcu.File.Index,4255 file_index: Zcu.File.Index,
4285 path_digest: Cache.BinDigest,4256 path_digest: Cache.BinDigest,
4286 old_root_type: InternPool.Index,
4287 prog_node: std.Progress.Node,4257 prog_node: std.Progress.Node,
4288 wg: *WaitGroup,4258 wg: *WaitGroup,
4289 src: Zcu.AstGenSrc,4259 src: Zcu.AstGenSrc,
...@@ -4292,7 +4262,7 @@ fn workerAstGenFile(...@@ -4292,7 +4262,7 @@ fn workerAstGenFile(
4292 defer child_prog_node.end();4262 defer child_prog_node.end();
42934263
4294 const pt: Zcu.PerThread = .{ .zcu = comp.module.?, .tid = @enumFromInt(tid) };4264 const pt: Zcu.PerThread = .{ .zcu = comp.module.?, .tid = @enumFromInt(tid) };
4295 pt.astGenFile(file, path_digest, old_root_type) catch |err| switch (err) {4265 pt.astGenFile(file, path_digest) catch |err| switch (err) {
4296 error.AnalysisFail => return,4266 error.AnalysisFail => return,
4297 else => {4267 else => {
4298 file.status = .retryable_failure;4268 file.status = .retryable_failure;
...@@ -4323,7 +4293,7 @@ fn workerAstGenFile(...@@ -4323,7 +4293,7 @@ fn workerAstGenFile(
4323 // `@import("builtin")` is handled specially.4293 // `@import("builtin")` is handled specially.
4324 if (mem.eql(u8, import_path, "builtin")) continue;4294 if (mem.eql(u8, import_path, "builtin")) continue;
43254295
4326 const import_result, const imported_path_digest, const imported_root_type = blk: {4296 const import_result, const imported_path_digest = blk: {
4327 comp.mutex.lock();4297 comp.mutex.lock();
4328 defer comp.mutex.unlock();4298 defer comp.mutex.unlock();
43294299
...@@ -4338,8 +4308,7 @@ fn workerAstGenFile(...@@ -4338,8 +4308,7 @@ fn workerAstGenFile(
4338 comp.appendFileSystemInput(fsi, res.file.mod.root, res.file.sub_file_path) catch continue;4308 comp.appendFileSystemInput(fsi, res.file.mod.root, res.file.sub_file_path) catch continue;
4339 };4309 };
4340 const imported_path_digest = pt.zcu.filePathDigest(res.file_index);4310 const imported_path_digest = pt.zcu.filePathDigest(res.file_index);
4341 const imported_root_type = pt.zcu.fileRootType(res.file_index);4311 break :blk .{ res, imported_path_digest };
4342 break :blk .{ res, imported_path_digest, imported_root_type };
4343 };4312 };
4344 if (import_result.is_new) {4313 if (import_result.is_new) {
4345 log.debug("AstGen of {s} has import '{s}'; queuing AstGen of {s}", .{4314 log.debug("AstGen of {s} has import '{s}'; queuing AstGen of {s}", .{
...@@ -4350,7 +4319,7 @@ fn workerAstGenFile(...@@ -4350,7 +4319,7 @@ fn workerAstGenFile(
4350 .import_tok = item.data.token,4319 .import_tok = item.data.token,
4351 } };4320 } };
4352 comp.thread_pool.spawnWgId(wg, workerAstGenFile, .{4321 comp.thread_pool.spawnWgId(wg, workerAstGenFile, .{
4353 comp, import_result.file, import_result.file_index, imported_path_digest, imported_root_type, prog_node, wg, sub_src,4322 comp, import_result.file, import_result.file_index, imported_path_digest, prog_node, wg, sub_src,
4354 });4323 });
4355 }4324 }
4356 }4325 }
...@@ -6443,7 +6412,8 @@ fn buildOutputFromZig(...@@ -6443,7 +6412,8 @@ fn buildOutputFromZig(
64436412
6444 try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node);6413 try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node);
64456414
6446 assert(out.* == null);6415 // Under incremental compilation, `out` may already be populated from a prior update.
6416 assert(out.* == null or comp.incremental);
6447 out.* = try sub_compilation.toCrtFile();6417 out.* = try sub_compilation.toCrtFile();
6448}6418}
64496419
src/InternPool.zig+307-48
...@@ -62,22 +62,60 @@ const want_multi_threaded = true;...@@ -62,22 +62,60 @@ const want_multi_threaded = true;
62/// Whether a single-threaded intern pool impl is in use.62/// Whether a single-threaded intern pool impl is in use.
63pub const single_threaded = builtin.single_threaded or !want_multi_threaded;63pub const single_threaded = builtin.single_threaded or !want_multi_threaded;
6464
65/// A `TrackedInst.Index` provides a single, unchanging reference to a ZIR instruction across a whole
66/// compilation. From this index, you can acquire a `TrackedInst`, which containss a reference to both
67/// the file which the instruction lives in, and the instruction index itself, which is updated on
68/// incremental updates by `Zcu.updateZirRefs`.
65pub const TrackedInst = extern struct {69pub const TrackedInst = extern struct {
66 file: FileIndex,70 file: FileIndex,
67 inst: Zir.Inst.Index,71 inst: Zir.Inst.Index,
68 comptime {72
69 // The fields should be tightly packed. See also serialiation logic in `Compilation.saveState`.73 /// It is possible on an incremental update that we "lose" a ZIR instruction: some tracked `%x` in
70 assert(@sizeOf(@This()) == @sizeOf(FileIndex) + @sizeOf(Zir.Inst.Index));74 /// the old ZIR failed to map to any `%y` in the new ZIR. For this reason, we actually store values
71 }75 /// of type `MaybeLost`, which uses `ZirIndex.lost` to represent this case. `Index.resolve` etc
76 /// return `null` when the `TrackedInst` being resolved has been lost.
77 pub const MaybeLost = extern struct {
78 file: FileIndex,
79 inst: ZirIndex,
80 pub const ZirIndex = enum(u32) {
81 /// Tracking failed for this ZIR instruction. Uses of it should fail.
82 lost = std.math.maxInt(u32),
83 _,
84 pub fn unwrap(inst: ZirIndex) ?Zir.Inst.Index {
85 return switch (inst) {
86 .lost => null,
87 _ => @enumFromInt(@intFromEnum(inst)),
88 };
89 }
90 pub fn wrap(inst: Zir.Inst.Index) ZirIndex {
91 return @enumFromInt(@intFromEnum(inst));
92 }
93 };
94 comptime {
95 // The fields should be tightly packed. See also serialiation logic in `Compilation.saveState`.
96 assert(@sizeOf(@This()) == @sizeOf(FileIndex) + @sizeOf(ZirIndex));
97 }
98 };
99
72 pub const Index = enum(u32) {100 pub const Index = enum(u32) {
73 _,101 _,
74 pub fn resolveFull(tracked_inst_index: TrackedInst.Index, ip: *const InternPool) TrackedInst {102 pub fn resolveFull(tracked_inst_index: TrackedInst.Index, ip: *const InternPool) ?TrackedInst {
103 const tracked_inst_unwrapped = tracked_inst_index.unwrap(ip);
104 const tracked_insts = ip.getLocalShared(tracked_inst_unwrapped.tid).tracked_insts.acquire();
105 const maybe_lost = tracked_insts.view().items(.@"0")[tracked_inst_unwrapped.index];
106 return .{
107 .file = maybe_lost.file,
108 .inst = maybe_lost.inst.unwrap() orelse return null,
109 };
110 }
111 pub fn resolveFile(tracked_inst_index: TrackedInst.Index, ip: *const InternPool) FileIndex {
75 const tracked_inst_unwrapped = tracked_inst_index.unwrap(ip);112 const tracked_inst_unwrapped = tracked_inst_index.unwrap(ip);
76 const tracked_insts = ip.getLocalShared(tracked_inst_unwrapped.tid).tracked_insts.acquire();113 const tracked_insts = ip.getLocalShared(tracked_inst_unwrapped.tid).tracked_insts.acquire();
77 return tracked_insts.view().items(.@"0")[tracked_inst_unwrapped.index];114 const maybe_lost = tracked_insts.view().items(.@"0")[tracked_inst_unwrapped.index];
115 return maybe_lost.file;
78 }116 }
79 pub fn resolve(i: TrackedInst.Index, ip: *const InternPool) Zir.Inst.Index {117 pub fn resolve(i: TrackedInst.Index, ip: *const InternPool) ?Zir.Inst.Index {
80 return i.resolveFull(ip).inst;118 return (i.resolveFull(ip) orelse return null).inst;
81 }119 }
82120
83 pub fn toOptional(i: TrackedInst.Index) Optional {121 pub fn toOptional(i: TrackedInst.Index) Optional {
...@@ -120,7 +158,11 @@ pub fn trackZir(...@@ -120,7 +158,11 @@ pub fn trackZir(
120 tid: Zcu.PerThread.Id,158 tid: Zcu.PerThread.Id,
121 key: TrackedInst,159 key: TrackedInst,
122) Allocator.Error!TrackedInst.Index {160) Allocator.Error!TrackedInst.Index {
123 const full_hash = Hash.hash(0, std.mem.asBytes(&key));161 const maybe_lost_key: TrackedInst.MaybeLost = .{
162 .file = key.file,
163 .inst = TrackedInst.MaybeLost.ZirIndex.wrap(key.inst),
164 };
165 const full_hash = Hash.hash(0, std.mem.asBytes(&maybe_lost_key));
124 const hash: u32 = @truncate(full_hash >> 32);166 const hash: u32 = @truncate(full_hash >> 32);
125 const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))];167 const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))];
126 var map = shard.shared.tracked_inst_map.acquire();168 var map = shard.shared.tracked_inst_map.acquire();
...@@ -132,12 +174,11 @@ pub fn trackZir(...@@ -132,12 +174,11 @@ pub fn trackZir(
132 const entry = &map.entries[map_index];174 const entry = &map.entries[map_index];
133 const index = entry.acquire().unwrap() orelse break;175 const index = entry.acquire().unwrap() orelse break;
134 if (entry.hash != hash) continue;176 if (entry.hash != hash) continue;
135 if (std.meta.eql(index.resolveFull(ip), key)) return index;177 if (std.meta.eql(index.resolveFull(ip) orelse continue, key)) return index;
136 }178 }
137 shard.mutate.tracked_inst_map.mutex.lock();179 shard.mutate.tracked_inst_map.mutex.lock();
138 defer shard.mutate.tracked_inst_map.mutex.unlock();180 defer shard.mutate.tracked_inst_map.mutex.unlock();
139 if (map.entries != shard.shared.tracked_inst_map.entries) {181 if (map.entries != shard.shared.tracked_inst_map.entries) {
140 shard.mutate.tracked_inst_map.len += 1;
141 map = shard.shared.tracked_inst_map;182 map = shard.shared.tracked_inst_map;
142 map_mask = map.header().mask();183 map_mask = map.header().mask();
143 map_index = hash;184 map_index = hash;
...@@ -147,7 +188,7 @@ pub fn trackZir(...@@ -147,7 +188,7 @@ pub fn trackZir(
147 const entry = &map.entries[map_index];188 const entry = &map.entries[map_index];
148 const index = entry.acquire().unwrap() orelse break;189 const index = entry.acquire().unwrap() orelse break;
149 if (entry.hash != hash) continue;190 if (entry.hash != hash) continue;
150 if (std.meta.eql(index.resolveFull(ip), key)) return index;191 if (std.meta.eql(index.resolveFull(ip) orelse continue, key)) return index;
151 }192 }
152 defer shard.mutate.tracked_inst_map.len += 1;193 defer shard.mutate.tracked_inst_map.len += 1;
153 const local = ip.getLocal(tid);194 const local = ip.getLocal(tid);
...@@ -161,7 +202,7 @@ pub fn trackZir(...@@ -161,7 +202,7 @@ pub fn trackZir(
161 .tid = tid,202 .tid = tid,
162 .index = list.mutate.len,203 .index = list.mutate.len,
163 }).wrap(ip);204 }).wrap(ip);
164 list.appendAssumeCapacity(.{key});205 list.appendAssumeCapacity(.{maybe_lost_key});
165 entry.release(index.toOptional());206 entry.release(index.toOptional());
166 return index;207 return index;
167 }208 }
...@@ -205,12 +246,94 @@ pub fn trackZir(...@@ -205,12 +246,94 @@ pub fn trackZir(
205 .tid = tid,246 .tid = tid,
206 .index = list.mutate.len,247 .index = list.mutate.len,
207 }).wrap(ip);248 }).wrap(ip);
208 list.appendAssumeCapacity(.{key});249 list.appendAssumeCapacity(.{maybe_lost_key});
209 map.entries[map_index] = .{ .value = index.toOptional(), .hash = hash };250 map.entries[map_index] = .{ .value = index.toOptional(), .hash = hash };
210 shard.shared.tracked_inst_map.release(new_map);251 shard.shared.tracked_inst_map.release(new_map);
211 return index;252 return index;
212}253}
213254
255/// At the start of an incremental update, we update every entry in `tracked_insts` to include
256/// the new ZIR index. Once this is done, we must update the hashmap metadata so that lookups
257/// return correct entries where they already exist.
258pub fn rehashTrackedInsts(
259 ip: *InternPool,
260 gpa: Allocator,
261 tid: Zcu.PerThread.Id,
262) Allocator.Error!void {
263 assert(tid == .main); // we shouldn't have any other threads active right now
264
265 // TODO: this function doesn't handle OOM well. What should it do?
266
267 // We don't lock anything, as this function assumes that no other thread is
268 // accessing `tracked_insts`. This is necessary because we're going to be
269 // iterating the `TrackedInst`s in each `Local`, so we have to know that
270 // none will be added as we work.
271
272 // Figure out how big each shard need to be and store it in its mutate `len`.
273 for (ip.shards) |*shard| shard.mutate.tracked_inst_map.len = 0;
274 for (ip.locals) |*local| {
275 // `getMutableTrackedInsts` is okay only because no other thread is currently active.
276 // We need the `mutate` for the len.
277 for (local.getMutableTrackedInsts(gpa).viewAllowEmpty().items(.@"0")) |tracked_inst| {
278 if (tracked_inst.inst == .lost) continue; // we can ignore this one!
279 const full_hash = Hash.hash(0, std.mem.asBytes(&tracked_inst));
280 const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))];
281 shard.mutate.tracked_inst_map.len += 1;
282 }
283 }
284
285 const Map = Shard.Map(TrackedInst.Index.Optional);
286
287 const arena_state = &ip.getLocal(tid).mutate.arena;
288
289 // We know how big each shard must be, so ensure we have the capacity we need.
290 for (ip.shards) |*shard| {
291 const want_capacity = std.math.ceilPowerOfTwo(u32, shard.mutate.tracked_inst_map.len * 5 / 3) catch unreachable;
292 const have_capacity = shard.shared.tracked_inst_map.header().capacity; // no acquire because we hold the mutex
293 if (have_capacity >= want_capacity) {
294 @memset(shard.shared.tracked_inst_map.entries[0..have_capacity], .{ .value = .none, .hash = undefined });
295 continue;
296 }
297 var arena = arena_state.promote(gpa);
298 defer arena_state.* = arena.state;
299 const new_map_buf = try arena.allocator().alignedAlloc(
300 u8,
301 Map.alignment,
302 Map.entries_offset + want_capacity * @sizeOf(Map.Entry),
303 );
304 const new_map: Map = .{ .entries = @ptrCast(new_map_buf[Map.entries_offset..].ptr) };
305 new_map.header().* = .{ .capacity = want_capacity };
306 @memset(new_map.entries[0..want_capacity], .{ .value = .none, .hash = undefined });
307 shard.shared.tracked_inst_map.release(new_map);
308 }
309
310 // Now, actually insert the items.
311 for (ip.locals, 0..) |*local, local_tid| {
312 // `getMutableTrackedInsts` is okay only because no other thread is currently active.
313 // We need the `mutate` for the len.
314 for (local.getMutableTrackedInsts(gpa).viewAllowEmpty().items(.@"0"), 0..) |tracked_inst, local_inst_index| {
315 if (tracked_inst.inst == .lost) continue; // we can ignore this one!
316 const full_hash = Hash.hash(0, std.mem.asBytes(&tracked_inst));
317 const hash: u32 = @truncate(full_hash >> 32);
318 const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))];
319 const map = shard.shared.tracked_inst_map; // no acquire because we hold the mutex
320 const map_mask = map.header().mask();
321 var map_index = hash;
322 const entry = while (true) : (map_index += 1) {
323 map_index &= map_mask;
324 const entry = &map.entries[map_index];
325 if (entry.acquire() == .none) break entry;
326 };
327 const index = TrackedInst.Index.Unwrapped.wrap(.{
328 .tid = @enumFromInt(local_tid),
329 .index = @intCast(local_inst_index),
330 }, ip);
331 entry.hash = hash;
332 entry.release(index.toOptional());
333 }
334 }
335}
336
214/// Analysis Unit. Represents a single entity which undergoes semantic analysis.337/// Analysis Unit. Represents a single entity which undergoes semantic analysis.
215/// This is either a `Cau` or a runtime function.338/// This is either a `Cau` or a runtime function.
216/// The LSB is used as a tag bit.339/// The LSB is used as a tag bit.
...@@ -572,10 +695,6 @@ pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyI...@@ -572,10 +695,6 @@ pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyI
572 .ip = ip,695 .ip = ip,
573 .next_entry = .none,696 .next_entry = .none,
574 };697 };
575 if (ip.dep_entries.items[@intFromEnum(first_entry)].depender == .none) return .{
576 .ip = ip,
577 .next_entry = .none,
578 };
579 return .{698 return .{
580 .ip = ip,699 .ip = ip,
581 .next_entry = first_entry.toOptional(),700 .next_entry = first_entry.toOptional(),
...@@ -612,7 +731,6 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend...@@ -612,7 +731,6 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend
612731
613 if (gop.found_existing and ip.dep_entries.items[@intFromEnum(gop.value_ptr.*)].depender == .none) {732 if (gop.found_existing and ip.dep_entries.items[@intFromEnum(gop.value_ptr.*)].depender == .none) {
614 // Dummy entry, so we can reuse it rather than allocating a new one!733 // Dummy entry, so we can reuse it rather than allocating a new one!
615 ip.dep_entries.items[@intFromEnum(gop.value_ptr.*)].next = .none;
616 break :new_index gop.value_ptr.*;734 break :new_index gop.value_ptr.*;
617 }735 }
618736
...@@ -620,7 +738,12 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend...@@ -620,7 +738,12 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend
620 const new_index: DepEntry.Index, const ptr = if (ip.free_dep_entries.popOrNull()) |new_index| new: {738 const new_index: DepEntry.Index, const ptr = if (ip.free_dep_entries.popOrNull()) |new_index| new: {
621 break :new .{ new_index, &ip.dep_entries.items[@intFromEnum(new_index)] };739 break :new .{ new_index, &ip.dep_entries.items[@intFromEnum(new_index)] };
622 } else .{ @enumFromInt(ip.dep_entries.items.len), ip.dep_entries.addOneAssumeCapacity() };740 } else .{ @enumFromInt(ip.dep_entries.items.len), ip.dep_entries.addOneAssumeCapacity() };
623 ptr.next = if (gop.found_existing) gop.value_ptr.*.toOptional() else .none;741 if (gop.found_existing) {
742 ptr.next = gop.value_ptr.*.toOptional();
743 ip.dep_entries.items[@intFromEnum(gop.value_ptr.*)].prev = new_index.toOptional();
744 } else {
745 ptr.next = .none;
746 }
624 gop.value_ptr.* = new_index;747 gop.value_ptr.* = new_index;
625 break :new_index new_index;748 break :new_index new_index;
626 },749 },
...@@ -642,10 +765,9 @@ pub const NamespaceNameKey = struct {...@@ -642,10 +765,9 @@ pub const NamespaceNameKey = struct {
642};765};
643766
644pub const DepEntry = extern struct {767pub const DepEntry = extern struct {
645 /// If null, this is a dummy entry - all other fields are `undefined`. It is768 /// If null, this is a dummy entry. `next_dependee` is undefined. This is the first
646 /// the first and only entry in one of `intern_pool.*_deps`, and does not769 /// entry in one of `*_deps`, and does not appear in any list by `first_dependency`,
647 /// appear in any list by `first_dependency`, but is not in770 /// but is not in `free_dep_entries` since `*_deps` stores a reference to it.
648 /// `free_dep_entries` since `*_deps` stores a reference to it.
649 depender: AnalUnit.Optional,771 depender: AnalUnit.Optional,
650 /// Index into `dep_entries` forming a doubly linked list of all dependencies on this dependee.772 /// Index into `dep_entries` forming a doubly linked list of all dependencies on this dependee.
651 /// Used to iterate all dependers for a given dependee during an update.773 /// Used to iterate all dependers for a given dependee during an update.
...@@ -684,6 +806,14 @@ const Local = struct {...@@ -684,6 +806,14 @@ const Local = struct {
684 /// This state is fully local to the owning thread and does not require any806 /// This state is fully local to the owning thread and does not require any
685 /// atomic access.807 /// atomic access.
686 mutate: struct {808 mutate: struct {
809 /// When we need to allocate any long-lived buffer for mutating the `InternPool`, it is
810 /// allocated into this `arena` (for the `Id` of the thread performing the mutation). An
811 /// arena is used to avoid contention on the GPA, and to ensure that any code which retains
812 /// references to old state remains valid. For instance, when reallocing hashmap metadata,
813 /// a racing lookup on another thread may still retain a handle to the old metadata pointer,
814 /// so it must remain valid.
815 /// This arena's lifetime is tied to that of `Compilation`, although it can be cleared on
816 /// garbage collection (currently vaporware).
687 arena: std.heap.ArenaAllocator.State,817 arena: std.heap.ArenaAllocator.State,
688818
689 items: ListMutate,819 items: ListMutate,
...@@ -728,7 +858,7 @@ const Local = struct {...@@ -728,7 +858,7 @@ const Local = struct {
728 else => @compileError("unsupported host"),858 else => @compileError("unsupported host"),
729 };859 };
730 const Strings = List(struct { u8 });860 const Strings = List(struct { u8 });
731 const TrackedInsts = List(struct { TrackedInst });861 const TrackedInsts = List(struct { TrackedInst.MaybeLost });
732 const Maps = List(struct { FieldMap });862 const Maps = List(struct { FieldMap });
733 const Caus = List(struct { Cau });863 const Caus = List(struct { Cau });
734 const Navs = List(Nav.Repr);864 const Navs = List(Nav.Repr);
...@@ -959,6 +1089,14 @@ const Local = struct {...@@ -959,6 +1089,14 @@ const Local = struct {
959 mutable.list.release(new_list);1089 mutable.list.release(new_list);
960 }1090 }
9611091
1092 pub fn viewAllowEmpty(mutable: Mutable) View {
1093 const capacity = mutable.list.header().capacity;
1094 return .{
1095 .bytes = mutable.list.bytes,
1096 .len = mutable.mutate.len,
1097 .capacity = capacity,
1098 };
1099 }
962 pub fn view(mutable: Mutable) View {1100 pub fn view(mutable: Mutable) View {
963 const capacity = mutable.list.header().capacity;1101 const capacity = mutable.list.header().capacity;
964 assert(capacity > 0); // optimizes `MultiArrayList.Slice.items`1102 assert(capacity > 0); // optimizes `MultiArrayList.Slice.items`
...@@ -996,7 +1134,6 @@ const Local = struct {...@@ -996,7 +1134,6 @@ const Local = struct {
996 fn header(list: ListSelf) *Header {1134 fn header(list: ListSelf) *Header {
997 return @ptrFromInt(@intFromPtr(list.bytes) - bytes_offset);1135 return @ptrFromInt(@intFromPtr(list.bytes) - bytes_offset);
998 }1136 }
999
1000 pub fn view(list: ListSelf) View {1137 pub fn view(list: ListSelf) View {
1001 const capacity = list.header().capacity;1138 const capacity = list.header().capacity;
1002 assert(capacity > 0); // optimizes `MultiArrayList.Slice.items`1139 assert(capacity > 0); // optimizes `MultiArrayList.Slice.items`
...@@ -2570,7 +2707,12 @@ pub const Key = union(enum) {...@@ -2570,7 +2707,12 @@ pub const Key = union(enum) {
25702707
2571 .variable => |a_info| {2708 .variable => |a_info| {
2572 const b_info = b.variable;2709 const b_info = b.variable;
2573 return a_info.owner_nav == b_info.owner_nav;2710 return a_info.owner_nav == b_info.owner_nav and
2711 a_info.ty == b_info.ty and
2712 a_info.init == b_info.init and
2713 a_info.lib_name == b_info.lib_name and
2714 a_info.is_threadlocal == b_info.is_threadlocal and
2715 a_info.is_weak_linkage == b_info.is_weak_linkage;
2574 },2716 },
2575 .@"extern" => |a_info| {2717 .@"extern" => |a_info| {
2576 const b_info = b.@"extern";2718 const b_info = b.@"extern";
...@@ -6958,6 +7100,7 @@ fn getOrPutKeyEnsuringAdditionalCapacity(...@@ -6958,6 +7100,7 @@ fn getOrPutKeyEnsuringAdditionalCapacity(
6958 const index = entry.acquire();7100 const index = entry.acquire();
6959 if (index == .none) break;7101 if (index == .none) break;
6960 if (entry.hash != hash) continue;7102 if (entry.hash != hash) continue;
7103 if (ip.isRemoved(index)) continue;
6961 if (ip.indexToKey(index).eql(key, ip)) return .{ .existing = index };7104 if (ip.indexToKey(index).eql(key, ip)) return .{ .existing = index };
6962 }7105 }
6963 shard.mutate.map.mutex.lock();7106 shard.mutate.map.mutex.lock();
...@@ -7032,6 +7175,43 @@ fn getOrPutKeyEnsuringAdditionalCapacity(...@@ -7032,6 +7175,43 @@ fn getOrPutKeyEnsuringAdditionalCapacity(
7032 .map_index = map_index,7175 .map_index = map_index,
7033 } };7176 } };
7034}7177}
7178/// Like `getOrPutKey`, but asserts that the key already exists, and prepares to replace
7179/// its shard entry with a new `Index` anyway. After finalizing this, the old index remains
7180/// valid (in that `indexToKey` and similar queries will behave as before), but it will
7181/// never be returned from a lookup (`getOrPutKey` etc).
7182/// This is used by incremental compilation when an existing container type is outdated. In
7183/// this case, the type must be recreated at a new `InternPool.Index`, but the old index must
7184/// remain valid since now-unreferenced `AnalUnit`s may retain references to it. The old index
7185/// will be cleaned up when the `Zcu` undergoes garbage collection.
7186fn putKeyReplace(
7187 ip: *InternPool,
7188 tid: Zcu.PerThread.Id,
7189 key: Key,
7190) GetOrPutKey {
7191 const full_hash = key.hash64(ip);
7192 const hash: u32 = @truncate(full_hash >> 32);
7193 const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))];
7194 shard.mutate.map.mutex.lock();
7195 errdefer shard.mutate.map.mutex.unlock();
7196 const map = shard.shared.map;
7197 const map_mask = map.header().mask();
7198 var map_index = hash;
7199 while (true) : (map_index += 1) {
7200 map_index &= map_mask;
7201 const entry = &map.entries[map_index];
7202 const index = entry.value;
7203 assert(index != .none); // key not present
7204 if (entry.hash == hash and ip.indexToKey(index).eql(key, ip)) {
7205 break; // we found the entry to replace
7206 }
7207 }
7208 return .{ .new = .{
7209 .ip = ip,
7210 .tid = tid,
7211 .shard = shard,
7212 .map_index = map_index,
7213 } };
7214}
70357215
7036pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) Allocator.Error!Index {7216pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) Allocator.Error!Index {
7037 var gop = try ip.getOrPutKey(gpa, tid, key);7217 var gop = try ip.getOrPutKey(gpa, tid, key);
...@@ -7859,6 +8039,10 @@ pub const UnionTypeInit = struct {...@@ -7859,6 +8039,10 @@ pub const UnionTypeInit = struct {
7859 zir_index: TrackedInst.Index,8039 zir_index: TrackedInst.Index,
7860 captures: []const CaptureValue,8040 captures: []const CaptureValue,
7861 },8041 },
8042 declared_owned_captures: struct {
8043 zir_index: TrackedInst.Index,
8044 captures: CaptureValue.Slice,
8045 },
7862 reified: struct {8046 reified: struct {
7863 zir_index: TrackedInst.Index,8047 zir_index: TrackedInst.Index,
7864 type_hash: u64,8048 type_hash: u64,
...@@ -7871,17 +8055,28 @@ pub fn getUnionType(...@@ -7871,17 +8055,28 @@ pub fn getUnionType(
7871 gpa: Allocator,8055 gpa: Allocator,
7872 tid: Zcu.PerThread.Id,8056 tid: Zcu.PerThread.Id,
7873 ini: UnionTypeInit,8057 ini: UnionTypeInit,
8058 /// If it is known that there is an existing type with this key which is outdated,
8059 /// this is passed as `true`, and the type is replaced with one at a fresh index.
8060 replace_existing: bool,
7874) Allocator.Error!WipNamespaceType.Result {8061) Allocator.Error!WipNamespaceType.Result {
7875 var gop = try ip.getOrPutKey(gpa, tid, .{ .union_type = switch (ini.key) {8062 const key: Key = .{ .union_type = switch (ini.key) {
7876 .declared => |d| .{ .declared = .{8063 .declared => |d| .{ .declared = .{
7877 .zir_index = d.zir_index,8064 .zir_index = d.zir_index,
7878 .captures = .{ .external = d.captures },8065 .captures = .{ .external = d.captures },
7879 } },8066 } },
8067 .declared_owned_captures => |d| .{ .declared = .{
8068 .zir_index = d.zir_index,
8069 .captures = .{ .owned = d.captures },
8070 } },
7880 .reified => |r| .{ .reified = .{8071 .reified => |r| .{ .reified = .{
7881 .zir_index = r.zir_index,8072 .zir_index = r.zir_index,
7882 .type_hash = r.type_hash,8073 .type_hash = r.type_hash,
7883 } },8074 } },
7884 } });8075 } };
8076 var gop = if (replace_existing)
8077 ip.putKeyReplace(tid, key)
8078 else
8079 try ip.getOrPutKey(gpa, tid, key);
7885 defer gop.deinit();8080 defer gop.deinit();
7886 if (gop == .existing) return .{ .existing = gop.existing };8081 if (gop == .existing) return .{ .existing = gop.existing };
78878082
...@@ -7896,7 +8091,7 @@ pub fn getUnionType(...@@ -7896,7 +8091,7 @@ pub fn getUnionType(
7896 // TODO: fmt bug8091 // TODO: fmt bug
7897 // zig fmt: off8092 // zig fmt: off
7898 switch (ini.key) {8093 switch (ini.key) {
7899 .declared => |d| @intFromBool(d.captures.len != 0) + d.captures.len,8094 inline .declared, .declared_owned_captures => |d| @intFromBool(d.captures.len != 0) + d.captures.len,
7900 .reified => 2, // type_hash: PackedU648095 .reified => 2, // type_hash: PackedU64
7901 } +8096 } +
7902 // zig fmt: on8097 // zig fmt: on
...@@ -7905,7 +8100,10 @@ pub fn getUnionType(...@@ -7905,7 +8100,10 @@ pub fn getUnionType(
79058100
7906 const extra_index = addExtraAssumeCapacity(extra, Tag.TypeUnion{8101 const extra_index = addExtraAssumeCapacity(extra, Tag.TypeUnion{
7907 .flags = .{8102 .flags = .{
7908 .any_captures = ini.key == .declared and ini.key.declared.captures.len != 0,8103 .any_captures = switch (ini.key) {
8104 inline .declared, .declared_owned_captures => |d| d.captures.len != 0,
8105 .reified => false,
8106 },
7909 .runtime_tag = ini.flags.runtime_tag,8107 .runtime_tag = ini.flags.runtime_tag,
7910 .any_aligned_fields = ini.flags.any_aligned_fields,8108 .any_aligned_fields = ini.flags.any_aligned_fields,
7911 .layout = ini.flags.layout,8109 .layout = ini.flags.layout,
...@@ -7914,7 +8112,10 @@ pub fn getUnionType(...@@ -7914,7 +8112,10 @@ pub fn getUnionType(
7914 .assumed_runtime_bits = ini.flags.assumed_runtime_bits,8112 .assumed_runtime_bits = ini.flags.assumed_runtime_bits,
7915 .assumed_pointer_aligned = ini.flags.assumed_pointer_aligned,8113 .assumed_pointer_aligned = ini.flags.assumed_pointer_aligned,
7916 .alignment = ini.flags.alignment,8114 .alignment = ini.flags.alignment,
7917 .is_reified = ini.key == .reified,8115 .is_reified = switch (ini.key) {
8116 .declared, .declared_owned_captures => false,
8117 .reified => true,
8118 },
7918 },8119 },
7919 .fields_len = ini.fields_len,8120 .fields_len = ini.fields_len,
7920 .size = std.math.maxInt(u32),8121 .size = std.math.maxInt(u32),
...@@ -7938,6 +8139,10 @@ pub fn getUnionType(...@@ -7938,6 +8139,10 @@ pub fn getUnionType(
7938 extra.appendAssumeCapacity(.{@intCast(d.captures.len)});8139 extra.appendAssumeCapacity(.{@intCast(d.captures.len)});
7939 extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)});8140 extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)});
7940 },8141 },
8142 .declared_owned_captures => |d| if (d.captures.len != 0) {
8143 extra.appendAssumeCapacity(.{@intCast(d.captures.len)});
8144 extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures.get(ip))});
8145 },
7941 .reified => |r| _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash)),8146 .reified => |r| _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash)),
7942 }8147 }
79438148
...@@ -8035,6 +8240,10 @@ pub const StructTypeInit = struct {...@@ -8035,6 +8240,10 @@ pub const StructTypeInit = struct {
8035 zir_index: TrackedInst.Index,8240 zir_index: TrackedInst.Index,
8036 captures: []const CaptureValue,8241 captures: []const CaptureValue,
8037 },8242 },
8243 declared_owned_captures: struct {
8244 zir_index: TrackedInst.Index,
8245 captures: CaptureValue.Slice,
8246 },
8038 reified: struct {8247 reified: struct {
8039 zir_index: TrackedInst.Index,8248 zir_index: TrackedInst.Index,
8040 type_hash: u64,8249 type_hash: u64,
...@@ -8047,17 +8256,28 @@ pub fn getStructType(...@@ -8047,17 +8256,28 @@ pub fn getStructType(
8047 gpa: Allocator,8256 gpa: Allocator,
8048 tid: Zcu.PerThread.Id,8257 tid: Zcu.PerThread.Id,
8049 ini: StructTypeInit,8258 ini: StructTypeInit,
8259 /// If it is known that there is an existing type with this key which is outdated,
8260 /// this is passed as `true`, and the type is replaced with one at a fresh index.
8261 replace_existing: bool,
8050) Allocator.Error!WipNamespaceType.Result {8262) Allocator.Error!WipNamespaceType.Result {
8051 var gop = try ip.getOrPutKey(gpa, tid, .{ .struct_type = switch (ini.key) {8263 const key: Key = .{ .struct_type = switch (ini.key) {
8052 .declared => |d| .{ .declared = .{8264 .declared => |d| .{ .declared = .{
8053 .zir_index = d.zir_index,8265 .zir_index = d.zir_index,
8054 .captures = .{ .external = d.captures },8266 .captures = .{ .external = d.captures },
8055 } },8267 } },
8268 .declared_owned_captures => |d| .{ .declared = .{
8269 .zir_index = d.zir_index,
8270 .captures = .{ .owned = d.captures },
8271 } },
8056 .reified => |r| .{ .reified = .{8272 .reified => |r| .{ .reified = .{
8057 .zir_index = r.zir_index,8273 .zir_index = r.zir_index,
8058 .type_hash = r.type_hash,8274 .type_hash = r.type_hash,
8059 } },8275 } },
8060 } });8276 } };
8277 var gop = if (replace_existing)
8278 ip.putKeyReplace(tid, key)
8279 else
8280 try ip.getOrPutKey(gpa, tid, key);
8061 defer gop.deinit();8281 defer gop.deinit();
8062 if (gop == .existing) return .{ .existing = gop.existing };8282 if (gop == .existing) return .{ .existing = gop.existing };
80638283
...@@ -8080,7 +8300,7 @@ pub fn getStructType(...@@ -8080,7 +8300,7 @@ pub fn getStructType(
8080 // TODO: fmt bug8300 // TODO: fmt bug
8081 // zig fmt: off8301 // zig fmt: off
8082 switch (ini.key) {8302 switch (ini.key) {
8083 .declared => |d| @intFromBool(d.captures.len != 0) + d.captures.len,8303 inline .declared, .declared_owned_captures => |d| @intFromBool(d.captures.len != 0) + d.captures.len,
8084 .reified => 2, // type_hash: PackedU648304 .reified => 2, // type_hash: PackedU64
8085 } +8305 } +
8086 // zig fmt: on8306 // zig fmt: on
...@@ -8096,10 +8316,16 @@ pub fn getStructType(...@@ -8096,10 +8316,16 @@ pub fn getStructType(
8096 .backing_int_ty = .none,8316 .backing_int_ty = .none,
8097 .names_map = names_map,8317 .names_map = names_map,
8098 .flags = .{8318 .flags = .{
8099 .any_captures = ini.key == .declared and ini.key.declared.captures.len != 0,8319 .any_captures = switch (ini.key) {
8320 inline .declared, .declared_owned_captures => |d| d.captures.len != 0,
8321 .reified => false,
8322 },
8100 .field_inits_wip = false,8323 .field_inits_wip = false,
8101 .inits_resolved = ini.inits_resolved,8324 .inits_resolved = ini.inits_resolved,
8102 .is_reified = ini.key == .reified,8325 .is_reified = switch (ini.key) {
8326 .declared, .declared_owned_captures => false,
8327 .reified => true,
8328 },
8103 },8329 },
8104 });8330 });
8105 try items.append(.{8331 try items.append(.{
...@@ -8111,6 +8337,10 @@ pub fn getStructType(...@@ -8111,6 +8337,10 @@ pub fn getStructType(
8111 extra.appendAssumeCapacity(.{@intCast(d.captures.len)});8337 extra.appendAssumeCapacity(.{@intCast(d.captures.len)});
8112 extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)});8338 extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)});
8113 },8339 },
8340 .declared_owned_captures => |d| if (d.captures.len != 0) {
8341 extra.appendAssumeCapacity(.{@intCast(d.captures.len)});
8342 extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures.get(ip))});
8343 },
8114 .reified => |r| {8344 .reified => |r| {
8115 _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash));8345 _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash));
8116 },8346 },
...@@ -8138,7 +8368,7 @@ pub fn getStructType(...@@ -8138,7 +8368,7 @@ pub fn getStructType(
8138 // TODO: fmt bug8368 // TODO: fmt bug
8139 // zig fmt: off8369 // zig fmt: off
8140 switch (ini.key) {8370 switch (ini.key) {
8141 .declared => |d| @intFromBool(d.captures.len != 0) + d.captures.len,8371 inline .declared, .declared_owned_captures => |d| @intFromBool(d.captures.len != 0) + d.captures.len,
8142 .reified => 2, // type_hash: PackedU648372 .reified => 2, // type_hash: PackedU64
8143 } +8373 } +
8144 // zig fmt: on8374 // zig fmt: on
...@@ -8153,7 +8383,10 @@ pub fn getStructType(...@@ -8153,7 +8383,10 @@ pub fn getStructType(
8153 .fields_len = ini.fields_len,8383 .fields_len = ini.fields_len,
8154 .size = std.math.maxInt(u32),8384 .size = std.math.maxInt(u32),
8155 .flags = .{8385 .flags = .{
8156 .any_captures = ini.key == .declared and ini.key.declared.captures.len != 0,8386 .any_captures = switch (ini.key) {
8387 inline .declared, .declared_owned_captures => |d| d.captures.len != 0,
8388 .reified => false,
8389 },
8157 .is_extern = is_extern,8390 .is_extern = is_extern,
8158 .known_non_opv = ini.known_non_opv,8391 .known_non_opv = ini.known_non_opv,
8159 .requires_comptime = ini.requires_comptime,8392 .requires_comptime = ini.requires_comptime,
...@@ -8171,7 +8404,10 @@ pub fn getStructType(...@@ -8171,7 +8404,10 @@ pub fn getStructType(
8171 .field_inits_wip = false,8404 .field_inits_wip = false,
8172 .inits_resolved = ini.inits_resolved,8405 .inits_resolved = ini.inits_resolved,
8173 .fully_resolved = false,8406 .fully_resolved = false,
8174 .is_reified = ini.key == .reified,8407 .is_reified = switch (ini.key) {
8408 .declared, .declared_owned_captures => false,
8409 .reified => true,
8410 },
8175 },8411 },
8176 });8412 });
8177 try items.append(.{8413 try items.append(.{
...@@ -8183,6 +8419,10 @@ pub fn getStructType(...@@ -8183,6 +8419,10 @@ pub fn getStructType(
8183 extra.appendAssumeCapacity(.{@intCast(d.captures.len)});8419 extra.appendAssumeCapacity(.{@intCast(d.captures.len)});
8184 extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)});8420 extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)});
8185 },8421 },
8422 .declared_owned_captures => |d| if (d.captures.len != 0) {
8423 extra.appendAssumeCapacity(.{@intCast(d.captures.len)});
8424 extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures.get(ip))});
8425 },
8186 .reified => |r| {8426 .reified => |r| {
8187 _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash));8427 _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash));
8188 },8428 },
...@@ -8986,6 +9226,10 @@ pub const EnumTypeInit = struct {...@@ -8986,6 +9226,10 @@ pub const EnumTypeInit = struct {
8986 zir_index: TrackedInst.Index,9226 zir_index: TrackedInst.Index,
8987 captures: []const CaptureValue,9227 captures: []const CaptureValue,
8988 },9228 },
9229 declared_owned_captures: struct {
9230 zir_index: TrackedInst.Index,
9231 captures: CaptureValue.Slice,
9232 },
8989 reified: struct {9233 reified: struct {
8990 zir_index: TrackedInst.Index,9234 zir_index: TrackedInst.Index,
8991 type_hash: u64,9235 type_hash: u64,
...@@ -9081,17 +9325,28 @@ pub fn getEnumType(...@@ -9081,17 +9325,28 @@ pub fn getEnumType(
9081 gpa: Allocator,9325 gpa: Allocator,
9082 tid: Zcu.PerThread.Id,9326 tid: Zcu.PerThread.Id,
9083 ini: EnumTypeInit,9327 ini: EnumTypeInit,
9328 /// If it is known that there is an existing type with this key which is outdated,
9329 /// this is passed as `true`, and the type is replaced with one at a fresh index.
9330 replace_existing: bool,
9084) Allocator.Error!WipEnumType.Result {9331) Allocator.Error!WipEnumType.Result {
9085 var gop = try ip.getOrPutKey(gpa, tid, .{ .enum_type = switch (ini.key) {9332 const key: Key = .{ .enum_type = switch (ini.key) {
9086 .declared => |d| .{ .declared = .{9333 .declared => |d| .{ .declared = .{
9087 .zir_index = d.zir_index,9334 .zir_index = d.zir_index,
9088 .captures = .{ .external = d.captures },9335 .captures = .{ .external = d.captures },
9089 } },9336 } },
9337 .declared_owned_captures => |d| .{ .declared = .{
9338 .zir_index = d.zir_index,
9339 .captures = .{ .owned = d.captures },
9340 } },
9090 .reified => |r| .{ .reified = .{9341 .reified => |r| .{ .reified = .{
9091 .zir_index = r.zir_index,9342 .zir_index = r.zir_index,
9092 .type_hash = r.type_hash,9343 .type_hash = r.type_hash,
9093 } },9344 } },
9094 } });9345 } };
9346 var gop = if (replace_existing)
9347 ip.putKeyReplace(tid, key)
9348 else
9349 try ip.getOrPutKey(gpa, tid, key);
9095 defer gop.deinit();9350 defer gop.deinit();
9096 if (gop == .existing) return .{ .existing = gop.existing };9351 if (gop == .existing) return .{ .existing = gop.existing };
90979352
...@@ -9110,7 +9365,7 @@ pub fn getEnumType(...@@ -9110,7 +9365,7 @@ pub fn getEnumType(
9110 // TODO: fmt bug9365 // TODO: fmt bug
9111 // zig fmt: off9366 // zig fmt: off
9112 switch (ini.key) {9367 switch (ini.key) {
9113 .declared => |d| d.captures.len,9368 inline .declared, .declared_owned_captures => |d| d.captures.len,
9114 .reified => 2, // type_hash: PackedU649369 .reified => 2, // type_hash: PackedU64
9115 } +9370 } +
9116 // zig fmt: on9371 // zig fmt: on
...@@ -9120,7 +9375,7 @@ pub fn getEnumType(...@@ -9120,7 +9375,7 @@ pub fn getEnumType(
9120 const extra_index = addExtraAssumeCapacity(extra, EnumAuto{9375 const extra_index = addExtraAssumeCapacity(extra, EnumAuto{
9121 .name = undefined, // set by `prepare`9376 .name = undefined, // set by `prepare`
9122 .captures_len = switch (ini.key) {9377 .captures_len = switch (ini.key) {
9123 .declared => |d| @intCast(d.captures.len),9378 inline .declared, .declared_owned_captures => |d| @intCast(d.captures.len),
9124 .reified => std.math.maxInt(u32),9379 .reified => std.math.maxInt(u32),
9125 },9380 },
9126 .namespace = undefined, // set by `prepare`9381 .namespace = undefined, // set by `prepare`
...@@ -9139,6 +9394,7 @@ pub fn getEnumType(...@@ -9139,6 +9394,7 @@ pub fn getEnumType(
9139 extra.appendAssumeCapacity(undefined); // `cau` will be set by `finish`9394 extra.appendAssumeCapacity(undefined); // `cau` will be set by `finish`
9140 switch (ini.key) {9395 switch (ini.key) {
9141 .declared => |d| extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)}),9396 .declared => |d| extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)}),
9397 .declared_owned_captures => |d| extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures.get(ip))}),
9142 .reified => |r| _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash)),9398 .reified => |r| _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash)),
9143 }9399 }
9144 const names_start = extra.mutate.len;9400 const names_start = extra.mutate.len;
...@@ -9169,7 +9425,7 @@ pub fn getEnumType(...@@ -9169,7 +9425,7 @@ pub fn getEnumType(
9169 // TODO: fmt bug9425 // TODO: fmt bug
9170 // zig fmt: off9426 // zig fmt: off
9171 switch (ini.key) {9427 switch (ini.key) {
9172 .declared => |d| d.captures.len,9428 inline .declared, .declared_owned_captures => |d| d.captures.len,
9173 .reified => 2, // type_hash: PackedU649429 .reified => 2, // type_hash: PackedU64
9174 } +9430 } +
9175 // zig fmt: on9431 // zig fmt: on
...@@ -9180,7 +9436,7 @@ pub fn getEnumType(...@@ -9180,7 +9436,7 @@ pub fn getEnumType(
9180 const extra_index = addExtraAssumeCapacity(extra, EnumExplicit{9436 const extra_index = addExtraAssumeCapacity(extra, EnumExplicit{
9181 .name = undefined, // set by `prepare`9437 .name = undefined, // set by `prepare`
9182 .captures_len = switch (ini.key) {9438 .captures_len = switch (ini.key) {
9183 .declared => |d| @intCast(d.captures.len),9439 inline .declared, .declared_owned_captures => |d| @intCast(d.captures.len),
9184 .reified => std.math.maxInt(u32),9440 .reified => std.math.maxInt(u32),
9185 },9441 },
9186 .namespace = undefined, // set by `prepare`9442 .namespace = undefined, // set by `prepare`
...@@ -9204,6 +9460,7 @@ pub fn getEnumType(...@@ -9204,6 +9460,7 @@ pub fn getEnumType(
9204 extra.appendAssumeCapacity(undefined); // `cau` will be set by `finish`9460 extra.appendAssumeCapacity(undefined); // `cau` will be set by `finish`
9205 switch (ini.key) {9461 switch (ini.key) {
9206 .declared => |d| extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)}),9462 .declared => |d| extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)}),
9463 .declared_owned_captures => |d| extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures.get(ip))}),
9207 .reified => |r| _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash)),9464 .reified => |r| _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash)),
9208 }9465 }
9209 const names_start = extra.mutate.len;9466 const names_start = extra.mutate.len;
...@@ -9267,10 +9524,12 @@ pub fn getGeneratedTagEnumType(...@@ -9267,10 +9524,12 @@ pub fn getGeneratedTagEnumType(
9267 .tid = tid,9524 .tid = tid,
9268 .index = items.mutate.len,9525 .index = items.mutate.len,
9269 }, ip);9526 }, ip);
9527 const parent_namespace = ip.namespacePtr(ini.parent_namespace);
9270 const namespace = try ip.createNamespace(gpa, tid, .{9528 const namespace = try ip.createNamespace(gpa, tid, .{
9271 .parent = ini.parent_namespace.toOptional(),9529 .parent = ini.parent_namespace.toOptional(),
9272 .owner_type = enum_index,9530 .owner_type = enum_index,
9273 .file_scope = ip.namespacePtr(ini.parent_namespace).file_scope,9531 .file_scope = parent_namespace.file_scope,
9532 .generation = parent_namespace.generation,
9274 });9533 });
9275 errdefer ip.destroyNamespace(tid, namespace);9534 errdefer ip.destroyNamespace(tid, namespace);
92769535
...@@ -10866,6 +11125,7 @@ pub fn destroyNamespace(...@@ -10866,6 +11125,7 @@ pub fn destroyNamespace(
10866 .parent = undefined,11125 .parent = undefined,
10867 .file_scope = undefined,11126 .file_scope = undefined,
10868 .owner_type = undefined,11127 .owner_type = undefined,
11128 .generation = undefined,
10869 };11129 };
10870 @field(namespace, Local.namespace_next_free_field) =11130 @field(namespace, Local.namespace_next_free_field) =
10871 @enumFromInt(local.mutate.namespaces.free_list);11131 @enumFromInt(local.mutate.namespaces.free_list);
...@@ -11000,7 +11260,6 @@ pub fn getOrPutTrailingString(...@@ -11000,7 +11260,6 @@ pub fn getOrPutTrailingString(
11000 shard.mutate.string_map.mutex.lock();11260 shard.mutate.string_map.mutex.lock();
11001 defer shard.mutate.string_map.mutex.unlock();11261 defer shard.mutate.string_map.mutex.unlock();
11002 if (map.entries != shard.shared.string_map.entries) {11262 if (map.entries != shard.shared.string_map.entries) {
11003 shard.mutate.string_map.len += 1;
11004 map = shard.shared.string_map;11263 map = shard.shared.string_map;
11005 map_mask = map.header().mask();11264 map_mask = map.header().mask();
11006 map_index = hash;11265 map_index = hash;
src/Sema.zig+375-266
...@@ -110,6 +110,12 @@ exports: std.ArrayListUnmanaged(Zcu.Export) = .{},...@@ -110,6 +110,12 @@ exports: std.ArrayListUnmanaged(Zcu.Export) = .{},
110/// of data stored in `Zcu.all_references`. It exists to avoid adding references to110/// of data stored in `Zcu.all_references`. It exists to avoid adding references to
111/// a given `AnalUnit` multiple times.111/// a given `AnalUnit` multiple times.
112references: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .{},112references: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .{},
113type_references: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{},
114
115/// All dependencies registered so far by this `Sema`. This is a temporary duplicate
116/// of the main dependency data. It exists to avoid adding dependencies to a given
117/// `AnalUnit` multiple times.
118dependencies: std.AutoArrayHashMapUnmanaged(InternPool.Dependee, void) = .{},
113119
114const MaybeComptimeAlloc = struct {120const MaybeComptimeAlloc = struct {
115 /// The runtime index of the `alloc` instruction.121 /// The runtime index of the `alloc` instruction.
...@@ -877,6 +883,8 @@ pub fn deinit(sema: *Sema) void {...@@ -877,6 +883,8 @@ pub fn deinit(sema: *Sema) void {
877 sema.comptime_allocs.deinit(gpa);883 sema.comptime_allocs.deinit(gpa);
878 sema.exports.deinit(gpa);884 sema.exports.deinit(gpa);
879 sema.references.deinit(gpa);885 sema.references.deinit(gpa);
886 sema.type_references.deinit(gpa);
887 sema.dependencies.deinit(gpa);
880 sema.* = undefined;888 sema.* = undefined;
881}889}
882890
...@@ -999,7 +1007,7 @@ fn analyzeBodyInner(...@@ -999,7 +1007,7 @@ fn analyzeBodyInner(
999 // The hashmap lookup in here is a little expensive, and LLVM fails to optimize it away.1007 // The hashmap lookup in here is a little expensive, and LLVM fails to optimize it away.
1000 if (build_options.enable_logging) {1008 if (build_options.enable_logging) {
1001 std.log.scoped(.sema_zir).debug("sema ZIR {s} %{d}", .{ sub_file_path: {1009 std.log.scoped(.sema_zir).debug("sema ZIR {s} %{d}", .{ sub_file_path: {
1002 const file_index = block.src_base_inst.resolveFull(&zcu.intern_pool).file;1010 const file_index = block.src_base_inst.resolveFile(&zcu.intern_pool);
1003 const file = zcu.fileByIndex(file_index);1011 const file = zcu.fileByIndex(file_index);
1004 break :sub_file_path file.sub_file_path;1012 break :sub_file_path file.sub_file_path;
1005 }, inst });1013 }, inst });
...@@ -2496,12 +2504,12 @@ pub fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Module.Error...@@ -2496,12 +2504,12 @@ pub fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Module.Error
2496 const mod = sema.pt.zcu;2504 const mod = sema.pt.zcu;
24972505
2498 if (build_options.enable_debug_extensions and mod.comp.debug_compile_errors) {2506 if (build_options.enable_debug_extensions and mod.comp.debug_compile_errors) {
2499 var all_references = mod.resolveReferences() catch @panic("out of memory");2507 var all_references: ?std.AutoHashMapUnmanaged(AnalUnit, ?Zcu.ResolvedReference) = null;
2500 var wip_errors: std.zig.ErrorBundle.Wip = undefined;2508 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
2501 wip_errors.init(gpa) catch @panic("out of memory");2509 wip_errors.init(gpa) catch @panic("out of memory");
2502 Compilation.addModuleErrorMsg(mod, &wip_errors, err_msg.*, &all_references) catch unreachable;2510 Compilation.addModuleErrorMsg(mod, &wip_errors, err_msg.*, &all_references) catch @panic("out of memory");
2503 std.debug.print("compile error during Sema:\n", .{});2511 std.debug.print("compile error during Sema:\n", .{});
2504 var error_bundle = wip_errors.toOwnedBundle("") catch unreachable;2512 var error_bundle = wip_errors.toOwnedBundle("") catch @panic("out of memory");
2505 error_bundle.renderToStdErr(.{ .ttyconf = .no_color });2513 error_bundle.renderToStdErr(.{ .ttyconf = .no_color });
2506 crash_report.compilerPanic("unexpected compile error occurred", null, null);2514 crash_report.compilerPanic("unexpected compile error occurred", null, null);
2507 }2515 }
...@@ -2715,33 +2723,6 @@ fn wrapWipTy(sema: *Sema, wip_ty: anytype) @TypeOf(wip_ty) {...@@ -2715,33 +2723,6 @@ fn wrapWipTy(sema: *Sema, wip_ty: anytype) @TypeOf(wip_ty) {
2715 return new;2723 return new;
2716}2724}
27172725
2718/// Given a type just looked up in the `InternPool`, check whether it is
2719/// considered outdated on this update. If so, remove it from the pool
2720/// and return `true`.
2721fn maybeRemoveOutdatedType(sema: *Sema, ty: InternPool.Index) !bool {
2722 const pt = sema.pt;
2723 const zcu = pt.zcu;
2724 const ip = &zcu.intern_pool;
2725
2726 if (!zcu.comp.incremental) return false;
2727
2728 const cau_index = switch (ip.indexToKey(ty)) {
2729 .struct_type => ip.loadStructType(ty).cau.unwrap().?,
2730 .union_type => ip.loadUnionType(ty).cau,
2731 .enum_type => ip.loadEnumType(ty).cau.unwrap().?,
2732 else => unreachable,
2733 };
2734 const cau_unit = AnalUnit.wrap(.{ .cau = cau_index });
2735 const was_outdated = zcu.outdated.swapRemove(cau_unit) or
2736 zcu.potentially_outdated.swapRemove(cau_unit);
2737 if (!was_outdated) return false;
2738 _ = zcu.outdated_ready.swapRemove(cau_unit);
2739 zcu.intern_pool.removeDependenciesForDepender(zcu.gpa, cau_unit);
2740 zcu.intern_pool.remove(pt.tid, ty);
2741 try zcu.markDependeeOutdated(.{ .interned = ty });
2742 return true;
2743}
2744
2745fn zirStructDecl(2726fn zirStructDecl(
2746 sema: *Sema,2727 sema: *Sema,
2747 block: *Block,2728 block: *Block,
...@@ -2807,10 +2788,17 @@ fn zirStructDecl(...@@ -2807,10 +2788,17 @@ fn zirStructDecl(
2807 .captures = captures,2788 .captures = captures,
2808 } },2789 } },
2809 };2790 };
2810 const wip_ty = sema.wrapWipTy(switch (try ip.getStructType(gpa, pt.tid, struct_init)) {2791 const wip_ty = sema.wrapWipTy(switch (try ip.getStructType(gpa, pt.tid, struct_init, false)) {
2811 .existing => |ty| wip: {2792 .existing => |ty| {
2812 if (!try sema.maybeRemoveOutdatedType(ty)) return Air.internedToRef(ty);2793 const new_ty = try pt.ensureTypeUpToDate(ty, false);
2813 break :wip (try ip.getStructType(gpa, pt.tid, struct_init)).wip;2794
2795 // Make sure we update the namespace if the declaration is re-analyzed, to pick
2796 // up on e.g. changed comptime decls.
2797 try pt.ensureNamespaceUpToDate(Type.fromInterned(new_ty).getNamespaceIndex(mod));
2798
2799 try sema.declareDependency(.{ .interned = new_ty });
2800 try sema.addTypeReferenceEntry(src, new_ty);
2801 return Air.internedToRef(new_ty);
2814 },2802 },
2815 .wip => |wip| wip,2803 .wip => |wip| wip,
2816 });2804 });
...@@ -2828,6 +2816,7 @@ fn zirStructDecl(...@@ -2828,6 +2816,7 @@ fn zirStructDecl(
2828 .parent = block.namespace.toOptional(),2816 .parent = block.namespace.toOptional(),
2829 .owner_type = wip_ty.index,2817 .owner_type = wip_ty.index,
2830 .file_scope = block.getFileScopeIndex(mod),2818 .file_scope = block.getFileScopeIndex(mod),
2819 .generation = mod.generation,
2831 });2820 });
2832 errdefer pt.destroyNamespace(new_namespace_index);2821 errdefer pt.destroyNamespace(new_namespace_index);
28332822
...@@ -2850,8 +2839,8 @@ fn zirStructDecl(...@@ -2850,8 +2839,8 @@ fn zirStructDecl(
2850 if (block.ownerModule().strip) break :codegen_type;2839 if (block.ownerModule().strip) break :codegen_type;
2851 try mod.comp.queueJob(.{ .codegen_type = wip_ty.index });2840 try mod.comp.queueJob(.{ .codegen_type = wip_ty.index });
2852 }2841 }
2853 try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .cau = new_cau_index }));
2854 try sema.declareDependency(.{ .interned = wip_ty.index });2842 try sema.declareDependency(.{ .interned = wip_ty.index });
2843 try sema.addTypeReferenceEntry(src, wip_ty.index);
2855 return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index));2844 return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index));
2856}2845}
28572846
...@@ -2873,7 +2862,7 @@ fn createTypeName(...@@ -2873,7 +2862,7 @@ fn createTypeName(
2873 .anon => {}, // handled after switch2862 .anon => {}, // handled after switch
2874 .parent => return block.type_name_ctx,2863 .parent => return block.type_name_ctx,
2875 .func => func_strat: {2864 .func => func_strat: {
2876 const fn_info = sema.code.getFnInfo(ip.funcZirBodyInst(sema.func_index).resolve(ip));2865 const fn_info = sema.code.getFnInfo(ip.funcZirBodyInst(sema.func_index).resolve(ip) orelse return error.AnalysisFail);
2877 const zir_tags = sema.code.instructions.items(.tag);2866 const zir_tags = sema.code.instructions.items(.tag);
28782867
2879 var buf: std.ArrayListUnmanaged(u8) = .{};2868 var buf: std.ArrayListUnmanaged(u8) = .{};
...@@ -2966,7 +2955,6 @@ fn zirEnumDecl(...@@ -2966,7 +2955,6 @@ fn zirEnumDecl(
29662955
2967 const tracked_inst = try block.trackZir(inst);2956 const tracked_inst = try block.trackZir(inst);
2968 const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(0) };2957 const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(0) };
2969 const tag_ty_src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = .{ .node_offset_container_tag = 0 } };
29702958
2971 const tag_type_ref = if (small.has_tag_type) blk: {2959 const tag_type_ref = if (small.has_tag_type) blk: {
2972 const tag_type_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);2960 const tag_type_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
...@@ -3029,10 +3017,17 @@ fn zirEnumDecl(...@@ -3029,10 +3017,17 @@ fn zirEnumDecl(
3029 .captures = captures,3017 .captures = captures,
3030 } },3018 } },
3031 };3019 };
3032 const wip_ty = sema.wrapWipTy(switch (try ip.getEnumType(gpa, pt.tid, enum_init)) {3020 const wip_ty = sema.wrapWipTy(switch (try ip.getEnumType(gpa, pt.tid, enum_init, false)) {
3033 .existing => |ty| wip: {3021 .existing => |ty| {
3034 if (!try sema.maybeRemoveOutdatedType(ty)) return Air.internedToRef(ty);3022 const new_ty = try pt.ensureTypeUpToDate(ty, false);
3035 break :wip (try ip.getEnumType(gpa, pt.tid, enum_init)).wip;3023
3024 // Make sure we update the namespace if the declaration is re-analyzed, to pick
3025 // up on e.g. changed comptime decls.
3026 try pt.ensureNamespaceUpToDate(Type.fromInterned(new_ty).getNamespaceIndex(mod));
3027
3028 try sema.declareDependency(.{ .interned = new_ty });
3029 try sema.addTypeReferenceEntry(src, new_ty);
3030 return Air.internedToRef(new_ty);
3036 },3031 },
3037 .wip => |wip| wip,3032 .wip => |wip| wip,
3038 });3033 });
...@@ -3056,167 +3051,38 @@ fn zirEnumDecl(...@@ -3056,167 +3051,38 @@ fn zirEnumDecl(
3056 .parent = block.namespace.toOptional(),3051 .parent = block.namespace.toOptional(),
3057 .owner_type = wip_ty.index,3052 .owner_type = wip_ty.index,
3058 .file_scope = block.getFileScopeIndex(mod),3053 .file_scope = block.getFileScopeIndex(mod),
3054 .generation = mod.generation,
3059 });3055 });
3060 errdefer if (!done) pt.destroyNamespace(new_namespace_index);3056 errdefer if (!done) pt.destroyNamespace(new_namespace_index);
30613057
3062 const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index);3058 const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index);
30633059
3064 if (pt.zcu.comp.incremental) {
3065 try mod.intern_pool.addDependency(
3066 gpa,
3067 AnalUnit.wrap(.{ .cau = new_cau_index }),
3068 .{ .src_hash = try block.trackZir(inst) },
3069 );
3070 }
3071
3072 try pt.scanNamespace(new_namespace_index, decls);3060 try pt.scanNamespace(new_namespace_index, decls);
30733061
3074 try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .cau = new_cau_index }));
3075 try sema.declareDependency(.{ .interned = wip_ty.index });3062 try sema.declareDependency(.{ .interned = wip_ty.index });
3063 try sema.addTypeReferenceEntry(src, wip_ty.index);
30763064
3077 // We've finished the initial construction of this type, and are about to perform analysis.3065 // We've finished the initial construction of this type, and are about to perform analysis.
3078 // Set the Cau and namespace appropriately, and don't destroy anything on failure.3066 // Set the Cau and namespace appropriately, and don't destroy anything on failure.
3079 wip_ty.prepare(ip, new_cau_index, new_namespace_index);3067 wip_ty.prepare(ip, new_cau_index, new_namespace_index);
3080 done = true;3068 done = true;
30813069
3082 const int_tag_ty = ty: {3070 try Sema.resolveDeclaredEnum(
3083 // We create a block for the field type instructions because they3071 pt,
3084 // may need to reference Decls from inside the enum namespace.3072 wip_ty,
3085 // Within the field type, default value, and alignment expressions, the owner should be the enum's `Cau`.3073 inst,
30863074 tracked_inst,
3087 const prev_owner = sema.owner;3075 new_namespace_index,
3088 sema.owner = AnalUnit.wrap(.{ .cau = new_cau_index });3076 type_name,
3089 defer sema.owner = prev_owner;3077 new_cau_index,
30903078 small,
3091 const prev_func_index = sema.func_index;3079 body,
3092 sema.func_index = .none;3080 tag_type_ref,
3093 defer sema.func_index = prev_func_index;3081 any_values,
30943082 fields_len,
3095 var enum_block: Block = .{3083 sema.code,
3096 .parent = null,3084 body_end,
3097 .sema = sema,3085 );
3098 .namespace = new_namespace_index,
3099 .instructions = .{},
3100 .inlining = null,
3101 .is_comptime = true,
3102 .src_base_inst = tracked_inst,
3103 .type_name_ctx = type_name,
3104 };
3105 defer enum_block.instructions.deinit(sema.gpa);
3106
3107 if (body.len != 0) {
3108 _ = try sema.analyzeInlineBody(&enum_block, body, inst);
3109 }
3110
3111 if (tag_type_ref != .none) {
3112 const ty = try sema.resolveType(&enum_block, tag_ty_src, tag_type_ref);
3113 if (ty.zigTypeTag(mod) != .Int and ty.zigTypeTag(mod) != .ComptimeInt) {
3114 return sema.fail(&enum_block, tag_ty_src, "expected integer tag type, found '{}'", .{ty.fmt(pt)});
3115 }
3116 break :ty ty;
3117 } else if (fields_len == 0) {
3118 break :ty try pt.intType(.unsigned, 0);
3119 } else {
3120 const bits = std.math.log2_int_ceil(usize, fields_len);
3121 break :ty try pt.intType(.unsigned, bits);
3122 }
3123 };
3124
3125 wip_ty.setTagTy(ip, int_tag_ty.toIntern());
3126
3127 if (small.nonexhaustive and int_tag_ty.toIntern() != .comptime_int_type) {
3128 if (fields_len > 1 and std.math.log2_int(u64, fields_len) == int_tag_ty.bitSize(pt)) {
3129 return sema.fail(block, src, "non-exhaustive enum specifies every value", .{});
3130 }
3131 }
3132
3133 var bit_bag_index: usize = body_end;
3134 var cur_bit_bag: u32 = undefined;
3135 var field_i: u32 = 0;
3136 var last_tag_val: ?Value = null;
3137 while (field_i < fields_len) : (field_i += 1) {
3138 if (field_i % 32 == 0) {
3139 cur_bit_bag = sema.code.extra[bit_bag_index];
3140 bit_bag_index += 1;
3141 }
3142 const has_tag_value = @as(u1, @truncate(cur_bit_bag)) != 0;
3143 cur_bit_bag >>= 1;
3144
3145 const field_name_index: Zir.NullTerminatedString = @enumFromInt(sema.code.extra[extra_index]);
3146 const field_name_zir = sema.code.nullTerminatedString(field_name_index);
3147 extra_index += 2; // field name, doc comment
3148
3149 const field_name = try mod.intern_pool.getOrPutString(gpa, pt.tid, field_name_zir, .no_embedded_nulls);
3150
3151 const value_src: LazySrcLoc = .{
3152 .base_node_inst = tracked_inst,
3153 .offset = .{ .container_field_value = field_i },
3154 };
3155
3156 const tag_overflow = if (has_tag_value) overflow: {
3157 const tag_val_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
3158 extra_index += 1;
3159 const tag_inst = try sema.resolveInst(tag_val_ref);
3160 last_tag_val = try sema.resolveConstDefinedValue(block, .{
3161 .base_node_inst = tracked_inst,
3162 .offset = .{ .container_field_name = field_i },
3163 }, tag_inst, .{
3164 .needed_comptime_reason = "enum tag value must be comptime-known",
3165 });
3166 if (!(try sema.intFitsInType(last_tag_val.?, int_tag_ty, null))) break :overflow true;
3167 last_tag_val = try pt.getCoerced(last_tag_val.?, int_tag_ty);
3168 if (wip_ty.nextField(&mod.intern_pool, field_name, last_tag_val.?.toIntern())) |conflict| {
3169 assert(conflict.kind == .value); // AstGen validated names are unique
3170 const other_field_src: LazySrcLoc = .{
3171 .base_node_inst = tracked_inst,
3172 .offset = .{ .container_field_value = conflict.prev_field_idx },
3173 };
3174 const msg = msg: {
3175 const msg = try sema.errMsg(value_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValueSema(pt, sema)});
3176 errdefer msg.destroy(gpa);
3177 try sema.errNote(other_field_src, msg, "other occurrence here", .{});
3178 break :msg msg;
3179 };
3180 return sema.failWithOwnedErrorMsg(block, msg);
3181 }
3182 break :overflow false;
3183 } else if (any_values) overflow: {
3184 var overflow: ?usize = null;
3185 last_tag_val = if (last_tag_val) |val|
3186 try sema.intAdd(val, try pt.intValue(int_tag_ty, 1), int_tag_ty, &overflow)
3187 else
3188 try pt.intValue(int_tag_ty, 0);
3189 if (overflow != null) break :overflow true;
3190 if (wip_ty.nextField(&mod.intern_pool, field_name, last_tag_val.?.toIntern())) |conflict| {
3191 assert(conflict.kind == .value); // AstGen validated names are unique
3192 const other_field_src: LazySrcLoc = .{
3193 .base_node_inst = tracked_inst,
3194 .offset = .{ .container_field_value = conflict.prev_field_idx },
3195 };
3196 const msg = msg: {
3197 const msg = try sema.errMsg(value_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValueSema(pt, sema)});
3198 errdefer msg.destroy(gpa);
3199 try sema.errNote(other_field_src, msg, "other occurrence here", .{});
3200 break :msg msg;
3201 };
3202 return sema.failWithOwnedErrorMsg(block, msg);
3203 }
3204 break :overflow false;
3205 } else overflow: {
3206 assert(wip_ty.nextField(&mod.intern_pool, field_name, .none) == null);
3207 last_tag_val = try pt.intValue(Type.comptime_int, field_i);
3208 if (!try sema.intFitsInType(last_tag_val.?, int_tag_ty, null)) break :overflow true;
3209 last_tag_val = try pt.getCoerced(last_tag_val.?, int_tag_ty);
3210 break :overflow false;
3211 };
3212
3213 if (tag_overflow) {
3214 const msg = try sema.errMsg(value_src, "enumeration value '{}' too large for type '{}'", .{
3215 last_tag_val.?.fmtValueSema(pt, sema), int_tag_ty.fmt(pt),
3216 });
3217 return sema.failWithOwnedErrorMsg(block, msg);
3218 }
3219 }
32203086
3221 codegen_type: {3087 codegen_type: {
3222 if (mod.comp.config.use_llvm) break :codegen_type;3088 if (mod.comp.config.use_llvm) break :codegen_type;
...@@ -3295,10 +3161,17 @@ fn zirUnionDecl(...@@ -3295,10 +3161,17 @@ fn zirUnionDecl(
3295 .captures = captures,3161 .captures = captures,
3296 } },3162 } },
3297 };3163 };
3298 const wip_ty = sema.wrapWipTy(switch (try ip.getUnionType(gpa, pt.tid, union_init)) {3164 const wip_ty = sema.wrapWipTy(switch (try ip.getUnionType(gpa, pt.tid, union_init, false)) {
3299 .existing => |ty| wip: {3165 .existing => |ty| {
3300 if (!try sema.maybeRemoveOutdatedType(ty)) return Air.internedToRef(ty);3166 const new_ty = try pt.ensureTypeUpToDate(ty, false);
3301 break :wip (try ip.getUnionType(gpa, pt.tid, union_init)).wip;3167
3168 // Make sure we update the namespace if the declaration is re-analyzed, to pick
3169 // up on e.g. changed comptime decls.
3170 try pt.ensureNamespaceUpToDate(Type.fromInterned(new_ty).getNamespaceIndex(mod));
3171
3172 try sema.declareDependency(.{ .interned = new_ty });
3173 try sema.addTypeReferenceEntry(src, new_ty);
3174 return Air.internedToRef(new_ty);
3302 },3175 },
3303 .wip => |wip| wip,3176 .wip => |wip| wip,
3304 });3177 });
...@@ -3316,6 +3189,7 @@ fn zirUnionDecl(...@@ -3316,6 +3189,7 @@ fn zirUnionDecl(
3316 .parent = block.namespace.toOptional(),3189 .parent = block.namespace.toOptional(),
3317 .owner_type = wip_ty.index,3190 .owner_type = wip_ty.index,
3318 .file_scope = block.getFileScopeIndex(mod),3191 .file_scope = block.getFileScopeIndex(mod),
3192 .generation = mod.generation,
3319 });3193 });
3320 errdefer pt.destroyNamespace(new_namespace_index);3194 errdefer pt.destroyNamespace(new_namespace_index);
33213195
...@@ -3325,7 +3199,7 @@ fn zirUnionDecl(...@@ -3325,7 +3199,7 @@ fn zirUnionDecl(
3325 try mod.intern_pool.addDependency(3199 try mod.intern_pool.addDependency(
3326 gpa,3200 gpa,
3327 AnalUnit.wrap(.{ .cau = new_cau_index }),3201 AnalUnit.wrap(.{ .cau = new_cau_index }),
3328 .{ .src_hash = try block.trackZir(inst) },3202 .{ .src_hash = tracked_inst },
3329 );3203 );
3330 }3204 }
33313205
...@@ -3338,8 +3212,8 @@ fn zirUnionDecl(...@@ -3338,8 +3212,8 @@ fn zirUnionDecl(
3338 if (block.ownerModule().strip) break :codegen_type;3212 if (block.ownerModule().strip) break :codegen_type;
3339 try mod.comp.queueJob(.{ .codegen_type = wip_ty.index });3213 try mod.comp.queueJob(.{ .codegen_type = wip_ty.index });
3340 }3214 }
3341 try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .cau = new_cau_index }));
3342 try sema.declareDependency(.{ .interned = wip_ty.index });3215 try sema.declareDependency(.{ .interned = wip_ty.index });
3216 try sema.addTypeReferenceEntry(src, wip_ty.index);
3343 return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index));3217 return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index));
3344}3218}
33453219
...@@ -3387,8 +3261,15 @@ fn zirOpaqueDecl(...@@ -3387,8 +3261,15 @@ fn zirOpaqueDecl(
3387 };3261 };
3388 // No `wrapWipTy` needed as no std.builtin types are opaque.3262 // No `wrapWipTy` needed as no std.builtin types are opaque.
3389 const wip_ty = switch (try ip.getOpaqueType(gpa, pt.tid, opaque_init)) {3263 const wip_ty = switch (try ip.getOpaqueType(gpa, pt.tid, opaque_init)) {
3390 // No `maybeRemoveOutdatedType` as opaque types are never outdated.3264 .existing => |ty| {
3391 .existing => |ty| return Air.internedToRef(ty),3265 // Make sure we update the namespace if the declaration is re-analyzed, to pick
3266 // up on e.g. changed comptime decls.
3267 try pt.ensureNamespaceUpToDate(Type.fromInterned(ty).getNamespaceIndex(mod));
3268
3269 try sema.declareDependency(.{ .interned = ty });
3270 try sema.addTypeReferenceEntry(src, ty);
3271 return Air.internedToRef(ty);
3272 },
3392 .wip => |wip| wip,3273 .wip => |wip| wip,
3393 };3274 };
3394 errdefer wip_ty.cancel(ip, pt.tid);3275 errdefer wip_ty.cancel(ip, pt.tid);
...@@ -3405,6 +3286,7 @@ fn zirOpaqueDecl(...@@ -3405,6 +3286,7 @@ fn zirOpaqueDecl(
3405 .parent = block.namespace.toOptional(),3286 .parent = block.namespace.toOptional(),
3406 .owner_type = wip_ty.index,3287 .owner_type = wip_ty.index,
3407 .file_scope = block.getFileScopeIndex(mod),3288 .file_scope = block.getFileScopeIndex(mod),
3289 .generation = mod.generation,
3408 });3290 });
3409 errdefer pt.destroyNamespace(new_namespace_index);3291 errdefer pt.destroyNamespace(new_namespace_index);
34103292
...@@ -3416,6 +3298,7 @@ fn zirOpaqueDecl(...@@ -3416,6 +3298,7 @@ fn zirOpaqueDecl(
3416 if (block.ownerModule().strip) break :codegen_type;3298 if (block.ownerModule().strip) break :codegen_type;
3417 try mod.comp.queueJob(.{ .codegen_type = wip_ty.index });3299 try mod.comp.queueJob(.{ .codegen_type = wip_ty.index });
3418 }3300 }
3301 try sema.addTypeReferenceEntry(src, wip_ty.index);
3419 return Air.internedToRef(wip_ty.finish(ip, .none, new_namespace_index));3302 return Air.internedToRef(wip_ty.finish(ip, .none, new_namespace_index));
3420}3303}
34213304
...@@ -5487,7 +5370,7 @@ fn failWithBadMemberAccess(...@@ -5487,7 +5370,7 @@ fn failWithBadMemberAccess(
5487 .Enum => "enum",5370 .Enum => "enum",
5488 else => unreachable,5371 else => unreachable,
5489 };5372 };
5490 if (agg_ty.typeDeclInst(zcu)) |inst| if (inst.resolve(ip) == .main_struct_inst) {5373 if (agg_ty.typeDeclInst(zcu)) |inst| if ((inst.resolve(ip) orelse return error.AnalysisFail) == .main_struct_inst) {
5491 return sema.fail(block, field_src, "root struct of file '{}' has no member named '{}'", .{5374 return sema.fail(block, field_src, "root struct of file '{}' has no member named '{}'", .{
5492 agg_ty.fmt(pt), field_name.fmt(ip),5375 agg_ty.fmt(pt), field_name.fmt(ip),
5493 });5376 });
...@@ -6041,15 +5924,17 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr...@@ -6041,15 +5924,17 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
6041 return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});5924 return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
60425925
6043 const path_digest = zcu.filePathDigest(result.file_index);5926 const path_digest = zcu.filePathDigest(result.file_index);
6044 const old_root_type = zcu.fileRootType(result.file_index);5927 pt.astGenFile(result.file, path_digest) catch |err|
6045 pt.astGenFile(result.file, path_digest, old_root_type) catch |err|
6046 return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});5928 return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
60475929
6048 // TODO: register some kind of dependency on the file.5930 // TODO: register some kind of dependency on the file.
6049 // That way, if this returns `error.AnalysisFail`, we have the dependency banked ready to5931 // That way, if this returns `error.AnalysisFail`, we have the dependency banked ready to
6050 // trigger re-analysis later.5932 // trigger re-analysis later.
6051 try pt.ensureFileAnalyzed(result.file_index);5933 try pt.ensureFileAnalyzed(result.file_index);
6052 return Air.internedToRef(zcu.fileRootType(result.file_index));5934 const ty = zcu.fileRootType(result.file_index);
5935 try sema.declareDependency(.{ .interned = ty });
5936 try sema.addTypeReferenceEntry(src, ty);
5937 return Air.internedToRef(ty);
6053}5938}
60545939
6055fn zirSuspendBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {5940fn zirSuspendBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -6797,12 +6682,21 @@ fn lookupInNamespace(...@@ -6797,12 +6682,21 @@ fn lookupInNamespace(
6797 const zcu = pt.zcu;6682 const zcu = pt.zcu;
6798 const ip = &zcu.intern_pool;6683 const ip = &zcu.intern_pool;
67996684
6685 try pt.ensureNamespaceUpToDate(namespace_index);
6686
6800 const namespace = zcu.namespacePtr(namespace_index);6687 const namespace = zcu.namespacePtr(namespace_index);
68016688
6802 const adapter: Zcu.Namespace.NameAdapter = .{ .zcu = zcu };6689 const adapter: Zcu.Namespace.NameAdapter = .{ .zcu = zcu };
68036690
6804 const src_file = zcu.namespacePtr(block.namespace).file_scope;6691 const src_file = zcu.namespacePtr(block.namespace).file_scope;
68056692
6693 if (Type.fromInterned(namespace.owner_type).typeDeclInst(zcu)) |type_decl_inst| {
6694 try sema.declareDependency(.{ .namespace_name = .{
6695 .namespace = type_decl_inst,
6696 .name = ident_name,
6697 } });
6698 }
6699
6806 if (observe_usingnamespace and (namespace.pub_usingnamespace.items.len != 0 or namespace.priv_usingnamespace.items.len != 0)) {6700 if (observe_usingnamespace and (namespace.pub_usingnamespace.items.len != 0 or namespace.priv_usingnamespace.items.len != 0)) {
6807 const gpa = sema.gpa;6701 const gpa = sema.gpa;
6808 var checked_namespaces: std.AutoArrayHashMapUnmanaged(*Namespace, void) = .{};6702 var checked_namespaces: std.AutoArrayHashMapUnmanaged(*Namespace, void) = .{};
...@@ -7528,14 +7422,14 @@ fn analyzeCall(...@@ -7528,14 +7422,14 @@ fn analyzeCall(
7528 operation: CallOperation,7422 operation: CallOperation,
7529) CompileError!Air.Inst.Ref {7423) CompileError!Air.Inst.Ref {
7530 const pt = sema.pt;7424 const pt = sema.pt;
7531 const mod = pt.zcu;7425 const zcu = pt.zcu;
7532 const ip = &mod.intern_pool;7426 const ip = &zcu.intern_pool;
75337427
7534 const callee_ty = sema.typeOf(func);7428 const callee_ty = sema.typeOf(func);
7535 const func_ty_info = mod.typeToFunc(func_ty).?;7429 const func_ty_info = zcu.typeToFunc(func_ty).?;
7536 const cc = func_ty_info.cc;7430 const cc = func_ty_info.cc;
7537 if (try sema.resolveValue(func)) |func_val|7431 if (try sema.resolveValue(func)) |func_val|
7538 if (func_val.isUndef(mod))7432 if (func_val.isUndef(zcu))
7539 return sema.failWithUseOfUndef(block, call_src);7433 return sema.failWithUseOfUndef(block, call_src);
7540 if (cc == .Naked) {7434 if (cc == .Naked) {
7541 const maybe_func_inst = try sema.funcDeclSrcInst(func);7435 const maybe_func_inst = try sema.funcDeclSrcInst(func);
...@@ -7647,7 +7541,7 @@ fn analyzeCall(...@@ -7647,7 +7541,7 @@ fn analyzeCall(
7647 .needed_comptime_reason = "function being called at comptime must be comptime-known",7541 .needed_comptime_reason = "function being called at comptime must be comptime-known",
7648 .block_comptime_reason = comptime_reason,7542 .block_comptime_reason = comptime_reason,
7649 });7543 });
7650 const module_fn_index = switch (mod.intern_pool.indexToKey(func_val.toIntern())) {7544 const module_fn_index = switch (zcu.intern_pool.indexToKey(func_val.toIntern())) {
7651 .@"extern" => return sema.fail(block, call_src, "{s} call of extern function", .{7545 .@"extern" => return sema.fail(block, call_src, "{s} call of extern function", .{
7652 @as([]const u8, if (is_comptime_call) "comptime" else "inline"),7546 @as([]const u8, if (is_comptime_call) "comptime" else "inline"),
7653 }),7547 }),
...@@ -7664,7 +7558,7 @@ fn analyzeCall(...@@ -7664,7 +7558,7 @@ fn analyzeCall(
7664 },7558 },
7665 else => {},7559 else => {},
7666 }7560 }
7667 assert(callee_ty.isPtrAtRuntime(mod));7561 assert(callee_ty.isPtrAtRuntime(zcu));
7668 return sema.fail(block, call_src, "{s} call of function pointer", .{7562 return sema.fail(block, call_src, "{s} call of function pointer", .{
7669 if (is_comptime_call) "comptime" else "inline",7563 if (is_comptime_call) "comptime" else "inline",
7670 });7564 });
...@@ -7704,7 +7598,7 @@ fn analyzeCall(...@@ -7704,7 +7598,7 @@ fn analyzeCall(
7704 },7598 },
7705 };7599 };
77067600
7707 const module_fn = mod.funcInfo(module_fn_index);7601 const module_fn = zcu.funcInfo(module_fn_index);
77087602
7709 // This is not a function instance, so the function's `Nav` has a7603 // This is not a function instance, so the function's `Nav` has a
7710 // `Cau` -- we don't need to check `generic_owner`.7604 // `Cau` -- we don't need to check `generic_owner`.
...@@ -7718,7 +7612,7 @@ fn analyzeCall(...@@ -7718,7 +7612,7 @@ fn analyzeCall(
7718 // whenever performing an operation where the difference matters.7612 // whenever performing an operation where the difference matters.
7719 var ics = InlineCallSema.init(7613 var ics = InlineCallSema.init(
7720 sema,7614 sema,
7721 mod.cauFileScope(fn_cau_index).zir,7615 zcu.cauFileScope(fn_cau_index).zir,
7722 module_fn_index,7616 module_fn_index,
7723 block.error_return_trace_index,7617 block.error_return_trace_index,
7724 );7618 );
...@@ -7752,13 +7646,16 @@ fn analyzeCall(...@@ -7752,13 +7646,16 @@ fn analyzeCall(
77527646
7753 // Whether this call should be memoized, set to false if the call can7647 // Whether this call should be memoized, set to false if the call can
7754 // mutate comptime state.7648 // mutate comptime state.
7755 var should_memoize = true;7649 // TODO: comptime call memoization is currently not supported under incremental compilation
7650 // since dependencies are not marked on callers. If we want to keep this around (we should
7651 // check that it's worthwhile first!), each memoized call needs a `Cau`.
7652 var should_memoize = !zcu.comp.incremental;
77567653
7757 // If it's a comptime function call, we need to memoize it as long as no external7654 // If it's a comptime function call, we need to memoize it as long as no external
7758 // comptime memory is mutated.7655 // comptime memory is mutated.
7759 const memoized_arg_values = try sema.arena.alloc(InternPool.Index, func_ty_info.param_types.len);7656 const memoized_arg_values = try sema.arena.alloc(InternPool.Index, func_ty_info.param_types.len);
77607657
7761 const owner_info = mod.typeToFunc(Type.fromInterned(module_fn.ty)).?;7658 const owner_info = zcu.typeToFunc(Type.fromInterned(module_fn.ty)).?;
7762 const new_param_types = try sema.arena.alloc(InternPool.Index, owner_info.param_types.len);7659 const new_param_types = try sema.arena.alloc(InternPool.Index, owner_info.param_types.len);
7763 var new_fn_info: InternPool.GetFuncTypeKey = .{7660 var new_fn_info: InternPool.GetFuncTypeKey = .{
7764 .param_types = new_param_types,7661 .param_types = new_param_types,
...@@ -7778,7 +7675,7 @@ fn analyzeCall(...@@ -7778,7 +7675,7 @@ fn analyzeCall(
7778 // the AIR instructions of the callsite. The callee could be a generic function7675 // the AIR instructions of the callsite. The callee could be a generic function
7779 // which means its parameter type expressions must be resolved in order and used7676 // which means its parameter type expressions must be resolved in order and used
7780 // to successively coerce the arguments.7677 // to successively coerce the arguments.
7781 const fn_info = ics.callee().code.getFnInfo(module_fn.zir_body_inst.resolve(ip));7678 const fn_info = ics.callee().code.getFnInfo(module_fn.zir_body_inst.resolve(ip) orelse return error.AnalysisFail);
7782 try ics.callee().inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body);7679 try ics.callee().inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body);
77837680
7784 var arg_i: u32 = 0;7681 var arg_i: u32 = 0;
...@@ -7823,7 +7720,7 @@ fn analyzeCall(...@@ -7823,7 +7720,7 @@ fn analyzeCall(
7823 // each of the parameters, resolving the return type and providing it to the child7720 // each of the parameters, resolving the return type and providing it to the child
7824 // `Sema` so that it can be used for the `ret_ptr` instruction.7721 // `Sema` so that it can be used for the `ret_ptr` instruction.
7825 const ret_ty_inst = if (fn_info.ret_ty_body.len != 0)7722 const ret_ty_inst = if (fn_info.ret_ty_body.len != 0)
7826 try sema.resolveInlineBody(&child_block, fn_info.ret_ty_body, module_fn.zir_body_inst.resolve(ip))7723 try sema.resolveInlineBody(&child_block, fn_info.ret_ty_body, module_fn.zir_body_inst.resolve(ip) orelse return error.AnalysisFail)
7827 else7724 else
7828 try sema.resolveInst(fn_info.ret_ty_ref);7725 try sema.resolveInst(fn_info.ret_ty_ref);
7829 const ret_ty_src: LazySrcLoc = .{ .base_node_inst = module_fn.zir_body_inst, .offset = .{ .node_offset_fn_type_ret_ty = 0 } };7726 const ret_ty_src: LazySrcLoc = .{ .base_node_inst = module_fn.zir_body_inst, .offset = .{ .node_offset_fn_type_ret_ty = 0 } };
...@@ -7843,12 +7740,12 @@ fn analyzeCall(...@@ -7843,12 +7740,12 @@ fn analyzeCall(
7843 // bug generating invalid LLVM IR.7740 // bug generating invalid LLVM IR.
7844 const res2: Air.Inst.Ref = res2: {7741 const res2: Air.Inst.Ref = res2: {
7845 if (should_memoize and is_comptime_call) {7742 if (should_memoize and is_comptime_call) {
7846 if (mod.intern_pool.getIfExists(.{ .memoized_call = .{7743 if (zcu.intern_pool.getIfExists(.{ .memoized_call = .{
7847 .func = module_fn_index,7744 .func = module_fn_index,
7848 .arg_values = memoized_arg_values,7745 .arg_values = memoized_arg_values,
7849 .result = .none,7746 .result = .none,
7850 } })) |memoized_call_index| {7747 } })) |memoized_call_index| {
7851 const memoized_call = mod.intern_pool.indexToKey(memoized_call_index).memoized_call;7748 const memoized_call = zcu.intern_pool.indexToKey(memoized_call_index).memoized_call;
7852 break :res2 Air.internedToRef(memoized_call.result);7749 break :res2 Air.internedToRef(memoized_call.result);
7853 }7750 }
7854 }7751 }
...@@ -7907,7 +7804,7 @@ fn analyzeCall(...@@ -7907,7 +7804,7 @@ fn analyzeCall(
7907 // a reference to `comptime_allocs` so is not stable across instances of `Sema`.7804 // a reference to `comptime_allocs` so is not stable across instances of `Sema`.
7908 // TODO: check whether any external comptime memory was mutated by the7805 // TODO: check whether any external comptime memory was mutated by the
7909 // comptime function call. If so, then do not memoize the call here.7806 // comptime function call. If so, then do not memoize the call here.
7910 if (should_memoize and !Value.fromInterned(result_interned).canMutateComptimeVarState(mod)) {7807 if (should_memoize and !Value.fromInterned(result_interned).canMutateComptimeVarState(zcu)) {
7911 _ = try pt.intern(.{ .memoized_call = .{7808 _ = try pt.intern(.{ .memoized_call = .{
7912 .func = module_fn_index,7809 .func = module_fn_index,
7913 .arg_values = memoized_arg_values,7810 .arg_values = memoized_arg_values,
...@@ -7946,7 +7843,7 @@ fn analyzeCall(...@@ -7946,7 +7843,7 @@ fn analyzeCall(
7946 if (param_ty) |t| assert(!t.isGenericPoison());7843 if (param_ty) |t| assert(!t.isGenericPoison());
7947 arg_out.* = try args_info.analyzeArg(sema, block, arg_idx, param_ty, func_ty_info, func);7844 arg_out.* = try args_info.analyzeArg(sema, block, arg_idx, param_ty, func_ty_info, func);
7948 try sema.validateRuntimeValue(block, args_info.argSrc(block, arg_idx), arg_out.*);7845 try sema.validateRuntimeValue(block, args_info.argSrc(block, arg_idx), arg_out.*);
7949 if (sema.typeOf(arg_out.*).zigTypeTag(mod) == .NoReturn) {7846 if (sema.typeOf(arg_out.*).zigTypeTag(zcu) == .NoReturn) {
7950 return arg_out.*;7847 return arg_out.*;
7951 }7848 }
7952 }7849 }
...@@ -7955,15 +7852,15 @@ fn analyzeCall(...@@ -7955,15 +7852,15 @@ fn analyzeCall(
79557852
7956 switch (sema.owner.unwrap()) {7853 switch (sema.owner.unwrap()) {
7957 .cau => {},7854 .cau => {},
7958 .func => |owner_func| if (Type.fromInterned(func_ty_info.return_type).isError(mod)) {7855 .func => |owner_func| if (Type.fromInterned(func_ty_info.return_type).isError(zcu)) {
7959 ip.funcSetCallsOrAwaitsErrorableFn(owner_func);7856 ip.funcSetCallsOrAwaitsErrorableFn(owner_func);
7960 },7857 },
7961 }7858 }
79627859
7963 if (try sema.resolveValue(func)) |func_val| {7860 if (try sema.resolveValue(func)) |func_val| {
7964 if (mod.intern_pool.isFuncBody(func_val.toIntern())) {7861 if (zcu.intern_pool.isFuncBody(func_val.toIntern())) {
7965 try sema.addReferenceEntry(call_src, AnalUnit.wrap(.{ .func = func_val.toIntern() }));7862 try sema.addReferenceEntry(call_src, AnalUnit.wrap(.{ .func = func_val.toIntern() }));
7966 try mod.ensureFuncBodyAnalysisQueued(func_val.toIntern());7863 try zcu.ensureFuncBodyAnalysisQueued(func_val.toIntern());
7967 }7864 }
7968 }7865 }
79697866
...@@ -7990,7 +7887,7 @@ fn analyzeCall(...@@ -7990,7 +7887,7 @@ fn analyzeCall(
7990 // Function pointers and extern functions aren't guaranteed to7887 // Function pointers and extern functions aren't guaranteed to
7991 // actually be noreturn so we add a safety check for them.7888 // actually be noreturn so we add a safety check for them.
7992 if (try sema.resolveValue(func)) |func_val| {7889 if (try sema.resolveValue(func)) |func_val| {
7993 switch (mod.intern_pool.indexToKey(func_val.toIntern())) {7890 switch (zcu.intern_pool.indexToKey(func_val.toIntern())) {
7994 .func => break :skip_safety,7891 .func => break :skip_safety,
7995 .ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) {7892 .ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) {
7996 .nav => |nav| if (!ip.getNav(nav).isExtern(ip)) break :skip_safety,7893 .nav => |nav| if (!ip.getNav(nav).isExtern(ip)) break :skip_safety,
...@@ -8210,7 +8107,7 @@ fn instantiateGenericCall(...@@ -8210,7 +8107,7 @@ fn instantiateGenericCall(
8210 const fn_nav = ip.getNav(generic_owner_func.owner_nav);8107 const fn_nav = ip.getNav(generic_owner_func.owner_nav);
8211 const fn_cau = ip.getCau(fn_nav.analysis_owner.unwrap().?);8108 const fn_cau = ip.getCau(fn_nav.analysis_owner.unwrap().?);
8212 const fn_zir = zcu.namespacePtr(fn_cau.namespace).fileScope(zcu).zir;8109 const fn_zir = zcu.namespacePtr(fn_cau.namespace).fileScope(zcu).zir;
8213 const fn_info = fn_zir.getFnInfo(generic_owner_func.zir_body_inst.resolve(ip));8110 const fn_info = fn_zir.getFnInfo(generic_owner_func.zir_body_inst.resolve(ip) orelse return error.AnalysisFail);
82148111
8215 const comptime_args = try sema.arena.alloc(InternPool.Index, args_info.count());8112 const comptime_args = try sema.arena.alloc(InternPool.Index, args_info.count());
8216 @memset(comptime_args, .none);8113 @memset(comptime_args, .none);
...@@ -9416,7 +9313,7 @@ fn zirFunc(...@@ -9416,7 +9313,7 @@ fn zirFunc(
9416 break :cau generic_owner_nav.analysis_owner.unwrap().?;9313 break :cau generic_owner_nav.analysis_owner.unwrap().?;
9417 } else sema.owner.unwrap().cau;9314 } else sema.owner.unwrap().cau;
9418 const fn_is_exported = exported: {9315 const fn_is_exported = exported: {
9419 const decl_inst = ip.getCau(func_decl_cau).zir_index.resolve(ip);9316 const decl_inst = ip.getCau(func_decl_cau).zir_index.resolve(ip) orelse return error.AnalysisFail;
9420 const zir_decl = sema.code.getDeclaration(decl_inst)[0];9317 const zir_decl = sema.code.getDeclaration(decl_inst)[0];
9421 break :exported zir_decl.flags.is_export;9318 break :exported zir_decl.flags.is_export;
9422 };9319 };
...@@ -13964,12 +13861,6 @@ fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -13964,12 +13861,6 @@ fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
13964 });13861 });
1396513862
13966 try sema.checkNamespaceType(block, lhs_src, container_type);13863 try sema.checkNamespaceType(block, lhs_src, container_type);
13967 if (container_type.typeDeclInst(mod)) |type_decl_inst| {
13968 try sema.declareDependency(.{ .namespace_name = .{
13969 .namespace = type_decl_inst,
13970 .name = decl_name,
13971 } });
13972 }
1397313864
13974 const namespace = container_type.getNamespace(mod).unwrap() orelse return .bool_false;13865 const namespace = container_type.getNamespace(mod).unwrap() orelse return .bool_false;
13975 if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |lookup| {13866 if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |lookup| {
...@@ -14009,7 +13900,10 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air....@@ -14009,7 +13900,10 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
14009 // That way, if this returns `error.AnalysisFail`, we have the dependency banked ready to13900 // That way, if this returns `error.AnalysisFail`, we have the dependency banked ready to
14010 // trigger re-analysis later.13901 // trigger re-analysis later.
14011 try pt.ensureFileAnalyzed(result.file_index);13902 try pt.ensureFileAnalyzed(result.file_index);
14012 return Air.internedToRef(zcu.fileRootType(result.file_index));13903 const ty = zcu.fileRootType(result.file_index);
13904 try sema.declareDependency(.{ .interned = ty });
13905 try sema.addTypeReferenceEntry(operand_src, ty);
13906 return Air.internedToRef(ty);
14013}13907}
1401413908
14015fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {13909fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -17673,7 +17567,13 @@ fn zirThis(...@@ -17673,7 +17567,13 @@ fn zirThis(
17673 _ = extended;17567 _ = extended;
17674 const pt = sema.pt;17568 const pt = sema.pt;
17675 const namespace = pt.zcu.namespacePtr(block.namespace);17569 const namespace = pt.zcu.namespacePtr(block.namespace);
17676 return Air.internedToRef(namespace.owner_type);17570 const new_ty = try pt.ensureTypeUpToDate(namespace.owner_type, false);
17571 switch (pt.zcu.intern_pool.indexToKey(new_ty)) {
17572 .struct_type, .union_type, .enum_type => try sema.declareDependency(.{ .interned = new_ty }),
17573 .opaque_type => {},
17574 else => unreachable,
17575 }
17576 return Air.internedToRef(new_ty);
17677}17577}
1767817578
17679fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {17579fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
...@@ -17698,7 +17598,7 @@ fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat...@@ -17698,7 +17598,7 @@ fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat
17698 const msg = msg: {17598 const msg = msg: {
17699 const name = name: {17599 const name = name: {
17700 // TODO: we should probably store this name in the ZIR to avoid this complexity.17600 // TODO: we should probably store this name in the ZIR to avoid this complexity.
17701 const file, const src_base_node = Module.LazySrcLoc.resolveBaseNode(block.src_base_inst, mod);17601 const file, const src_base_node = Module.LazySrcLoc.resolveBaseNode(block.src_base_inst, mod).?;
17702 const tree = file.getTree(sema.gpa) catch |err| {17602 const tree = file.getTree(sema.gpa) catch |err| {
17703 // In this case we emit a warning + a less precise source location.17603 // In this case we emit a warning + a less precise source location.
17704 log.warn("unable to load {s}: {s}", .{17604 log.warn("unable to load {s}: {s}", .{
...@@ -17726,7 +17626,7 @@ fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat...@@ -17726,7 +17626,7 @@ fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat
17726 if (!block.is_typeof and !block.is_comptime and sema.func_index != .none) {17626 if (!block.is_typeof and !block.is_comptime and sema.func_index != .none) {
17727 const msg = msg: {17627 const msg = msg: {
17728 const name = name: {17628 const name = name: {
17729 const file, const src_base_node = Module.LazySrcLoc.resolveBaseNode(block.src_base_inst, mod);17629 const file, const src_base_node = Module.LazySrcLoc.resolveBaseNode(block.src_base_inst, mod).?;
17730 const tree = file.getTree(sema.gpa) catch |err| {17630 const tree = file.getTree(sema.gpa) catch |err| {
17731 // In this case we emit a warning + a less precise source location.17631 // In this case we emit a warning + a less precise source location.
17732 log.warn("unable to load {s}: {s}", .{17632 log.warn("unable to load {s}: {s}", .{
...@@ -18975,6 +18875,7 @@ fn typeInfoNamespaceDecls(...@@ -18975,6 +18875,7 @@ fn typeInfoNamespaceDecls(
18975 const ip = &zcu.intern_pool;18875 const ip = &zcu.intern_pool;
1897618876
18977 const namespace_index = opt_namespace_index.unwrap() orelse return;18877 const namespace_index = opt_namespace_index.unwrap() orelse return;
18878 try pt.ensureNamespaceUpToDate(namespace_index);
18978 const namespace = zcu.namespacePtr(namespace_index);18879 const namespace = zcu.namespacePtr(namespace_index);
1897918880
18980 const gop = try seen_namespaces.getOrPut(namespace);18881 const gop = try seen_namespaces.getOrPut(namespace);
...@@ -21821,7 +21722,10 @@ fn zirReify(...@@ -21821,7 +21722,10 @@ fn zirReify(
21821 .zir_index = try block.trackZir(inst),21722 .zir_index = try block.trackZir(inst),
21822 } },21723 } },
21823 })) {21724 })) {
21824 .existing => |ty| return Air.internedToRef(ty),21725 .existing => |ty| {
21726 try sema.addTypeReferenceEntry(src, ty);
21727 return Air.internedToRef(ty);
21728 },
21825 .wip => |wip| wip,21729 .wip => |wip| wip,
21826 };21730 };
21827 errdefer wip_ty.cancel(ip, pt.tid);21731 errdefer wip_ty.cancel(ip, pt.tid);
...@@ -21838,8 +21742,10 @@ fn zirReify(...@@ -21838,8 +21742,10 @@ fn zirReify(
21838 .parent = block.namespace.toOptional(),21742 .parent = block.namespace.toOptional(),
21839 .owner_type = wip_ty.index,21743 .owner_type = wip_ty.index,
21840 .file_scope = block.getFileScopeIndex(mod),21744 .file_scope = block.getFileScopeIndex(mod),
21745 .generation = mod.generation,
21841 });21746 });
2184221747
21748 try sema.addTypeReferenceEntry(src, wip_ty.index);
21843 return Air.internedToRef(wip_ty.finish(ip, .none, new_namespace_index));21749 return Air.internedToRef(wip_ty.finish(ip, .none, new_namespace_index));
21844 },21750 },
21845 .Union => {21751 .Union => {
...@@ -22019,11 +21925,16 @@ fn reifyEnum(...@@ -22019,11 +21925,16 @@ fn reifyEnum(
22019 .zir_index = tracked_inst,21925 .zir_index = tracked_inst,
22020 .type_hash = hasher.final(),21926 .type_hash = hasher.final(),
22021 } },21927 } },
22022 })) {21928 }, false)) {
22023 .wip => |wip| wip,21929 .wip => |wip| wip,
22024 .existing => |ty| return Air.internedToRef(ty),21930 .existing => |ty| {
21931 try sema.declareDependency(.{ .interned = ty });
21932 try sema.addTypeReferenceEntry(src, ty);
21933 return Air.internedToRef(ty);
21934 },
22025 };21935 };
22026 errdefer wip_ty.cancel(ip, pt.tid);21936 var done = false;
21937 errdefer if (!done) wip_ty.cancel(ip, pt.tid);
2202721938
22028 if (tag_ty.zigTypeTag(mod) != .Int) {21939 if (tag_ty.zigTypeTag(mod) != .Int) {
22029 return sema.fail(block, src, "Type.Enum.tag_type must be an integer type", .{});21940 return sema.fail(block, src, "Type.Enum.tag_type must be an integer type", .{});
...@@ -22041,12 +21952,16 @@ fn reifyEnum(...@@ -22041,12 +21952,16 @@ fn reifyEnum(
22041 .parent = block.namespace.toOptional(),21952 .parent = block.namespace.toOptional(),
22042 .owner_type = wip_ty.index,21953 .owner_type = wip_ty.index,
22043 .file_scope = block.getFileScopeIndex(mod),21954 .file_scope = block.getFileScopeIndex(mod),
21955 .generation = mod.generation,
22044 });21956 });
2204521957
22046 const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index);21958 const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index);
2204721959
21960 try sema.declareDependency(.{ .interned = wip_ty.index });
21961 try sema.addTypeReferenceEntry(src, wip_ty.index);
22048 wip_ty.prepare(ip, new_cau_index, new_namespace_index);21962 wip_ty.prepare(ip, new_cau_index, new_namespace_index);
22049 wip_ty.setTagTy(ip, tag_ty.toIntern());21963 wip_ty.setTagTy(ip, tag_ty.toIntern());
21964 done = true;
2205021965
22051 for (0..fields_len) |field_idx| {21966 for (0..fields_len) |field_idx| {
22052 const field_info = try fields_val.elemValue(pt, field_idx);21967 const field_info = try fields_val.elemValue(pt, field_idx);
...@@ -22181,9 +22096,13 @@ fn reifyUnion(...@@ -22181,9 +22096,13 @@ fn reifyUnion(
22181 .zir_index = tracked_inst,22096 .zir_index = tracked_inst,
22182 .type_hash = hasher.final(),22097 .type_hash = hasher.final(),
22183 } },22098 } },
22184 })) {22099 }, false)) {
22185 .wip => |wip| wip,22100 .wip => |wip| wip,
22186 .existing => |ty| return Air.internedToRef(ty),22101 .existing => |ty| {
22102 try sema.declareDependency(.{ .interned = ty });
22103 try sema.addTypeReferenceEntry(src, ty);
22104 return Air.internedToRef(ty);
22105 },
22187 };22106 };
22188 errdefer wip_ty.cancel(ip, pt.tid);22107 errdefer wip_ty.cancel(ip, pt.tid);
2218922108
...@@ -22338,6 +22257,7 @@ fn reifyUnion(...@@ -22338,6 +22257,7 @@ fn reifyUnion(
22338 .parent = block.namespace.toOptional(),22257 .parent = block.namespace.toOptional(),
22339 .owner_type = wip_ty.index,22258 .owner_type = wip_ty.index,
22340 .file_scope = block.getFileScopeIndex(mod),22259 .file_scope = block.getFileScopeIndex(mod),
22260 .generation = mod.generation,
22341 });22261 });
2234222262
22343 const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index);22263 const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index);
...@@ -22348,7 +22268,8 @@ fn reifyUnion(...@@ -22348,7 +22268,8 @@ fn reifyUnion(
22348 if (block.ownerModule().strip) break :codegen_type;22268 if (block.ownerModule().strip) break :codegen_type;
22349 try mod.comp.queueJob(.{ .codegen_type = wip_ty.index });22269 try mod.comp.queueJob(.{ .codegen_type = wip_ty.index });
22350 }22270 }
22351 try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .cau = new_cau_index }));22271 try sema.declareDependency(.{ .interned = wip_ty.index });
22272 try sema.addTypeReferenceEntry(src, wip_ty.index);
22352 return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index));22273 return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index));
22353}22274}
2235422275
...@@ -22446,9 +22367,13 @@ fn reifyStruct(...@@ -22446,9 +22367,13 @@ fn reifyStruct(
22446 .zir_index = tracked_inst,22367 .zir_index = tracked_inst,
22447 .type_hash = hasher.final(),22368 .type_hash = hasher.final(),
22448 } },22369 } },
22449 })) {22370 }, false)) {
22450 .wip => |wip| wip,22371 .wip => |wip| wip,
22451 .existing => |ty| return Air.internedToRef(ty),22372 .existing => |ty| {
22373 try sema.declareDependency(.{ .interned = ty });
22374 try sema.addTypeReferenceEntry(src, ty);
22375 return Air.internedToRef(ty);
22376 },
22452 };22377 };
22453 errdefer wip_ty.cancel(ip, pt.tid);22378 errdefer wip_ty.cancel(ip, pt.tid);
2245422379
...@@ -22616,6 +22541,7 @@ fn reifyStruct(...@@ -22616,6 +22541,7 @@ fn reifyStruct(
22616 .parent = block.namespace.toOptional(),22541 .parent = block.namespace.toOptional(),
22617 .owner_type = wip_ty.index,22542 .owner_type = wip_ty.index,
22618 .file_scope = block.getFileScopeIndex(mod),22543 .file_scope = block.getFileScopeIndex(mod),
22544 .generation = mod.generation,
22619 });22545 });
2262022546
22621 const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index);22547 const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index);
...@@ -22626,7 +22552,8 @@ fn reifyStruct(...@@ -22626,7 +22552,8 @@ fn reifyStruct(
22626 if (block.ownerModule().strip) break :codegen_type;22552 if (block.ownerModule().strip) break :codegen_type;
22627 try mod.comp.queueJob(.{ .codegen_type = wip_ty.index });22553 try mod.comp.queueJob(.{ .codegen_type = wip_ty.index });
22628 }22554 }
22629 try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .cau = new_cau_index }));22555 try sema.declareDependency(.{ .interned = wip_ty.index });
22556 try sema.addTypeReferenceEntry(src, wip_ty.index);
22630 return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index));22557 return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index));
22631}22558}
2263222559
...@@ -26125,7 +26052,7 @@ fn zirVarExtended(...@@ -26125,7 +26052,7 @@ fn zirVarExtended(
26125 const addrspace_src = block.src(.{ .node_offset_var_decl_addrspace = 0 });26052 const addrspace_src = block.src(.{ .node_offset_var_decl_addrspace = 0 });
2612626053
26127 const decl_inst, const decl_bodies = decl: {26054 const decl_inst, const decl_bodies = decl: {
26128 const decl_inst = sema.getOwnerCauDeclInst().resolve(ip);26055 const decl_inst = sema.getOwnerCauDeclInst().resolve(ip) orelse return error.AnalysisFail;
26129 const zir_decl, const extra_end = sema.code.getDeclaration(decl_inst);26056 const zir_decl, const extra_end = sema.code.getDeclaration(decl_inst);
26130 break :decl .{ decl_inst, zir_decl.getBodies(extra_end, sema.code) };26057 break :decl .{ decl_inst, zir_decl.getBodies(extra_end, sema.code) };
26131 };26058 };
...@@ -26354,7 +26281,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A...@@ -26354,7 +26281,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
26354 break :decl_inst cau.zir_index;26281 break :decl_inst cau.zir_index;
26355 } else sema.getOwnerCauDeclInst(); // not an instantiation so we're analyzing a function declaration Cau26282 } else sema.getOwnerCauDeclInst(); // not an instantiation so we're analyzing a function declaration Cau
2635626283
26357 const zir_decl = sema.code.getDeclaration(decl_inst.resolve(&mod.intern_pool))[0];26284 const zir_decl = sema.code.getDeclaration(decl_inst.resolve(&mod.intern_pool) orelse return error.AnalysisFail)[0];
26358 if (zir_decl.flags.is_export) {26285 if (zir_decl.flags.is_export) {
26359 break :cc .C;26286 break :cc .C;
26360 }26287 }
...@@ -27659,13 +27586,6 @@ fn fieldVal(...@@ -27659,13 +27586,6 @@ fn fieldVal(
27659 const val = (try sema.resolveDefinedValue(block, object_src, dereffed_type)).?;27586 const val = (try sema.resolveDefinedValue(block, object_src, dereffed_type)).?;
27660 const child_type = val.toType();27587 const child_type = val.toType();
2766127588
27662 if (child_type.typeDeclInst(mod)) |type_decl_inst| {
27663 try sema.declareDependency(.{ .namespace_name = .{
27664 .namespace = type_decl_inst,
27665 .name = field_name,
27666 } });
27667 }
27668
27669 switch (try child_type.zigTypeTagOrPoison(mod)) {27589 switch (try child_type.zigTypeTagOrPoison(mod)) {
27670 .ErrorSet => {27590 .ErrorSet => {
27671 switch (ip.indexToKey(child_type.toIntern())) {27591 switch (ip.indexToKey(child_type.toIntern())) {
...@@ -27897,13 +27817,6 @@ fn fieldPtr(...@@ -27897,13 +27817,6 @@ fn fieldPtr(
27897 const val = (sema.resolveDefinedValue(block, src, inner) catch unreachable).?;27817 const val = (sema.resolveDefinedValue(block, src, inner) catch unreachable).?;
27898 const child_type = val.toType();27818 const child_type = val.toType();
2789927819
27900 if (child_type.typeDeclInst(mod)) |type_decl_inst| {
27901 try sema.declareDependency(.{ .namespace_name = .{
27902 .namespace = type_decl_inst,
27903 .name = field_name,
27904 } });
27905 }
27906
27907 switch (child_type.zigTypeTag(mod)) {27820 switch (child_type.zigTypeTag(mod)) {
27908 .ErrorSet => {27821 .ErrorSet => {
27909 switch (ip.indexToKey(child_type.toIntern())) {27822 switch (ip.indexToKey(child_type.toIntern())) {
...@@ -32223,7 +32136,7 @@ fn addReferenceEntry(...@@ -32223,7 +32136,7 @@ fn addReferenceEntry(
32223 referenced_unit: AnalUnit,32136 referenced_unit: AnalUnit,
32224) !void {32137) !void {
32225 const zcu = sema.pt.zcu;32138 const zcu = sema.pt.zcu;
32226 if (zcu.comp.reference_trace == 0) return;32139 if (!zcu.comp.incremental and zcu.comp.reference_trace == 0) return;
32227 const gop = try sema.references.getOrPut(sema.gpa, referenced_unit);32140 const gop = try sema.references.getOrPut(sema.gpa, referenced_unit);
32228 if (gop.found_existing) return;32141 if (gop.found_existing) return;
32229 // TODO: we need to figure out how to model inline calls here.32142 // TODO: we need to figure out how to model inline calls here.
...@@ -32232,6 +32145,18 @@ fn addReferenceEntry(...@@ -32232,6 +32145,18 @@ fn addReferenceEntry(
32232 try zcu.addUnitReference(sema.owner, referenced_unit, src);32145 try zcu.addUnitReference(sema.owner, referenced_unit, src);
32233}32146}
3223432147
32148fn addTypeReferenceEntry(
32149 sema: *Sema,
32150 src: LazySrcLoc,
32151 referenced_type: InternPool.Index,
32152) !void {
32153 const zcu = sema.pt.zcu;
32154 if (!zcu.comp.incremental and zcu.comp.reference_trace == 0) return;
32155 const gop = try sema.type_references.getOrPut(sema.gpa, referenced_type);
32156 if (gop.found_existing) return;
32157 try zcu.addTypeReference(sema.owner, referenced_type, src);
32158}
32159
32235pub fn ensureNavResolved(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav.Index) CompileError!void {32160pub fn ensureNavResolved(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav.Index) CompileError!void {
32236 const pt = sema.pt;32161 const pt = sema.pt;
32237 const zcu = pt.zcu;32162 const zcu = pt.zcu;
...@@ -35323,7 +35248,7 @@ pub fn resolveStructLayout(sema: *Sema, ty: Type) SemaError!void {...@@ -35323,7 +35248,7 @@ pub fn resolveStructLayout(sema: *Sema, ty: Type) SemaError!void {
35323 if (struct_type.haveLayout(ip))35248 if (struct_type.haveLayout(ip))
35324 return;35249 return;
3532535250
35326 try ty.resolveFields(pt);35251 try sema.resolveTypeFieldsStruct(ty.toIntern(), struct_type);
3532735252
35328 if (struct_type.layout == .@"packed") {35253 if (struct_type.layout == .@"packed") {
35329 semaBackingIntType(pt, struct_type) catch |err| switch (err) {35254 semaBackingIntType(pt, struct_type) catch |err| switch (err) {
...@@ -35505,7 +35430,7 @@ fn semaBackingIntType(pt: Zcu.PerThread, struct_type: InternPool.LoadedStructTyp...@@ -35505,7 +35430,7 @@ fn semaBackingIntType(pt: Zcu.PerThread, struct_type: InternPool.LoadedStructTyp
35505 break :blk accumulator;35430 break :blk accumulator;
35506 };35431 };
3550735432
35508 const zir_index = struct_type.zir_index.unwrap().?.resolve(ip);35433 const zir_index = struct_type.zir_index.unwrap().?.resolve(ip) orelse return error.AnalysisFail;
35509 const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;35434 const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;
35510 assert(extended.opcode == .struct_decl);35435 assert(extended.opcode == .struct_decl);
35511 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);35436 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
...@@ -36120,7 +36045,7 @@ fn semaStructFields(...@@ -36120,7 +36045,7 @@ fn semaStructFields(
36120 const cau_index = struct_type.cau.unwrap().?;36045 const cau_index = struct_type.cau.unwrap().?;
36121 const namespace_index = ip.getCau(cau_index).namespace;36046 const namespace_index = ip.getCau(cau_index).namespace;
36122 const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir;36047 const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir;
36123 const zir_index = struct_type.zir_index.unwrap().?.resolve(ip);36048 const zir_index = struct_type.zir_index.unwrap().?.resolve(ip) orelse return error.AnalysisFail;
3612436049
36125 const fields_len, const small, var extra_index = structZirInfo(zir, zir_index);36050 const fields_len, const small, var extra_index = structZirInfo(zir, zir_index);
3612636051
...@@ -36343,7 +36268,7 @@ fn semaStructFieldInits(...@@ -36343,7 +36268,7 @@ fn semaStructFieldInits(
36343 const cau_index = struct_type.cau.unwrap().?;36268 const cau_index = struct_type.cau.unwrap().?;
36344 const namespace_index = ip.getCau(cau_index).namespace;36269 const namespace_index = ip.getCau(cau_index).namespace;
36345 const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir;36270 const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir;
36346 const zir_index = struct_type.zir_index.unwrap().?.resolve(ip);36271 const zir_index = struct_type.zir_index.unwrap().?.resolve(ip) orelse return error.AnalysisFail;
36347 const fields_len, const small, var extra_index = structZirInfo(zir, zir_index);36272 const fields_len, const small, var extra_index = structZirInfo(zir, zir_index);
3634836273
36349 var comptime_err_ret_trace = std.ArrayList(LazySrcLoc).init(gpa);36274 var comptime_err_ret_trace = std.ArrayList(LazySrcLoc).init(gpa);
...@@ -36477,7 +36402,7 @@ fn semaUnionFields(pt: Zcu.PerThread, arena: Allocator, union_ty: InternPool.Ind...@@ -36477,7 +36402,7 @@ fn semaUnionFields(pt: Zcu.PerThread, arena: Allocator, union_ty: InternPool.Ind
36477 const ip = &zcu.intern_pool;36402 const ip = &zcu.intern_pool;
36478 const cau_index = union_type.cau;36403 const cau_index = union_type.cau;
36479 const zir = zcu.namespacePtr(union_type.namespace).fileScope(zcu).zir;36404 const zir = zcu.namespacePtr(union_type.namespace).fileScope(zcu).zir;
36480 const zir_index = union_type.zir_index.resolve(ip);36405 const zir_index = union_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
36481 const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;36406 const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;
36482 assert(extended.opcode == .union_decl);36407 assert(extended.opcode == .union_decl);
36483 const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small);36408 const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small);
...@@ -36591,11 +36516,11 @@ fn semaUnionFields(pt: Zcu.PerThread, arena: Allocator, union_ty: InternPool.Ind...@@ -36591,11 +36516,11 @@ fn semaUnionFields(pt: Zcu.PerThread, arena: Allocator, union_ty: InternPool.Ind
36591 }36516 }
36592 } else {36517 } else {
36593 // The provided type is the enum tag type.36518 // The provided type is the enum tag type.
36594 union_type.setTagType(ip, provided_ty.toIntern());
36595 const enum_type = switch (ip.indexToKey(provided_ty.toIntern())) {36519 const enum_type = switch (ip.indexToKey(provided_ty.toIntern())) {
36596 .enum_type => ip.loadEnumType(provided_ty.toIntern()),36520 .enum_type => ip.loadEnumType(provided_ty.toIntern()),
36597 else => return sema.fail(&block_scope, tag_ty_src, "expected enum tag type, found '{}'", .{provided_ty.fmt(pt)}),36521 else => return sema.fail(&block_scope, tag_ty_src, "expected enum tag type, found '{}'", .{provided_ty.fmt(pt)}),
36598 };36522 };
36523 union_type.setTagType(ip, provided_ty.toIntern());
36599 // The fields of the union must match the enum exactly.36524 // The fields of the union must match the enum exactly.
36600 // A flag per field is used to check for missing and extraneous fields.36525 // A flag per field is used to check for missing and extraneous fields.
36601 explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len);36526 explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len);
...@@ -38223,6 +38148,9 @@ pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void {...@@ -38223,6 +38148,9 @@ pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void {
38223 const zcu = sema.pt.zcu;38148 const zcu = sema.pt.zcu;
38224 if (!zcu.comp.incremental) return;38149 if (!zcu.comp.incremental) return;
3822538150
38151 const gop = try sema.dependencies.getOrPut(sema.gpa, dependee);
38152 if (gop.found_existing) return;
38153
38226 // Avoid creating dependencies on ourselves. This situation can arise when we analyze the fields38154 // Avoid creating dependencies on ourselves. This situation can arise when we analyze the fields
38227 // of a type and they use `@This()`. This dependency would be unnecessary, and in fact would38155 // of a type and they use `@This()`. This dependency would be unnecessary, and in fact would
38228 // just result in over-analysis since `Zcu.findOutdatedToAnalyze` would never be able to resolve38156 // just result in over-analysis since `Zcu.findOutdatedToAnalyze` would never be able to resolve
...@@ -38446,6 +38374,187 @@ fn getOwnerFuncDeclInst(sema: *Sema) InternPool.TrackedInst.Index {...@@ -38446,6 +38374,187 @@ fn getOwnerFuncDeclInst(sema: *Sema) InternPool.TrackedInst.Index {
38446 return ip.getCau(cau).zir_index;38374 return ip.getCau(cau).zir_index;
38447}38375}
3844838376
38377/// Called as soon as a `declared` enum type is created.
38378/// Resolves the tag type and field inits.
38379/// Marks the `src_inst` dependency on the enum's declaration, so call sites need not do this.
38380pub fn resolveDeclaredEnum(
38381 pt: Zcu.PerThread,
38382 wip_ty: InternPool.WipEnumType,
38383 inst: Zir.Inst.Index,
38384 tracked_inst: InternPool.TrackedInst.Index,
38385 namespace: InternPool.NamespaceIndex,
38386 type_name: InternPool.NullTerminatedString,
38387 enum_cau: InternPool.Cau.Index,
38388 small: Zir.Inst.EnumDecl.Small,
38389 body: []const Zir.Inst.Index,
38390 tag_type_ref: Zir.Inst.Ref,
38391 any_values: bool,
38392 fields_len: u32,
38393 zir: Zir,
38394 body_end: usize,
38395) Zcu.CompileError!void {
38396 const zcu = pt.zcu;
38397 const gpa = zcu.gpa;
38398 const ip = &zcu.intern_pool;
38399
38400 const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable;
38401
38402 const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(0) };
38403 const tag_ty_src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = .{ .node_offset_container_tag = 0 } };
38404
38405 const anal_unit = AnalUnit.wrap(.{ .cau = enum_cau });
38406
38407 var arena = std.heap.ArenaAllocator.init(gpa);
38408 defer arena.deinit();
38409
38410 var comptime_err_ret_trace = std.ArrayList(Zcu.LazySrcLoc).init(gpa);
38411 defer comptime_err_ret_trace.deinit();
38412
38413 var sema: Sema = .{
38414 .pt = pt,
38415 .gpa = gpa,
38416 .arena = arena.allocator(),
38417 .code = zir,
38418 .owner = anal_unit,
38419 .func_index = .none,
38420 .func_is_naked = false,
38421 .fn_ret_ty = Type.void,
38422 .fn_ret_ty_ies = null,
38423 .comptime_err_ret_trace = &comptime_err_ret_trace,
38424 };
38425 defer sema.deinit();
38426
38427 try sema.declareDependency(.{ .src_hash = tracked_inst });
38428
38429 var block: Block = .{
38430 .parent = null,
38431 .sema = &sema,
38432 .namespace = namespace,
38433 .instructions = .{},
38434 .inlining = null,
38435 .is_comptime = true,
38436 .src_base_inst = tracked_inst,
38437 .type_name_ctx = type_name,
38438 };
38439 defer block.instructions.deinit(gpa);
38440
38441 const int_tag_ty = ty: {
38442 if (body.len != 0) {
38443 _ = try sema.analyzeInlineBody(&block, body, inst);
38444 }
38445
38446 if (tag_type_ref != .none) {
38447 const ty = try sema.resolveType(&block, tag_ty_src, tag_type_ref);
38448 if (ty.zigTypeTag(zcu) != .Int and ty.zigTypeTag(zcu) != .ComptimeInt) {
38449 return sema.fail(&block, tag_ty_src, "expected integer tag type, found '{}'", .{ty.fmt(pt)});
38450 }
38451 break :ty ty;
38452 } else if (fields_len == 0) {
38453 break :ty try pt.intType(.unsigned, 0);
38454 } else {
38455 const bits = std.math.log2_int_ceil(usize, fields_len);
38456 break :ty try pt.intType(.unsigned, bits);
38457 }
38458 };
38459
38460 wip_ty.setTagTy(ip, int_tag_ty.toIntern());
38461
38462 if (small.nonexhaustive and int_tag_ty.toIntern() != .comptime_int_type) {
38463 if (fields_len > 1 and std.math.log2_int(u64, fields_len) == int_tag_ty.bitSize(pt)) {
38464 return sema.fail(&block, src, "non-exhaustive enum specifies every value", .{});
38465 }
38466 }
38467
38468 var extra_index = body_end + bit_bags_count;
38469 var bit_bag_index: usize = body_end;
38470 var cur_bit_bag: u32 = undefined;
38471 var last_tag_val: ?Value = null;
38472 for (0..fields_len) |field_i_usize| {
38473 const field_i: u32 = @intCast(field_i_usize);
38474 if (field_i % 32 == 0) {
38475 cur_bit_bag = zir.extra[bit_bag_index];
38476 bit_bag_index += 1;
38477 }
38478 const has_tag_value = @as(u1, @truncate(cur_bit_bag)) != 0;
38479 cur_bit_bag >>= 1;
38480
38481 const field_name_index: Zir.NullTerminatedString = @enumFromInt(zir.extra[extra_index]);
38482 const field_name_zir = zir.nullTerminatedString(field_name_index);
38483 extra_index += 2; // field name, doc comment
38484
38485 const field_name = try ip.getOrPutString(gpa, pt.tid, field_name_zir, .no_embedded_nulls);
38486
38487 const value_src: LazySrcLoc = .{
38488 .base_node_inst = tracked_inst,
38489 .offset = .{ .container_field_value = field_i },
38490 };
38491
38492 const tag_overflow = if (has_tag_value) overflow: {
38493 const tag_val_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
38494 extra_index += 1;
38495 const tag_inst = try sema.resolveInst(tag_val_ref);
38496 last_tag_val = try sema.resolveConstDefinedValue(&block, .{
38497 .base_node_inst = tracked_inst,
38498 .offset = .{ .container_field_name = field_i },
38499 }, tag_inst, .{
38500 .needed_comptime_reason = "enum tag value must be comptime-known",
38501 });
38502 if (!(try sema.intFitsInType(last_tag_val.?, int_tag_ty, null))) break :overflow true;
38503 last_tag_val = try pt.getCoerced(last_tag_val.?, int_tag_ty);
38504 if (wip_ty.nextField(ip, field_name, last_tag_val.?.toIntern())) |conflict| {
38505 assert(conflict.kind == .value); // AstGen validated names are unique
38506 const other_field_src: LazySrcLoc = .{
38507 .base_node_inst = tracked_inst,
38508 .offset = .{ .container_field_value = conflict.prev_field_idx },
38509 };
38510 const msg = msg: {
38511 const msg = try sema.errMsg(value_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValueSema(pt, &sema)});
38512 errdefer msg.destroy(gpa);
38513 try sema.errNote(other_field_src, msg, "other occurrence here", .{});
38514 break :msg msg;
38515 };
38516 return sema.failWithOwnedErrorMsg(&block, msg);
38517 }
38518 break :overflow false;
38519 } else if (any_values) overflow: {
38520 var overflow: ?usize = null;
38521 last_tag_val = if (last_tag_val) |val|
38522 try sema.intAdd(val, try pt.intValue(int_tag_ty, 1), int_tag_ty, &overflow)
38523 else
38524 try pt.intValue(int_tag_ty, 0);
38525 if (overflow != null) break :overflow true;
38526 if (wip_ty.nextField(ip, field_name, last_tag_val.?.toIntern())) |conflict| {
38527 assert(conflict.kind == .value); // AstGen validated names are unique
38528 const other_field_src: LazySrcLoc = .{
38529 .base_node_inst = tracked_inst,
38530 .offset = .{ .container_field_value = conflict.prev_field_idx },
38531 };
38532 const msg = msg: {
38533 const msg = try sema.errMsg(value_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValueSema(pt, &sema)});
38534 errdefer msg.destroy(gpa);
38535 try sema.errNote(other_field_src, msg, "other occurrence here", .{});
38536 break :msg msg;
38537 };
38538 return sema.failWithOwnedErrorMsg(&block, msg);
38539 }
38540 break :overflow false;
38541 } else overflow: {
38542 assert(wip_ty.nextField(ip, field_name, .none) == null);
38543 last_tag_val = try pt.intValue(Type.comptime_int, field_i);
38544 if (!try sema.intFitsInType(last_tag_val.?, int_tag_ty, null)) break :overflow true;
38545 last_tag_val = try pt.getCoerced(last_tag_val.?, int_tag_ty);
38546 break :overflow false;
38547 };
38548
38549 if (tag_overflow) {
38550 const msg = try sema.errMsg(value_src, "enumeration value '{}' too large for type '{}'", .{
38551 last_tag_val.?.fmtValueSema(pt, &sema), int_tag_ty.fmt(pt),
38552 });
38553 return sema.failWithOwnedErrorMsg(&block, msg);
38554 }
38555 }
38556}
38557
38449pub const bitCastVal = @import("Sema/bitcast.zig").bitCast;38558pub const bitCastVal = @import("Sema/bitcast.zig").bitCast;
38450pub const bitCastSpliceVal = @import("Sema/bitcast.zig").bitCastSplice;38559pub const bitCastSpliceVal = @import("Sema/bitcast.zig").bitCastSplice;
3845138560
src/Type.zig+1-1
...@@ -3437,7 +3437,7 @@ pub fn typeDeclSrcLine(ty: Type, zcu: *Zcu) ?u32 {...@@ -3437,7 +3437,7 @@ pub fn typeDeclSrcLine(ty: Type, zcu: *Zcu) ?u32 {
3437 },3437 },
3438 else => return null,3438 else => return null,
3439 };3439 };
3440 const info = tracked.resolveFull(&zcu.intern_pool);3440 const info = tracked.resolveFull(&zcu.intern_pool) orelse return null;
3441 const file = zcu.fileByIndex(info.file);3441 const file = zcu.fileByIndex(info.file);
3442 assert(file.zir_loaded);3442 assert(file.zir_loaded);
3443 const zir = file.zir;3443 const zir = file.zir;
src/Zcu.zig+485-135
...@@ -10,7 +10,7 @@ const builtin = @import("builtin");...@@ -10,7 +10,7 @@ const builtin = @import("builtin");
10const mem = std.mem;10const mem = std.mem;
11const Allocator = std.mem.Allocator;11const Allocator = std.mem.Allocator;
12const assert = std.debug.assert;12const assert = std.debug.assert;
13const log = std.log.scoped(.module);13const log = std.log.scoped(.zcu);
14const BigIntConst = std.math.big.int.Const;14const BigIntConst = std.math.big.int.Const;
15const BigIntMutable = std.math.big.int.Mutable;15const BigIntMutable = std.math.big.int.Mutable;
16const Target = std.Target;16const Target = std.Target;
...@@ -153,27 +153,27 @@ cimport_errors: std.AutoArrayHashMapUnmanaged(AnalUnit, std.zig.ErrorBundle) = ....@@ -153,27 +153,27 @@ cimport_errors: std.AutoArrayHashMapUnmanaged(AnalUnit, std.zig.ErrorBundle) = .
153/// Maximum amount of distinct error values, set by --error-limit153/// Maximum amount of distinct error values, set by --error-limit
154error_limit: ErrorInt,154error_limit: ErrorInt,
155155
156/// Value is the number of PO or outdated Decls which this AnalUnit depends on.156/// Value is the number of PO dependencies of this AnalUnit.
157/// This value will decrease as we perform semantic analysis to learn what is outdated.
158/// If any of these PO deps is outdated, this value will be moved to `outdated`.
157potentially_outdated: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .{},159potentially_outdated: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .{},
158/// Value is the number of PO or outdated Decls which this AnalUnit depends on.160/// Value is the number of PO dependencies of this AnalUnit.
159/// Once this value drops to 0, the AnalUnit is a candidate for re-analysis.161/// Once this value drops to 0, the AnalUnit is a candidate for re-analysis.
160outdated: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .{},162outdated: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .{},
161/// This contains all `AnalUnit`s in `outdated` whose PO dependency count is 0.163/// This contains all `AnalUnit`s in `outdated` whose PO dependency count is 0.
162/// Such `AnalUnit`s are ready for immediate re-analysis.164/// Such `AnalUnit`s are ready for immediate re-analysis.
163/// See `findOutdatedToAnalyze` for details.165/// See `findOutdatedToAnalyze` for details.
164outdated_ready: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .{},166outdated_ready: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .{},
165/// This contains a set of struct types whose corresponding `Cau` may not be in
166/// `outdated`, but are the root types of files which have updated source and
167/// thus must be re-analyzed. If such a type is only in this set, the struct type
168/// index may be preserved (only the namespace might change). If its owned `Cau`
169/// is also outdated, the struct type index must be recreated.
170outdated_file_root: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{},
171/// This contains a list of AnalUnit whose analysis or codegen failed, but the167/// This contains a list of AnalUnit whose analysis or codegen failed, but the
172/// failure was something like running out of disk space, and trying again may168/// failure was something like running out of disk space, and trying again may
173/// succeed. On the next update, we will flush this list, marking all members of169/// succeed. On the next update, we will flush this list, marking all members of
174/// it as outdated.170/// it as outdated.
175retryable_failures: std.ArrayListUnmanaged(AnalUnit) = .{},171retryable_failures: std.ArrayListUnmanaged(AnalUnit) = .{},
176172
173/// These are the modules which we initially queue for analysis in `Compilation.update`.
174/// `resolveReferences` will use these as the root of its reachability traversal.
175analysis_roots: std.BoundedArray(*Package.Module, 3) = .{},
176
177stage1_flags: packed struct {177stage1_flags: packed struct {
178 have_winmain: bool = false,178 have_winmain: bool = false,
179 have_wwinmain: bool = false,179 have_wwinmain: bool = false,
...@@ -192,7 +192,7 @@ global_assembly: std.AutoArrayHashMapUnmanaged(InternPool.Cau.Index, []u8) = .{}...@@ -192,7 +192,7 @@ global_assembly: std.AutoArrayHashMapUnmanaged(InternPool.Cau.Index, []u8) = .{}
192192
193/// Key is the `AnalUnit` *performing* the reference. This representation allows193/// Key is the `AnalUnit` *performing* the reference. This representation allows
194/// incremental updates to quickly delete references caused by a specific `AnalUnit`.194/// incremental updates to quickly delete references caused by a specific `AnalUnit`.
195/// Value is index into `all_reference` of the first reference triggered by the unit.195/// Value is index into `all_references` of the first reference triggered by the unit.
196/// The `next` field on the `Reference` forms a linked list of all references196/// The `next` field on the `Reference` forms a linked list of all references
197/// triggered by the key `AnalUnit`.197/// triggered by the key `AnalUnit`.
198reference_table: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .{},198reference_table: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .{},
...@@ -200,11 +200,23 @@ all_references: std.ArrayListUnmanaged(Reference) = .{},...@@ -200,11 +200,23 @@ all_references: std.ArrayListUnmanaged(Reference) = .{},
200/// Freelist of indices in `all_references`.200/// Freelist of indices in `all_references`.
201free_references: std.ArrayListUnmanaged(u32) = .{},201free_references: std.ArrayListUnmanaged(u32) = .{},
202202
203/// Key is the `AnalUnit` *performing* the reference. This representation allows
204/// incremental updates to quickly delete references caused by a specific `AnalUnit`.
205/// Value is index into `all_type_reference` of the first reference triggered by the unit.
206/// The `next` field on the `TypeReference` forms a linked list of all type references
207/// triggered by the key `AnalUnit`.
208type_reference_table: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .{},
209all_type_references: std.ArrayListUnmanaged(TypeReference) = .{},
210/// Freelist of indices in `all_type_references`.
211free_type_references: std.ArrayListUnmanaged(u32) = .{},
212
203panic_messages: [PanicId.len]InternPool.Nav.Index.Optional = .{.none} ** PanicId.len,213panic_messages: [PanicId.len]InternPool.Nav.Index.Optional = .{.none} ** PanicId.len,
204/// The panic function body.214/// The panic function body.
205panic_func_index: InternPool.Index = .none,215panic_func_index: InternPool.Index = .none,
206null_stack_trace: InternPool.Index = .none,216null_stack_trace: InternPool.Index = .none,
207217
218generation: u32 = 0,
219
208pub const PerThread = @import("Zcu/PerThread.zig");220pub const PerThread = @import("Zcu/PerThread.zig");
209221
210pub const PanicId = enum {222pub const PanicId = enum {
...@@ -308,10 +320,21 @@ pub const Reference = struct {...@@ -308,10 +320,21 @@ pub const Reference = struct {
308 src: LazySrcLoc,320 src: LazySrcLoc,
309};321};
310322
323pub const TypeReference = struct {
324 /// The container type which was referenced.
325 referenced: InternPool.Index,
326 /// Index into `all_type_references` of the next `TypeReference` triggered by the same `AnalUnit`.
327 /// `std.math.maxInt(u32)` is the sentinel.
328 next: u32,
329 /// The source location of the reference.
330 src: LazySrcLoc,
331};
332
311/// The container that structs, enums, unions, and opaques have.333/// The container that structs, enums, unions, and opaques have.
312pub const Namespace = struct {334pub const Namespace = struct {
313 parent: OptionalIndex,335 parent: OptionalIndex,
314 file_scope: File.Index,336 file_scope: File.Index,
337 generation: u32,
315 /// Will be a struct, enum, union, or opaque.338 /// Will be a struct, enum, union, or opaque.
316 owner_type: InternPool.Index,339 owner_type: InternPool.Index,
317 /// Members of the namespace which are marked `pub`.340 /// Members of the namespace which are marked `pub`.
...@@ -2022,10 +2045,11 @@ pub const LazySrcLoc = struct {...@@ -2022,10 +2045,11 @@ pub const LazySrcLoc = struct {
2022 .offset = .unneeded,2045 .offset = .unneeded,
2023 };2046 };
20242047
2025 pub fn resolveBaseNode(base_node_inst: InternPool.TrackedInst.Index, zcu: *Zcu) struct { *File, Ast.Node.Index } {2048 /// Returns `null` if the ZIR instruction has been lost across incremental updates.
2049 pub fn resolveBaseNode(base_node_inst: InternPool.TrackedInst.Index, zcu: *Zcu) ?struct { *File, Ast.Node.Index } {
2026 const ip = &zcu.intern_pool;2050 const ip = &zcu.intern_pool;
2027 const file_index, const zir_inst = inst: {2051 const file_index, const zir_inst = inst: {
2028 const info = base_node_inst.resolveFull(ip);2052 const info = base_node_inst.resolveFull(ip) orelse return null;
2029 break :inst .{ info.file, info.inst };2053 break :inst .{ info.file, info.inst };
2030 };2054 };
2031 const file = zcu.fileByIndex(file_index);2055 const file = zcu.fileByIndex(file_index);
...@@ -2051,7 +2075,15 @@ pub const LazySrcLoc = struct {...@@ -2051,7 +2075,15 @@ pub const LazySrcLoc = struct {
2051 /// Resolve the file and AST node of `base_node_inst` to get a resolved `SrcLoc`.2075 /// Resolve the file and AST node of `base_node_inst` to get a resolved `SrcLoc`.
2052 /// The resulting `SrcLoc` should only be used ephemerally, as it is not correct across incremental updates.2076 /// The resulting `SrcLoc` should only be used ephemerally, as it is not correct across incremental updates.
2053 pub fn upgrade(lazy: LazySrcLoc, zcu: *Zcu) SrcLoc {2077 pub fn upgrade(lazy: LazySrcLoc, zcu: *Zcu) SrcLoc {
2054 const file, const base_node = resolveBaseNode(lazy.base_node_inst, zcu);2078 return lazy.upgradeOrLost(zcu).?;
2079 }
2080
2081 /// Like `upgrade`, but returns `null` if the source location has been lost across incremental updates.
2082 pub fn upgradeOrLost(lazy: LazySrcLoc, zcu: *Zcu) ?SrcLoc {
2083 const file, const base_node: Ast.Node.Index = if (lazy.offset == .entire_file) .{
2084 zcu.fileByIndex(lazy.base_node_inst.resolveFile(&zcu.intern_pool)),
2085 0,
2086 } else resolveBaseNode(lazy.base_node_inst, zcu) orelse return null;
2055 return .{2087 return .{
2056 .file_scope = file,2088 .file_scope = file,
2057 .base_node = base_node,2089 .base_node = base_node,
...@@ -2148,7 +2180,6 @@ pub fn deinit(zcu: *Zcu) void {...@@ -2148,7 +2180,6 @@ pub fn deinit(zcu: *Zcu) void {
2148 zcu.potentially_outdated.deinit(gpa);2180 zcu.potentially_outdated.deinit(gpa);
2149 zcu.outdated.deinit(gpa);2181 zcu.outdated.deinit(gpa);
2150 zcu.outdated_ready.deinit(gpa);2182 zcu.outdated_ready.deinit(gpa);
2151 zcu.outdated_file_root.deinit(gpa);
2152 zcu.retryable_failures.deinit(gpa);2183 zcu.retryable_failures.deinit(gpa);
21532184
2154 zcu.test_functions.deinit(gpa);2185 zcu.test_functions.deinit(gpa);
...@@ -2162,6 +2193,10 @@ pub fn deinit(zcu: *Zcu) void {...@@ -2162,6 +2193,10 @@ pub fn deinit(zcu: *Zcu) void {
2162 zcu.all_references.deinit(gpa);2193 zcu.all_references.deinit(gpa);
2163 zcu.free_references.deinit(gpa);2194 zcu.free_references.deinit(gpa);
21642195
2196 zcu.type_reference_table.deinit(gpa);
2197 zcu.all_type_references.deinit(gpa);
2198 zcu.free_type_references.deinit(gpa);
2199
2165 zcu.intern_pool.deinit(gpa);2200 zcu.intern_pool.deinit(gpa);
2166}2201}
21672202
...@@ -2255,55 +2290,89 @@ pub fn loadZirCacheBody(gpa: Allocator, header: Zir.Header, cache_file: std.fs.F...@@ -2255,55 +2290,89 @@ pub fn loadZirCacheBody(gpa: Allocator, header: Zir.Header, cache_file: std.fs.F
2255 return zir;2290 return zir;
2256}2291}
22572292
2258pub fn markDependeeOutdated(zcu: *Zcu, dependee: InternPool.Dependee) !void {2293pub fn markDependeeOutdated(
2259 log.debug("outdated dependee: {}", .{dependee});2294 zcu: *Zcu,
2295 /// When we are diffing ZIR and marking things as outdated, we won't yet have marked the dependencies as PO.
2296 /// However, when we discover during analysis that something was outdated, the `Dependee` was already
2297 /// marked as PO, so we need to decrement the PO dep count for each depender.
2298 marked_po: enum { not_marked_po, marked_po },
2299 dependee: InternPool.Dependee,
2300) !void {
2301 log.debug("outdated dependee: {}", .{zcu.fmtDependee(dependee)});
2260 var it = zcu.intern_pool.dependencyIterator(dependee);2302 var it = zcu.intern_pool.dependencyIterator(dependee);
2261 while (it.next()) |depender| {2303 while (it.next()) |depender| {
2262 if (zcu.outdated.contains(depender)) {2304 if (zcu.outdated.getPtr(depender)) |po_dep_count| {
2263 // We do not need to increment the PO dep count, as if the outdated2305 switch (marked_po) {
2264 // dependee is a Decl, we had already marked this as PO.2306 .not_marked_po => {},
2307 .marked_po => {
2308 po_dep_count.* -= 1;
2309 log.debug("outdated {} => already outdated {} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), po_dep_count.* });
2310 if (po_dep_count.* == 0) {
2311 log.debug("outdated ready: {}", .{zcu.fmtAnalUnit(depender)});
2312 try zcu.outdated_ready.put(zcu.gpa, depender, {});
2313 }
2314 },
2315 }
2265 continue;2316 continue;
2266 }2317 }
2267 const opt_po_entry = zcu.potentially_outdated.fetchSwapRemove(depender);2318 const opt_po_entry = zcu.potentially_outdated.fetchSwapRemove(depender);
2319 const new_po_dep_count = switch (marked_po) {
2320 .not_marked_po => if (opt_po_entry) |e| e.value else 0,
2321 .marked_po => if (opt_po_entry) |e| e.value - 1 else {
2322 // This `AnalUnit` has already been re-analyzed this update, and registered a dependency
2323 // on this thing, but already has sufficiently up-to-date information. Nothing to do.
2324 continue;
2325 },
2326 };
2268 try zcu.outdated.putNoClobber(2327 try zcu.outdated.putNoClobber(
2269 zcu.gpa,2328 zcu.gpa,
2270 depender,2329 depender,
2271 // We do not need to increment this count for the same reason as above.2330 new_po_dep_count,
2272 if (opt_po_entry) |e| e.value else 0,
2273 );2331 );
2274 log.debug("outdated: {}", .{depender});2332 log.debug("outdated {} => new outdated {} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), new_po_dep_count });
2275 if (opt_po_entry == null) {2333 if (new_po_dep_count == 0) {
2276 // This is a new entry with no PO dependencies.2334 log.debug("outdated ready: {}", .{zcu.fmtAnalUnit(depender)});
2277 try zcu.outdated_ready.put(zcu.gpa, depender, {});2335 try zcu.outdated_ready.put(zcu.gpa, depender, {});
2278 }2336 }
2279 // If this is a Decl and was not previously PO, we must recursively2337 // If this is a Decl and was not previously PO, we must recursively
2280 // mark dependencies on its tyval as PO.2338 // mark dependencies on its tyval as PO.
2281 if (opt_po_entry == null) {2339 if (opt_po_entry == null) {
2340 assert(marked_po == .not_marked_po);
2282 try zcu.markTransitiveDependersPotentiallyOutdated(depender);2341 try zcu.markTransitiveDependersPotentiallyOutdated(depender);
2283 }2342 }
2284 }2343 }
2285}2344}
22862345
2287pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {2346pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
2347 log.debug("up-to-date dependee: {}", .{zcu.fmtDependee(dependee)});
2288 var it = zcu.intern_pool.dependencyIterator(dependee);2348 var it = zcu.intern_pool.dependencyIterator(dependee);
2289 while (it.next()) |depender| {2349 while (it.next()) |depender| {
2290 if (zcu.outdated.getPtr(depender)) |po_dep_count| {2350 if (zcu.outdated.getPtr(depender)) |po_dep_count| {
2291 // This depender is already outdated, but it now has one2351 // This depender is already outdated, but it now has one
2292 // less PO dependency!2352 // less PO dependency!
2293 po_dep_count.* -= 1;2353 po_dep_count.* -= 1;
2354 log.debug("up-to-date {} => {} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), po_dep_count.* });
2294 if (po_dep_count.* == 0) {2355 if (po_dep_count.* == 0) {
2356 log.debug("outdated ready: {}", .{zcu.fmtAnalUnit(depender)});
2295 try zcu.outdated_ready.put(zcu.gpa, depender, {});2357 try zcu.outdated_ready.put(zcu.gpa, depender, {});
2296 }2358 }
2297 continue;2359 continue;
2298 }2360 }
2299 // This depender is definitely at least PO, because this Decl was just analyzed2361 // This depender is definitely at least PO, because this Decl was just analyzed
2300 // due to being outdated.2362 // due to being outdated.
2301 const ptr = zcu.potentially_outdated.getPtr(depender).?;2363 const ptr = zcu.potentially_outdated.getPtr(depender) orelse {
2364 // This dependency has been registered during in-progress analysis, but the unit is
2365 // not in `potentially_outdated` because analysis is in-progress. Nothing to do.
2366 continue;
2367 };
2302 if (ptr.* > 1) {2368 if (ptr.* > 1) {
2303 ptr.* -= 1;2369 ptr.* -= 1;
2370 log.debug("up-to-date {} => {} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), ptr.* });
2304 continue;2371 continue;
2305 }2372 }
23062373
2374 log.debug("up-to-date {} => {} po_deps=0 (up-to-date)", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender) });
2375
2307 // This dependency is no longer PO, i.e. is known to be up-to-date.2376 // This dependency is no longer PO, i.e. is known to be up-to-date.
2308 assert(zcu.potentially_outdated.swapRemove(depender));2377 assert(zcu.potentially_outdated.swapRemove(depender));
2309 // If this is a Decl, we must recursively mark dependencies on its tyval2378 // If this is a Decl, we must recursively mark dependencies on its tyval
...@@ -2323,14 +2392,16 @@ pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {...@@ -2323,14 +2392,16 @@ pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
2323/// in turn be PO, due to a dependency on the original AnalUnit's tyval or IES.2392/// in turn be PO, due to a dependency on the original AnalUnit's tyval or IES.
2324fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUnit) !void {2393fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUnit) !void {
2325 const ip = &zcu.intern_pool;2394 const ip = &zcu.intern_pool;
2326 var it = ip.dependencyIterator(switch (maybe_outdated.unwrap()) {2395 const dependee: InternPool.Dependee = switch (maybe_outdated.unwrap()) {
2327 .cau => |cau| switch (ip.getCau(cau).owner.unwrap()) {2396 .cau => |cau| switch (ip.getCau(cau).owner.unwrap()) {
2328 .nav => |nav| .{ .nav_val = nav }, // TODO: also `nav_ref` deps when introduced2397 .nav => |nav| .{ .nav_val = nav }, // TODO: also `nav_ref` deps when introduced
2329 .none, .type => return, // analysis of this `Cau` can't outdate any dependencies2398 .type => |ty| .{ .interned = ty },
2399 .none => return, // analysis of this `Cau` can't outdate any dependencies
2330 },2400 },
2331 .func => |func_index| .{ .interned = func_index }, // IES2401 .func => |func_index| .{ .interned = func_index }, // IES
2332 });2402 };
23332403 log.debug("potentially outdated dependee: {}", .{zcu.fmtDependee(dependee)});
2404 var it = ip.dependencyIterator(dependee);
2334 while (it.next()) |po| {2405 while (it.next()) |po| {
2335 if (zcu.outdated.getPtr(po)) |po_dep_count| {2406 if (zcu.outdated.getPtr(po)) |po_dep_count| {
2336 // This dependency is already outdated, but it now has one more PO2407 // This dependency is already outdated, but it now has one more PO
...@@ -2339,14 +2410,17 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni...@@ -2339,14 +2410,17 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni
2339 _ = zcu.outdated_ready.swapRemove(po);2410 _ = zcu.outdated_ready.swapRemove(po);
2340 }2411 }
2341 po_dep_count.* += 1;2412 po_dep_count.* += 1;
2413 log.debug("po {} => {} [outdated] po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po), po_dep_count.* });
2342 continue;2414 continue;
2343 }2415 }
2344 if (zcu.potentially_outdated.getPtr(po)) |n| {2416 if (zcu.potentially_outdated.getPtr(po)) |n| {
2345 // There is now one more PO dependency.2417 // There is now one more PO dependency.
2346 n.* += 1;2418 n.* += 1;
2419 log.debug("po {} => {} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po), n.* });
2347 continue;2420 continue;
2348 }2421 }
2349 try zcu.potentially_outdated.putNoClobber(zcu.gpa, po, 1);2422 try zcu.potentially_outdated.putNoClobber(zcu.gpa, po, 1);
2423 log.debug("po {} => {} po_deps=1", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po) });
2350 // This AnalUnit was not already PO, so we must recursively mark its dependers as also PO.2424 // This AnalUnit was not already PO, so we must recursively mark its dependers as also PO.
2351 try zcu.markTransitiveDependersPotentiallyOutdated(po);2425 try zcu.markTransitiveDependersPotentiallyOutdated(po);
2352 }2426 }
...@@ -2355,9 +2429,11 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni...@@ -2355,9 +2429,11 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni
2355pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {2429pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {
2356 if (!zcu.comp.incremental) return null;2430 if (!zcu.comp.incremental) return null;
23572431
2358 if (true) @panic("TODO: findOutdatedToAnalyze");2432 if (zcu.outdated.count() == 0) {
23592433 // Any units in `potentially_outdated` must just be stuck in loops with one another: none of those
2360 if (zcu.outdated.count() == 0 and zcu.potentially_outdated.count() == 0) {2434 // units have had any outdated dependencies so far, and all of their remaining PO deps are triggered
2435 // by other units in `potentially_outdated`. So, we can safety assume those units up-to-date.
2436 zcu.potentially_outdated.clearRetainingCapacity();
2361 log.debug("findOutdatedToAnalyze: no outdated depender", .{});2437 log.debug("findOutdatedToAnalyze: no outdated depender", .{});
2362 return null;2438 return null;
2363 }2439 }
...@@ -2372,96 +2448,75 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {...@@ -2372,96 +2448,75 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {
2372 // In this case, we must defer to more complex logic below.2448 // In this case, we must defer to more complex logic below.
23732449
2374 if (zcu.outdated_ready.count() > 0) {2450 if (zcu.outdated_ready.count() > 0) {
2375 log.debug("findOutdatedToAnalyze: trivial '{s} {d}'", .{2451 const unit = zcu.outdated_ready.keys()[0];
2376 @tagName(zcu.outdated_ready.keys()[0].unwrap()),2452 log.debug("findOutdatedToAnalyze: trivial {}", .{zcu.fmtAnalUnit(unit)});
2377 switch (zcu.outdated_ready.keys()[0].unwrap()) {2453 return unit;
2378 inline else => |x| @intFromEnum(x),
2379 },
2380 });
2381 return zcu.outdated_ready.keys()[0];
2382 }2454 }
23832455
2384 // Next, we will see if there is any outdated file root which was not in2456 // There is no single AnalUnit which is ready for re-analysis. Instead, we must assume that some
2385 // `outdated`. This set will be small (number of files changed in this2457 // Cau with PO dependencies is outdated -- e.g. in the above example we arbitrarily pick one of
2386 // update), so it's alright for us to just iterate here.2458 // A or B. We should select a Cau, since a Cau is definitely responsible for the loop in the
2387 for (zcu.outdated_file_root.keys()) |file_decl| {2459 // dependency graph (since IES dependencies can't have loops). We should also, of course, not
2388 const decl_depender = AnalUnit.wrap(.{ .decl = file_decl });2460 // select a Cau owned by a `comptime` declaration, since you can't depend on those!
2389 if (zcu.outdated.contains(decl_depender)) {
2390 // Since we didn't hit this in the first loop, this Decl must have
2391 // pending dependencies, so is ineligible.
2392 continue;
2393 }
2394 if (zcu.potentially_outdated.contains(decl_depender)) {
2395 // This Decl's struct may or may not need to be recreated depending
2396 // on whether it is outdated. If we analyzed it now, we would have
2397 // to assume it was outdated and recreate it!
2398 continue;
2399 }
2400 log.debug("findOutdatedToAnalyze: outdated file root decl '{d}'", .{file_decl});
2401 return decl_depender;
2402 }
2403
2404 // There is no single AnalUnit which is ready for re-analysis. Instead, we
2405 // must assume that some Decl with PO dependencies is outdated - e.g. in the
2406 // above example we arbitrarily pick one of A or B. We should select a Decl,
2407 // since a Decl is definitely responsible for the loop in the dependency
2408 // graph (since you can't depend on a runtime function analysis!).
24092461
2410 // The choice of this Decl could have a big impact on how much total2462 // The choice of this Cau could have a big impact on how much total analysis we perform, since
2411 // analysis we perform, since if analysis concludes its tyval is unchanged,2463 // if analysis concludes any dependencies on its result are up-to-date, then other PO AnalUnit
2412 // then other PO AnalUnit may be resolved as up-to-date. To hopefully avoid2464 // may be resolved as up-to-date. To hopefully avoid doing too much work, let's find a Decl
2413 // doing too much work, let's find a Decl which the most things depend on -2465 // which the most things depend on - the idea is that this will resolve a lot of loops (but this
2414 // the idea is that this will resolve a lot of loops (but this is only a2466 // is only a heuristic).
2415 // heuristic).
24162467
2417 log.debug("findOutdatedToAnalyze: no trivial ready, using heuristic; {d} outdated, {d} PO", .{2468 log.debug("findOutdatedToAnalyze: no trivial ready, using heuristic; {d} outdated, {d} PO", .{
2418 zcu.outdated.count(),2469 zcu.outdated.count(),
2419 zcu.potentially_outdated.count(),2470 zcu.potentially_outdated.count(),
2420 });2471 });
24212472
2422 const Decl = {};2473 const ip = &zcu.intern_pool;
24232474
2424 var chosen_decl_idx: ?Decl.Index = null;2475 var chosen_cau: ?InternPool.Cau.Index = null;
2425 var chosen_decl_dependers: u32 = undefined;2476 var chosen_cau_dependers: u32 = undefined;
24262477
2427 for (zcu.outdated.keys()) |depender| {2478 inline for (.{ zcu.outdated.keys(), zcu.potentially_outdated.keys() }) |outdated_units| {
2428 const decl_index = switch (depender.unwrap()) {2479 for (outdated_units) |unit| {
2429 .decl => |d| d,2480 const cau = switch (unit.unwrap()) {
2430 .func => continue,2481 .cau => |cau| cau,
2431 };2482 .func => continue, // a `func` definitely can't be causing the loop so it is a bad choice
2483 };
2484 const cau_owner = ip.getCau(cau).owner;
24322485
2433 var n: u32 = 0;2486 var n: u32 = 0;
2434 var it = zcu.intern_pool.dependencyIterator(.{ .decl_val = decl_index });2487 var it = ip.dependencyIterator(switch (cau_owner.unwrap()) {
2435 while (it.next()) |_| n += 1;2488 .none => continue, // there can be no dependencies on this `Cau` so it is a terrible choice
2489 .type => |ty| .{ .interned = ty },
2490 .nav => |nav| .{ .nav_val = nav },
2491 });
2492 while (it.next()) |_| n += 1;
24362493
2437 if (chosen_decl_idx == null or n > chosen_decl_dependers) {2494 if (chosen_cau == null or n > chosen_cau_dependers) {
2438 chosen_decl_idx = decl_index;2495 chosen_cau = cau;
2439 chosen_decl_dependers = n;2496 chosen_cau_dependers = n;
2497 }
2440 }2498 }
2441 }2499 }
24422500
2443 for (zcu.potentially_outdated.keys()) |depender| {2501 if (chosen_cau == null) {
2444 const decl_index = switch (depender.unwrap()) {2502 for (zcu.outdated.keys(), zcu.outdated.values()) |o, opod| {
2445 .decl => |d| d,2503 const func = o.unwrap().func;
2446 .func => continue,2504 const nav = zcu.funcInfo(func).owner_nav;
2447 };2505 std.io.getStdErr().writer().print("outdated: func {}, nav {}, name '{}', [p]o deps {}\n", .{ func, nav, ip.getNav(nav).fqn.fmt(ip), opod }) catch {};
24482506 }
2449 var n: u32 = 0;2507 for (zcu.potentially_outdated.keys(), zcu.potentially_outdated.values()) |o, opod| {
2450 var it = zcu.intern_pool.dependencyIterator(.{ .decl_val = decl_index });2508 const func = o.unwrap().func;
2451 while (it.next()) |_| n += 1;2509 const nav = zcu.funcInfo(func).owner_nav;
24522510 std.io.getStdErr().writer().print("po: func {}, nav {}, name '{}', [p]o deps {}\n", .{ func, nav, ip.getNav(nav).fqn.fmt(ip), opod }) catch {};
2453 if (chosen_decl_idx == null or n > chosen_decl_dependers) {
2454 chosen_decl_idx = decl_index;
2455 chosen_decl_dependers = n;
2456 }2511 }
2457 }2512 }
24582513
2459 log.debug("findOutdatedToAnalyze: heuristic returned Decl {d} ({d} dependers)", .{2514 log.debug("findOutdatedToAnalyze: heuristic returned '{}' ({d} dependers)", .{
2460 chosen_decl_idx.?,2515 zcu.fmtAnalUnit(AnalUnit.wrap(.{ .cau = chosen_cau.? })),
2461 chosen_decl_dependers,2516 chosen_cau_dependers,
2462 });2517 });
24632518
2464 return AnalUnit.wrap(.{ .decl = chosen_decl_idx.? });2519 return AnalUnit.wrap(.{ .cau = chosen_cau.? });
2465}2520}
24662521
2467/// During an incremental update, before semantic analysis, call this to flush all values from2522/// During an incremental update, before semantic analysis, call this to flush all values from
...@@ -2506,10 +2561,10 @@ pub fn mapOldZirToNew(...@@ -2506,10 +2561,10 @@ pub fn mapOldZirToNew(
2506 });2561 });
25072562
2508 // Used as temporary buffers for namespace declaration instructions2563 // Used as temporary buffers for namespace declaration instructions
2509 var old_decls = std.ArrayList(Zir.Inst.Index).init(gpa);2564 var old_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .{};
2510 defer old_decls.deinit();2565 defer old_decls.deinit(gpa);
2511 var new_decls = std.ArrayList(Zir.Inst.Index).init(gpa);2566 var new_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .{};
2512 defer new_decls.deinit();2567 defer new_decls.deinit(gpa);
25132568
2514 while (match_stack.popOrNull()) |match_item| {2569 while (match_stack.popOrNull()) |match_item| {
2515 // Match the namespace declaration itself2570 // Match the namespace declaration itself
...@@ -2583,7 +2638,7 @@ pub fn mapOldZirToNew(...@@ -2583,7 +2638,7 @@ pub fn mapOldZirToNew(
2583 break :inst unnamed_tests.items[unnamed_test_idx];2638 break :inst unnamed_tests.items[unnamed_test_idx];
2584 },2639 },
2585 _ => inst: {2640 _ => inst: {
2586 const name_nts = new_decl.name.toString(old_zir).?;2641 const name_nts = new_decl.name.toString(new_zir).?;
2587 const name = new_zir.nullTerminatedString(name_nts);2642 const name = new_zir.nullTerminatedString(name_nts);
2588 if (new_decl.name.isNamedTest(new_zir)) {2643 if (new_decl.name.isNamedTest(new_zir)) {
2589 break :inst named_tests.get(name) orelse continue;2644 break :inst named_tests.get(name) orelse continue;
...@@ -2596,11 +2651,11 @@ pub fn mapOldZirToNew(...@@ -2596,11 +2651,11 @@ pub fn mapOldZirToNew(
2596 // Match the `declaration` instruction2651 // Match the `declaration` instruction
2597 try inst_map.put(gpa, old_decl_inst, new_decl_inst);2652 try inst_map.put(gpa, old_decl_inst, new_decl_inst);
25982653
2599 // Find namespace declarations within this declaration2654 // Find container type declarations within this declaration
2600 try old_zir.findDecls(&old_decls, old_decl_inst);2655 try old_zir.findDecls(gpa, &old_decls, old_decl_inst);
2601 try new_zir.findDecls(&new_decls, new_decl_inst);2656 try new_zir.findDecls(gpa, &new_decls, new_decl_inst);
26022657
2603 // We don't have any smart way of matching up these namespace declarations, so we always2658 // We don't have any smart way of matching up these type declarations, so we always
2604 // correlate them based on source order.2659 // correlate them based on source order.
2605 const n = @min(old_decls.items.len, new_decls.items.len);2660 const n = @min(old_decls.items.len, new_decls.items.len);
2606 try match_stack.ensureUnusedCapacity(gpa, n);2661 try match_stack.ensureUnusedCapacity(gpa, n);
...@@ -2699,16 +2754,32 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void {...@@ -2699,16 +2754,32 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void {
2699pub fn deleteUnitReferences(zcu: *Zcu, anal_unit: AnalUnit) void {2754pub fn deleteUnitReferences(zcu: *Zcu, anal_unit: AnalUnit) void {
2700 const gpa = zcu.gpa;2755 const gpa = zcu.gpa;
27012756
2702 const kv = zcu.reference_table.fetchSwapRemove(anal_unit) orelse return;2757 unit_refs: {
2703 var idx = kv.value;2758 const kv = zcu.reference_table.fetchSwapRemove(anal_unit) orelse break :unit_refs;
2759 var idx = kv.value;
27042760
2705 while (idx != std.math.maxInt(u32)) {2761 while (idx != std.math.maxInt(u32)) {
2706 zcu.free_references.append(gpa, idx) catch {2762 zcu.free_references.append(gpa, idx) catch {
2707 // This space will be reused eventually, so we need not propagate this error.2763 // This space will be reused eventually, so we need not propagate this error.
2708 // Just leak it for now, and let GC reclaim it later on.2764 // Just leak it for now, and let GC reclaim it later on.
2709 return;2765 break :unit_refs;
2710 };2766 };
2711 idx = zcu.all_references.items[idx].next;2767 idx = zcu.all_references.items[idx].next;
2768 }
2769 }
2770
2771 type_refs: {
2772 const kv = zcu.type_reference_table.fetchSwapRemove(anal_unit) orelse break :type_refs;
2773 var idx = kv.value;
2774
2775 while (idx != std.math.maxInt(u32)) {
2776 zcu.free_type_references.append(gpa, idx) catch {
2777 // This space will be reused eventually, so we need not propagate this error.
2778 // Just leak it for now, and let GC reclaim it later on.
2779 break :type_refs;
2780 };
2781 idx = zcu.all_type_references.items[idx].next;
2782 }
2712 }2783 }
2713}2784}
27142785
...@@ -2735,6 +2806,29 @@ pub fn addUnitReference(zcu: *Zcu, src_unit: AnalUnit, referenced_unit: AnalUnit...@@ -2735,6 +2806,29 @@ pub fn addUnitReference(zcu: *Zcu, src_unit: AnalUnit, referenced_unit: AnalUnit
2735 gop.value_ptr.* = @intCast(ref_idx);2806 gop.value_ptr.* = @intCast(ref_idx);
2736}2807}
27372808
2809pub fn addTypeReference(zcu: *Zcu, src_unit: AnalUnit, referenced_type: InternPool.Index, ref_src: LazySrcLoc) Allocator.Error!void {
2810 const gpa = zcu.gpa;
2811
2812 try zcu.type_reference_table.ensureUnusedCapacity(gpa, 1);
2813
2814 const ref_idx = zcu.free_type_references.popOrNull() orelse idx: {
2815 _ = try zcu.all_type_references.addOne(gpa);
2816 break :idx zcu.all_type_references.items.len - 1;
2817 };
2818
2819 errdefer comptime unreachable;
2820
2821 const gop = zcu.type_reference_table.getOrPutAssumeCapacity(src_unit);
2822
2823 zcu.all_type_references.items[ref_idx] = .{
2824 .referenced = referenced_type,
2825 .next = if (gop.found_existing) gop.value_ptr.* else std.math.maxInt(u32),
2826 .src = ref_src,
2827 };
2828
2829 gop.value_ptr.* = @intCast(ref_idx);
2830}
2831
2738pub fn errorSetBits(mod: *Zcu) u16 {2832pub fn errorSetBits(mod: *Zcu) u16 {
2739 if (mod.error_limit == 0) return 0;2833 if (mod.error_limit == 0) return 0;
2740 return @as(u16, std.math.log2_int(ErrorInt, mod.error_limit)) + 1;2834 return @as(u16, std.math.log2_int(ErrorInt, mod.error_limit)) + 1;
...@@ -3029,28 +3123,215 @@ pub const ResolvedReference = struct {...@@ -3029,28 +3123,215 @@ pub const ResolvedReference = struct {
3029};3123};
30303124
3031/// Returns a mapping from an `AnalUnit` to where it is referenced.3125/// Returns a mapping from an `AnalUnit` to where it is referenced.
3032/// TODO: in future, this must be adapted to traverse from roots of analysis. That way, we can3126/// If the value is `null`, the `AnalUnit` is a root of analysis.
3033/// use the returned map to determine which units have become unreferenced in an incremental update.3127/// If an `AnalUnit` is not in the returned map, it is unreferenced.
3034pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ResolvedReference) {3128pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?ResolvedReference) {
3035 const gpa = zcu.gpa;3129 const gpa = zcu.gpa;
3130 const comp = zcu.comp;
3131 const ip = &zcu.intern_pool;
30363132
3037 var result: std.AutoHashMapUnmanaged(AnalUnit, ResolvedReference) = .{};3133 var result: std.AutoHashMapUnmanaged(AnalUnit, ?ResolvedReference) = .{};
3038 errdefer result.deinit(gpa);3134 errdefer result.deinit(gpa);
30393135
3136 var checked_types: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{};
3137 var type_queue: std.AutoArrayHashMapUnmanaged(InternPool.Index, ?ResolvedReference) = .{};
3138 var unit_queue: std.AutoArrayHashMapUnmanaged(AnalUnit, ?ResolvedReference) = .{};
3139 defer {
3140 checked_types.deinit(gpa);
3141 type_queue.deinit(gpa);
3142 unit_queue.deinit(gpa);
3143 }
3144
3040 // This is not a sufficient size, but a lower bound.3145 // This is not a sufficient size, but a lower bound.
3041 try result.ensureTotalCapacity(gpa, @intCast(zcu.reference_table.count()));3146 try result.ensureTotalCapacity(gpa, @intCast(zcu.reference_table.count()));
30423147
3043 for (zcu.reference_table.keys(), zcu.reference_table.values()) |referencer, first_ref_idx| {3148 try type_queue.ensureTotalCapacity(gpa, zcu.analysis_roots.len);
3044 assert(first_ref_idx != std.math.maxInt(u32));3149 for (zcu.analysis_roots.slice()) |mod| {
3045 var ref_idx = first_ref_idx;3150 // Logic ripped from `Zcu.PerThread.importPkg`.
3046 while (ref_idx != std.math.maxInt(u32)) {3151 // TODO: this is silly, `Module` should just store a reference to its root `File`.
3047 const ref = zcu.all_references.items[ref_idx];3152 const resolved_path = try std.fs.path.resolve(gpa, &.{
3048 const gop = try result.getOrPut(gpa, ref.referenced);3153 mod.root.root_dir.path orelse ".",
3049 if (!gop.found_existing) {3154 mod.root.sub_path,
3050 gop.value_ptr.* = .{ .referencer = referencer, .src = ref.src };3155 mod.root_src_path,
3156 });
3157 defer gpa.free(resolved_path);
3158 const file = zcu.import_table.get(resolved_path).?;
3159 const root_ty = zcu.fileRootType(file);
3160 if (root_ty == .none) continue;
3161 type_queue.putAssumeCapacityNoClobber(root_ty, null);
3162 }
3163
3164 while (true) {
3165 if (type_queue.popOrNull()) |kv| {
3166 const ty = kv.key;
3167 const referencer = kv.value;
3168 try checked_types.putNoClobber(gpa, ty, {});
3169
3170 log.debug("handle type '{}'", .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)});
3171
3172 // If this type has a `Cau` for resolution, it's automatically referenced.
3173 const resolution_cau: InternPool.Cau.Index.Optional = switch (ip.indexToKey(ty)) {
3174 .struct_type => ip.loadStructType(ty).cau,
3175 .union_type => ip.loadUnionType(ty).cau.toOptional(),
3176 .enum_type => ip.loadEnumType(ty).cau,
3177 .opaque_type => .none,
3178 else => unreachable,
3179 };
3180 if (resolution_cau.unwrap()) |cau| {
3181 // this should only be referenced by the type
3182 const unit = AnalUnit.wrap(.{ .cau = cau });
3183 assert(!result.contains(unit));
3184 try unit_queue.putNoClobber(gpa, unit, referencer);
3185 }
3186
3187 // If this is a union with a generated tag, its tag type is automatically referenced.
3188 // We don't add this reference for non-generated tags, as those will already be referenced via the union's `Cau`, with a better source location.
3189 if (zcu.typeToUnion(Type.fromInterned(ty))) |union_obj| {
3190 const tag_ty = union_obj.enum_tag_ty;
3191 if (tag_ty != .none) {
3192 if (ip.indexToKey(tag_ty).enum_type == .generated_tag) {
3193 if (!checked_types.contains(tag_ty)) {
3194 try type_queue.put(gpa, tag_ty, referencer);
3195 }
3196 }
3197 }
3198 }
3199
3200 // Queue any decls within this type which would be automatically analyzed.
3201 // Keep in sync with analysis queueing logic in `Zcu.PerThread.ScanDeclIter.scanDecl`.
3202 const ns = Type.fromInterned(ty).getNamespace(zcu).unwrap().?;
3203 for (zcu.namespacePtr(ns).other_decls.items) |cau| {
3204 // These are `comptime` and `test` declarations.
3205 // `comptime` decls are always analyzed; `test` declarations are analyzed depending on the test filter.
3206 const inst_info = ip.getCau(cau).zir_index.resolveFull(ip) orelse continue;
3207 const file = zcu.fileByIndex(inst_info.file);
3208 // If the file failed AstGen, the TrackedInst refers to the old ZIR.
3209 const zir = if (file.status == .success_zir) file.zir else file.prev_zir.?.*;
3210 const declaration = zir.getDeclaration(inst_info.inst)[0];
3211 const want_analysis = switch (declaration.name) {
3212 .@"usingnamespace" => unreachable,
3213 .@"comptime" => true,
3214 else => a: {
3215 if (!comp.config.is_test) break :a false;
3216 if (file.mod != zcu.main_mod) break :a false;
3217 if (declaration.name.isNamedTest(zir) or declaration.name == .decltest) {
3218 const nav = ip.getCau(cau).owner.unwrap().nav;
3219 const fqn_slice = ip.getNav(nav).fqn.toSlice(ip);
3220 for (comp.test_filters) |test_filter| {
3221 if (std.mem.indexOf(u8, fqn_slice, test_filter) != null) break;
3222 } else break :a false;
3223 }
3224 break :a true;
3225 },
3226 };
3227 if (want_analysis) {
3228 const unit = AnalUnit.wrap(.{ .cau = cau });
3229 if (!result.contains(unit)) {
3230 log.debug("type '{}': ref cau %{}", .{
3231 Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
3232 @intFromEnum(inst_info.inst),
3233 });
3234 try unit_queue.put(gpa, unit, referencer);
3235 }
3236 }
3237 }
3238 for (zcu.namespacePtr(ns).pub_decls.keys()) |nav| {
3239 // These are named declarations. They are analyzed only if marked `export`.
3240 const cau = ip.getNav(nav).analysis_owner.unwrap().?;
3241 const inst_info = ip.getCau(cau).zir_index.resolveFull(ip) orelse continue;
3242 const file = zcu.fileByIndex(inst_info.file);
3243 // If the file failed AstGen, the TrackedInst refers to the old ZIR.
3244 const zir = if (file.status == .success_zir) file.zir else file.prev_zir.?.*;
3245 const declaration = zir.getDeclaration(inst_info.inst)[0];
3246 if (declaration.flags.is_export) {
3247 const unit = AnalUnit.wrap(.{ .cau = cau });
3248 if (!result.contains(unit)) {
3249 log.debug("type '{}': ref cau %{}", .{
3250 Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
3251 @intFromEnum(inst_info.inst),
3252 });
3253 try unit_queue.put(gpa, unit, referencer);
3254 }
3255 }
3256 }
3257 for (zcu.namespacePtr(ns).priv_decls.keys()) |nav| {
3258 // These are named declarations. They are analyzed only if marked `export`.
3259 const cau = ip.getNav(nav).analysis_owner.unwrap().?;
3260 const inst_info = ip.getCau(cau).zir_index.resolveFull(ip) orelse continue;
3261 const file = zcu.fileByIndex(inst_info.file);
3262 // If the file failed AstGen, the TrackedInst refers to the old ZIR.
3263 const zir = if (file.status == .success_zir) file.zir else file.prev_zir.?.*;
3264 const declaration = zir.getDeclaration(inst_info.inst)[0];
3265 if (declaration.flags.is_export) {
3266 const unit = AnalUnit.wrap(.{ .cau = cau });
3267 if (!result.contains(unit)) {
3268 log.debug("type '{}': ref cau %{}", .{
3269 Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
3270 @intFromEnum(inst_info.inst),
3271 });
3272 try unit_queue.put(gpa, unit, referencer);
3273 }
3274 }
3275 }
3276 // Incremental compilation does not support `usingnamespace`.
3277 // These are only included to keep good reference traces in non-incremental updates.
3278 for (zcu.namespacePtr(ns).pub_usingnamespace.items) |nav| {
3279 const cau = ip.getNav(nav).analysis_owner.unwrap().?;
3280 const unit = AnalUnit.wrap(.{ .cau = cau });
3281 if (!result.contains(unit)) try unit_queue.put(gpa, unit, referencer);
3282 }
3283 for (zcu.namespacePtr(ns).priv_usingnamespace.items) |nav| {
3284 const cau = ip.getNav(nav).analysis_owner.unwrap().?;
3285 const unit = AnalUnit.wrap(.{ .cau = cau });
3286 if (!result.contains(unit)) try unit_queue.put(gpa, unit, referencer);
3051 }3287 }
3052 ref_idx = ref.next;3288 continue;
3289 }
3290 if (unit_queue.popOrNull()) |kv| {
3291 const unit = kv.key;
3292 try result.putNoClobber(gpa, unit, kv.value);
3293
3294 log.debug("handle unit '{}'", .{zcu.fmtAnalUnit(unit)});
3295
3296 if (zcu.reference_table.get(unit)) |first_ref_idx| {
3297 assert(first_ref_idx != std.math.maxInt(u32));
3298 var ref_idx = first_ref_idx;
3299 while (ref_idx != std.math.maxInt(u32)) {
3300 const ref = zcu.all_references.items[ref_idx];
3301 if (!result.contains(ref.referenced)) {
3302 log.debug("unit '{}': ref unit '{}'", .{
3303 zcu.fmtAnalUnit(unit),
3304 zcu.fmtAnalUnit(ref.referenced),
3305 });
3306 try unit_queue.put(gpa, ref.referenced, .{
3307 .referencer = unit,
3308 .src = ref.src,
3309 });
3310 }
3311 ref_idx = ref.next;
3312 }
3313 }
3314 if (zcu.type_reference_table.get(unit)) |first_ref_idx| {
3315 assert(first_ref_idx != std.math.maxInt(u32));
3316 var ref_idx = first_ref_idx;
3317 while (ref_idx != std.math.maxInt(u32)) {
3318 const ref = zcu.all_type_references.items[ref_idx];
3319 if (!checked_types.contains(ref.referenced)) {
3320 log.debug("unit '{}': ref type '{}'", .{
3321 zcu.fmtAnalUnit(unit),
3322 Type.fromInterned(ref.referenced).containerTypeName(ip).fmt(ip),
3323 });
3324 try type_queue.put(gpa, ref.referenced, .{
3325 .referencer = unit,
3326 .src = ref.src,
3327 });
3328 }
3329 ref_idx = ref.next;
3330 }
3331 }
3332 continue;
3053 }3333 }
3334 break;
3054 }3335 }
30553336
3056 return result;3337 return result;
...@@ -3093,7 +3374,7 @@ pub fn navSrcLoc(zcu: *const Zcu, nav_index: InternPool.Nav.Index) LazySrcLoc {...@@ -3093,7 +3374,7 @@ pub fn navSrcLoc(zcu: *const Zcu, nav_index: InternPool.Nav.Index) LazySrcLoc {
30933374
3094pub fn navSrcLine(zcu: *Zcu, nav_index: InternPool.Nav.Index) u32 {3375pub fn navSrcLine(zcu: *Zcu, nav_index: InternPool.Nav.Index) u32 {
3095 const ip = &zcu.intern_pool;3376 const ip = &zcu.intern_pool;
3096 const inst_info = ip.getNav(nav_index).srcInst(ip).resolveFull(ip);3377 const inst_info = ip.getNav(nav_index).srcInst(ip).resolveFull(ip).?;
3097 const zir = zcu.fileByIndex(inst_info.file).zir;3378 const zir = zcu.fileByIndex(inst_info.file).zir;
3098 const inst = zir.instructions.get(@intFromEnum(inst_info.inst));3379 const inst = zir.instructions.get(@intFromEnum(inst_info.inst));
3099 assert(inst.tag == .declaration);3380 assert(inst.tag == .declaration);
...@@ -3106,7 +3387,7 @@ pub fn navValue(zcu: *const Zcu, nav_index: InternPool.Nav.Index) Value {...@@ -3106,7 +3387,7 @@ pub fn navValue(zcu: *const Zcu, nav_index: InternPool.Nav.Index) Value {
31063387
3107pub fn navFileScopeIndex(zcu: *Zcu, nav: InternPool.Nav.Index) File.Index {3388pub fn navFileScopeIndex(zcu: *Zcu, nav: InternPool.Nav.Index) File.Index {
3108 const ip = &zcu.intern_pool;3389 const ip = &zcu.intern_pool;
3109 return ip.getNav(nav).srcInst(ip).resolveFull(ip).file;3390 return ip.getNav(nav).srcInst(ip).resolveFile(ip);
3110}3391}
31113392
3112pub fn navFileScope(zcu: *Zcu, nav: InternPool.Nav.Index) *File {3393pub fn navFileScope(zcu: *Zcu, nav: InternPool.Nav.Index) *File {
...@@ -3115,6 +3396,75 @@ pub fn navFileScope(zcu: *Zcu, nav: InternPool.Nav.Index) *File {...@@ -3115,6 +3396,75 @@ pub fn navFileScope(zcu: *Zcu, nav: InternPool.Nav.Index) *File {
31153396
3116pub fn cauFileScope(zcu: *Zcu, cau: InternPool.Cau.Index) *File {3397pub fn cauFileScope(zcu: *Zcu, cau: InternPool.Cau.Index) *File {
3117 const ip = &zcu.intern_pool;3398 const ip = &zcu.intern_pool;
3118 const file_index = ip.getCau(cau).zir_index.resolveFull(ip).file;3399 const file_index = ip.getCau(cau).zir_index.resolveFile(ip);
3119 return zcu.fileByIndex(file_index);3400 return zcu.fileByIndex(file_index);
3120}3401}
3402
3403pub fn fmtAnalUnit(zcu: *Zcu, unit: AnalUnit) std.fmt.Formatter(formatAnalUnit) {
3404 return .{ .data = .{ .unit = unit, .zcu = zcu } };
3405}
3406pub fn fmtDependee(zcu: *Zcu, d: InternPool.Dependee) std.fmt.Formatter(formatDependee) {
3407 return .{ .data = .{ .dependee = d, .zcu = zcu } };
3408}
3409
3410fn formatAnalUnit(data: struct { unit: AnalUnit, zcu: *Zcu }, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
3411 _ = .{ fmt, options };
3412 const zcu = data.zcu;
3413 const ip = &zcu.intern_pool;
3414 switch (data.unit.unwrap()) {
3415 .cau => |cau_index| {
3416 const cau = ip.getCau(cau_index);
3417 switch (cau.owner.unwrap()) {
3418 .nav => |nav| return writer.print("cau(decl='{}')", .{ip.getNav(nav).fqn.fmt(ip)}),
3419 .type => |ty| return writer.print("cau(ty='{}')", .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)}),
3420 .none => if (cau.zir_index.resolveFull(ip)) |resolved| {
3421 const file_path = zcu.fileByIndex(resolved.file).sub_file_path;
3422 return writer.print("cau(inst=('{s}', %{}))", .{ file_path, @intFromEnum(resolved.inst) });
3423 } else {
3424 return writer.writeAll("cau(inst=<lost>)");
3425 },
3426 }
3427 },
3428 .func => |func| {
3429 const nav = zcu.funcInfo(func).owner_nav;
3430 return writer.print("func('{}')", .{ip.getNav(nav).fqn.fmt(ip)});
3431 },
3432 }
3433}
3434fn formatDependee(data: struct { dependee: InternPool.Dependee, zcu: *Zcu }, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
3435 _ = .{ fmt, options };
3436 const zcu = data.zcu;
3437 const ip = &zcu.intern_pool;
3438 switch (data.dependee) {
3439 .src_hash => |ti| {
3440 const info = ti.resolveFull(ip) orelse {
3441 return writer.writeAll("inst(<lost>)");
3442 };
3443 const file_path = zcu.fileByIndex(info.file).sub_file_path;
3444 return writer.print("inst('{s}', %{d})", .{ file_path, @intFromEnum(info.inst) });
3445 },
3446 .nav_val => |nav| {
3447 const fqn = ip.getNav(nav).fqn;
3448 return writer.print("nav('{}')", .{fqn.fmt(ip)});
3449 },
3450 .interned => |ip_index| switch (ip.indexToKey(ip_index)) {
3451 .struct_type, .union_type, .enum_type => return writer.print("type('{}')", .{Type.fromInterned(ip_index).containerTypeName(ip).fmt(ip)}),
3452 .func => |f| return writer.print("ies('{}')", .{ip.getNav(f.owner_nav).fqn.fmt(ip)}),
3453 else => unreachable,
3454 },
3455 .namespace => |ti| {
3456 const info = ti.resolveFull(ip) orelse {
3457 return writer.writeAll("namespace(<lost>)");
3458 };
3459 const file_path = zcu.fileByIndex(info.file).sub_file_path;
3460 return writer.print("namespace('{s}', %{d})", .{ file_path, @intFromEnum(info.inst) });
3461 },
3462 .namespace_name => |k| {
3463 const info = k.namespace.resolveFull(ip) orelse {
3464 return writer.print("namespace(<lost>, '{}')", .{k.name.fmt(ip)});
3465 };
3466 const file_path = zcu.fileByIndex(info.file).sub_file_path;
3467 return writer.print("namespace('{s}', %{d}, '{}')", .{ file_path, @intFromEnum(info.inst), k.name.fmt(ip) });
3468 },
3469 }
3470}
src/Zcu/PerThread.zig+901-321
...@@ -1,3 +1,6 @@...@@ -1,3 +1,6 @@
1//! This type provides a wrapper around a `*Zcu` for uses which require a thread `Id`.
2//! Any operation which mutates `InternPool` state lives here rather than on `Zcu`.
3
1zcu: *Zcu,4zcu: *Zcu,
25
3/// Dense, per-thread unique index.6/// Dense, per-thread unique index.
...@@ -39,7 +42,6 @@ pub fn astGenFile(...@@ -39,7 +42,6 @@ pub fn astGenFile(
39 pt: Zcu.PerThread,42 pt: Zcu.PerThread,
40 file: *Zcu.File,43 file: *Zcu.File,
41 path_digest: Cache.BinDigest,44 path_digest: Cache.BinDigest,
42 old_root_type: InternPool.Index,
43) !void {45) !void {
44 dev.check(.ast_gen);46 dev.check(.ast_gen);
45 assert(!file.mod.isBuiltin());47 assert(!file.mod.isBuiltin());
...@@ -299,25 +301,15 @@ pub fn astGenFile(...@@ -299,25 +301,15 @@ pub fn astGenFile(
299 file.status = .astgen_failure;301 file.status = .astgen_failure;
300 return error.AnalysisFail;302 return error.AnalysisFail;
301 }303 }
302
303 if (old_root_type != .none) {
304 // The root of this file must be re-analyzed, since the file has changed.
305 comp.mutex.lock();
306 defer comp.mutex.unlock();
307
308 log.debug("outdated file root type: {}", .{old_root_type});
309 try zcu.outdated_file_root.put(gpa, old_root_type, {});
310 }
311}304}
312305
313const UpdatedFile = struct {306const UpdatedFile = struct {
314 file_index: Zcu.File.Index,
315 file: *Zcu.File,307 file: *Zcu.File,
316 inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index),308 inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index),
317};309};
318310
319fn cleanupUpdatedFiles(gpa: Allocator, updated_files: *std.ArrayListUnmanaged(UpdatedFile)) void {311fn cleanupUpdatedFiles(gpa: Allocator, updated_files: *std.AutoArrayHashMapUnmanaged(Zcu.File.Index, UpdatedFile)) void {
320 for (updated_files.items) |*elem| elem.inst_map.deinit(gpa);312 for (updated_files.values()) |*elem| elem.inst_map.deinit(gpa);
321 updated_files.deinit(gpa);313 updated_files.deinit(gpa);
322}314}
323315
...@@ -328,143 +320,166 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {...@@ -328,143 +320,166 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
328 const gpa = zcu.gpa;320 const gpa = zcu.gpa;
329321
330 // We need to visit every updated File for every TrackedInst in InternPool.322 // We need to visit every updated File for every TrackedInst in InternPool.
331 var updated_files: std.ArrayListUnmanaged(UpdatedFile) = .{};323 var updated_files: std.AutoArrayHashMapUnmanaged(Zcu.File.Index, UpdatedFile) = .{};
332 defer cleanupUpdatedFiles(gpa, &updated_files);324 defer cleanupUpdatedFiles(gpa, &updated_files);
333 for (zcu.import_table.values()) |file_index| {325 for (zcu.import_table.values()) |file_index| {
334 const file = zcu.fileByIndex(file_index);326 const file = zcu.fileByIndex(file_index);
335 const old_zir = file.prev_zir orelse continue;327 const old_zir = file.prev_zir orelse continue;
336 const new_zir = file.zir;328 const new_zir = file.zir;
337 try updated_files.append(gpa, .{329 const gop = try updated_files.getOrPut(gpa, file_index);
338 .file_index = file_index,330 assert(!gop.found_existing);
331 gop.value_ptr.* = .{
339 .file = file,332 .file = file,
340 .inst_map = .{},333 .inst_map = .{},
341 });334 };
342 const inst_map = &updated_files.items[updated_files.items.len - 1].inst_map;335 if (!new_zir.hasCompileErrors()) {
343 try Zcu.mapOldZirToNew(gpa, old_zir.*, new_zir, inst_map);336 try Zcu.mapOldZirToNew(gpa, old_zir.*, file.zir, &gop.value_ptr.inst_map);
337 }
344 }338 }
345339
346 if (updated_files.items.len == 0)340 if (updated_files.count() == 0)
347 return;341 return;
348342
349 for (ip.locals, 0..) |*local, tid| {343 for (ip.locals, 0..) |*local, tid| {
350 const tracked_insts_list = local.getMutableTrackedInsts(gpa);344 const tracked_insts_list = local.getMutableTrackedInsts(gpa);
351 for (tracked_insts_list.view().items(.@"0"), 0..) |*tracked_inst, tracked_inst_unwrapped_index| {345 for (tracked_insts_list.viewAllowEmpty().items(.@"0"), 0..) |*tracked_inst, tracked_inst_unwrapped_index| {
352 for (updated_files.items) |updated_file| {346 const file_index = tracked_inst.file;
353 const file_index = updated_file.file_index;347 const updated_file = updated_files.get(file_index) orelse continue;
354 if (tracked_inst.file != file_index) continue;
355
356 const file = updated_file.file;
357 const old_zir = file.prev_zir.?.*;
358 const new_zir = file.zir;
359 const old_tag = old_zir.instructions.items(.tag);
360 const old_data = old_zir.instructions.items(.data);
361 const inst_map = &updated_file.inst_map;
362
363 const old_inst = tracked_inst.inst;
364 const tracked_inst_index = (InternPool.TrackedInst.Index.Unwrapped{
365 .tid = @enumFromInt(tid),
366 .index = @intCast(tracked_inst_unwrapped_index),
367 }).wrap(ip);
368 tracked_inst.inst = inst_map.get(old_inst) orelse {
369 // Tracking failed for this instruction. Invalidate associated `src_hash` deps.
370 log.debug("tracking failed for %{d}", .{old_inst});
371 try zcu.markDependeeOutdated(.{ .src_hash = tracked_inst_index });
372 continue;
373 };
374348
375 if (old_zir.getAssociatedSrcHash(old_inst)) |old_hash| hash_changed: {349 const file = updated_file.file;
376 if (new_zir.getAssociatedSrcHash(tracked_inst.inst)) |new_hash| {350
377 if (std.zig.srcHashEql(old_hash, new_hash)) {351 if (file.zir.hasCompileErrors()) {
378 break :hash_changed;352 // If we mark this as outdated now, users of this inst will just get a transitive analysis failure.
379 }353 // Ultimately, they would end up throwing out potentially useful analysis results.
380 log.debug("hash for (%{d} -> %{d}) changed: {} -> {}", .{354 // So, do nothing. We already have the file failure -- that's sufficient for now!
381 old_inst,355 continue;
382 tracked_inst.inst,356 }
383 std.fmt.fmtSliceHexLower(&old_hash),357 const old_inst = tracked_inst.inst.unwrap() orelse continue; // we can't continue tracking lost insts
384 std.fmt.fmtSliceHexLower(&new_hash),358 const tracked_inst_index = (InternPool.TrackedInst.Index.Unwrapped{
385 });359 .tid = @enumFromInt(tid),
360 .index = @intCast(tracked_inst_unwrapped_index),
361 }).wrap(ip);
362 const new_inst = updated_file.inst_map.get(old_inst) orelse {
363 // Tracking failed for this instruction. Invalidate associated `src_hash` deps.
364 log.debug("tracking failed for %{d}", .{old_inst});
365 tracked_inst.inst = .lost;
366 try zcu.markDependeeOutdated(.not_marked_po, .{ .src_hash = tracked_inst_index });
367 continue;
368 };
369 tracked_inst.inst = InternPool.TrackedInst.MaybeLost.ZirIndex.wrap(new_inst);
370
371 const old_zir = file.prev_zir.?.*;
372 const new_zir = file.zir;
373 const old_tag = old_zir.instructions.items(.tag);
374 const old_data = old_zir.instructions.items(.data);
375
376 if (old_zir.getAssociatedSrcHash(old_inst)) |old_hash| hash_changed: {
377 if (new_zir.getAssociatedSrcHash(new_inst)) |new_hash| {
378 if (std.zig.srcHashEql(old_hash, new_hash)) {
379 break :hash_changed;
386 }380 }
387 // The source hash associated with this instruction changed - invalidate relevant dependencies.381 log.debug("hash for (%{d} -> %{d}) changed: {} -> {}", .{
388 try zcu.markDependeeOutdated(.{ .src_hash = tracked_inst_index });382 old_inst,
383 new_inst,
384 std.fmt.fmtSliceHexLower(&old_hash),
385 std.fmt.fmtSliceHexLower(&new_hash),
386 });
389 }387 }
388 // The source hash associated with this instruction changed - invalidate relevant dependencies.
389 try zcu.markDependeeOutdated(.not_marked_po, .{ .src_hash = tracked_inst_index });
390 }
390391
391 // If this is a `struct_decl` etc, we must invalidate any outdated namespace dependencies.392 // If this is a `struct_decl` etc, we must invalidate any outdated namespace dependencies.
392 const has_namespace = switch (old_tag[@intFromEnum(old_inst)]) {393 const has_namespace = switch (old_tag[@intFromEnum(old_inst)]) {
393 .extended => switch (old_data[@intFromEnum(old_inst)].extended.opcode) {394 .extended => switch (old_data[@intFromEnum(old_inst)].extended.opcode) {
394 .struct_decl, .union_decl, .opaque_decl, .enum_decl => true,395 .struct_decl, .union_decl, .opaque_decl, .enum_decl => true,
395 else => false,
396 },
397 else => false,396 else => false,
398 };397 },
399 if (!has_namespace) continue;398 else => false,
400399 };
401 var old_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{};400 if (!has_namespace) continue;
402 defer old_names.deinit(zcu.gpa);401
403 {402 var old_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{};
404 var it = old_zir.declIterator(old_inst);403 defer old_names.deinit(zcu.gpa);
405 while (it.next()) |decl_inst| {404 {
406 const decl_name = old_zir.getDeclaration(decl_inst)[0].name;405 var it = old_zir.declIterator(old_inst);
407 switch (decl_name) {406 while (it.next()) |decl_inst| {
408 .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue,407 const decl_name = old_zir.getDeclaration(decl_inst)[0].name;
409 _ => if (decl_name.isNamedTest(old_zir)) continue,408 switch (decl_name) {
410 }409 .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue,
411 const name_zir = decl_name.toString(old_zir).?;410 _ => if (decl_name.isNamedTest(old_zir)) continue,
412 const name_ip = try zcu.intern_pool.getOrPutString(
413 zcu.gpa,
414 pt.tid,
415 old_zir.nullTerminatedString(name_zir),
416 .no_embedded_nulls,
417 );
418 try old_names.put(zcu.gpa, name_ip, {});
419 }411 }
412 const name_zir = decl_name.toString(old_zir).?;
413 const name_ip = try zcu.intern_pool.getOrPutString(
414 zcu.gpa,
415 pt.tid,
416 old_zir.nullTerminatedString(name_zir),
417 .no_embedded_nulls,
418 );
419 try old_names.put(zcu.gpa, name_ip, {});
420 }420 }
421 var any_change = false;421 }
422 {422 var any_change = false;
423 var it = new_zir.declIterator(tracked_inst.inst);423 {
424 while (it.next()) |decl_inst| {424 var it = new_zir.declIterator(new_inst);
425 const decl_name = new_zir.getDeclaration(decl_inst)[0].name;425 while (it.next()) |decl_inst| {
426 switch (decl_name) {426 const decl_name = new_zir.getDeclaration(decl_inst)[0].name;
427 .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue,427 switch (decl_name) {
428 _ => if (decl_name.isNamedTest(new_zir)) continue,428 .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue,
429 }429 _ => if (decl_name.isNamedTest(new_zir)) continue,
430 const name_zir = decl_name.toString(new_zir).?;
431 const name_ip = try zcu.intern_pool.getOrPutString(
432 zcu.gpa,
433 pt.tid,
434 new_zir.nullTerminatedString(name_zir),
435 .no_embedded_nulls,
436 );
437 if (!old_names.swapRemove(name_ip)) continue;
438 // Name added
439 any_change = true;
440 try zcu.markDependeeOutdated(.{ .namespace_name = .{
441 .namespace = tracked_inst_index,
442 .name = name_ip,
443 } });
444 }430 }
445 }431 const name_zir = decl_name.toString(new_zir).?;
446 // The only elements remaining in `old_names` now are any names which were removed.432 const name_ip = try zcu.intern_pool.getOrPutString(
447 for (old_names.keys()) |name_ip| {433 zcu.gpa,
434 pt.tid,
435 new_zir.nullTerminatedString(name_zir),
436 .no_embedded_nulls,
437 );
438 if (old_names.swapRemove(name_ip)) continue;
439 // Name added
448 any_change = true;440 any_change = true;
449 try zcu.markDependeeOutdated(.{ .namespace_name = .{441 try zcu.markDependeeOutdated(.not_marked_po, .{ .namespace_name = .{
450 .namespace = tracked_inst_index,442 .namespace = tracked_inst_index,
451 .name = name_ip,443 .name = name_ip,
452 } });444 } });
453 }445 }
446 }
447 // The only elements remaining in `old_names` now are any names which were removed.
448 for (old_names.keys()) |name_ip| {
449 any_change = true;
450 try zcu.markDependeeOutdated(.not_marked_po, .{ .namespace_name = .{
451 .namespace = tracked_inst_index,
452 .name = name_ip,
453 } });
454 }
454455
455 if (any_change) {456 if (any_change) {
456 try zcu.markDependeeOutdated(.{ .namespace = tracked_inst_index });457 try zcu.markDependeeOutdated(.not_marked_po, .{ .namespace = tracked_inst_index });
457 }
458 }458 }
459 }459 }
460 }460 }
461461
462 for (updated_files.items) |updated_file| {462 try ip.rehashTrackedInsts(gpa, pt.tid);
463
464 for (updated_files.keys(), updated_files.values()) |file_index, updated_file| {
463 const file = updated_file.file;465 const file = updated_file.file;
464 const prev_zir = file.prev_zir.?;466 if (file.zir.hasCompileErrors()) {
465 file.prev_zir = null;467 // Keep `prev_zir` around: it's the last non-error ZIR.
466 prev_zir.deinit(gpa);468 // Don't update the namespace, as we have no new data to update *to*.
467 gpa.destroy(prev_zir);469 } else {
470 const prev_zir = file.prev_zir.?;
471 file.prev_zir = null;
472 prev_zir.deinit(gpa);
473 gpa.destroy(prev_zir);
474
475 // For every file which has changed, re-scan the namespace of the file's root struct type.
476 // These types are special-cased because they don't have an enclosing declaration which will
477 // be re-analyzed (causing the struct's namespace to be re-scanned). It's fine to do this
478 // now because this work is fast (no actual Sema work is happening, we're just updating the
479 // namespace contents). We must do this after updating ZIR refs above, since `scanNamespace`
480 // will track some instructions.
481 try pt.updateFileNamespace(file_index);
482 }
468 }483 }
469}484}
470485
...@@ -473,8 +488,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {...@@ -473,8 +488,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
473pub fn ensureFileAnalyzed(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {488pub fn ensureFileAnalyzed(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {
474 const file_root_type = pt.zcu.fileRootType(file_index);489 const file_root_type = pt.zcu.fileRootType(file_index);
475 if (file_root_type != .none) {490 if (file_root_type != .none) {
476 const file_root_type_cau = pt.zcu.intern_pool.loadStructType(file_root_type).cau.unwrap().?;491 _ = try pt.ensureTypeUpToDate(file_root_type, false);
477 return pt.ensureCauAnalyzed(file_root_type_cau);
478 } else {492 } else {
479 return pt.semaFile(file_index);493 return pt.semaFile(file_index);
480 }494 }
...@@ -491,9 +505,8 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu...@@ -491,9 +505,8 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu
491 const gpa = zcu.gpa;505 const gpa = zcu.gpa;
492 const ip = &zcu.intern_pool;506 const ip = &zcu.intern_pool;
493507
494 const anal_unit = InternPool.AnalUnit.wrap(.{ .cau = cau_index });508 const anal_unit = AnalUnit.wrap(.{ .cau = cau_index });
495 const cau = ip.getCau(cau_index);509 const cau = ip.getCau(cau_index);
496 const inst_info = cau.zir_index.resolveFull(ip);
497510
498 log.debug("ensureCauAnalyzed {d}", .{@intFromEnum(cau_index)});511 log.debug("ensureCauAnalyzed {d}", .{@intFromEnum(cau_index)});
499512
...@@ -514,14 +527,96 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu...@@ -514,14 +527,96 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu
514527
515 if (cau_outdated) {528 if (cau_outdated) {
516 _ = zcu.outdated_ready.swapRemove(anal_unit);529 _ = zcu.outdated_ready.swapRemove(anal_unit);
530 } else {
531 // We can trust the current information about this `Cau`.
532 if (zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit)) {
533 return error.AnalysisFail;
534 }
535 // If it wasn't failed and wasn't marked outdated, then either...
536 // * it is a type and is up-to-date, or
537 // * it is a `comptime` decl and is up-to-date, or
538 // * it is another decl and is EITHER up-to-date OR never-referenced (so unresolved)
539 // We just need to check for that last case.
540 switch (cau.owner.unwrap()) {
541 .type, .none => return,
542 .nav => |nav| if (ip.getNav(nav).status == .resolved) return,
543 }
517 }544 }
518545
519 // TODO: this only works if namespace lookups in Sema trigger `ensureCauAnalyzed`, because546 const sema_result: SemaCauResult, const analysis_fail = if (pt.ensureCauAnalyzedInner(cau_index, cau_outdated)) |result|
520 // `outdated_file_root` information is not "viral", so we need that a namespace lookup first547 .{ result, false }
521 // handles the case where the file root is not an outdated *type* but does have an outdated548 else |err| switch (err) {
522 // *namespace*. A more logically simple alternative may be for a file's root struct to register549 error.AnalysisFail => res: {
523 // a dependency on the file's entire source code (hash). Alternatively, we could make sure that550 if (!zcu.failed_analysis.contains(anal_unit)) {
524 // these are always handled first in an update. Actually, that's probably the best option.551 // If this `Cau` caused the error, it would have an entry in `failed_analysis`.
552 // Since it does not, this must be a transitive failure.
553 try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
554 }
555 // We treat errors as up-to-date, since those uses would just trigger a transitive error.
556 // The exception is types, since type declarations may require re-analysis if the type, e.g. its captures, changed.
557 const outdated = cau.owner.unwrap() == .type;
558 break :res .{ .{
559 .invalidate_decl_val = outdated,
560 .invalidate_decl_ref = outdated,
561 }, true };
562 },
563 error.OutOfMemory => res: {
564 try zcu.failed_analysis.ensureUnusedCapacity(gpa, 1);
565 try zcu.retryable_failures.ensureUnusedCapacity(gpa, 1);
566 const msg = try Zcu.ErrorMsg.create(
567 gpa,
568 .{ .base_node_inst = cau.zir_index, .offset = Zcu.LazySrcLoc.Offset.nodeOffset(0) },
569 "unable to analyze: OutOfMemory",
570 .{},
571 );
572 zcu.retryable_failures.appendAssumeCapacity(anal_unit);
573 zcu.failed_analysis.putAssumeCapacityNoClobber(anal_unit, msg);
574 // We treat errors as up-to-date, since those uses would just trigger a transitive error
575 break :res .{ .{
576 .invalidate_decl_val = false,
577 .invalidate_decl_ref = false,
578 }, true };
579 },
580 };
581
582 if (cau_outdated) {
583 // TODO: we do not yet have separate dependencies for decl values vs types.
584 const invalidate = sema_result.invalidate_decl_val or sema_result.invalidate_decl_ref;
585 const dependee: InternPool.Dependee = switch (cau.owner.unwrap()) {
586 .none => return, // there are no dependencies on a `comptime` decl!
587 .nav => |nav_index| .{ .nav_val = nav_index },
588 .type => |ty| .{ .interned = ty },
589 };
590
591 if (invalidate) {
592 // This dependency was marked as PO, meaning dependees were waiting
593 // on its analysis result, and it has turned out to be outdated.
594 // Update dependees accordingly.
595 try zcu.markDependeeOutdated(.marked_po, dependee);
596 } else {
597 // This dependency was previously PO, but turned out to be up-to-date.
598 // We do not need to queue successive analysis.
599 try zcu.markPoDependeeUpToDate(dependee);
600 }
601 }
602
603 if (analysis_fail) return error.AnalysisFail;
604}
605
606fn ensureCauAnalyzedInner(
607 pt: Zcu.PerThread,
608 cau_index: InternPool.Cau.Index,
609 cau_outdated: bool,
610) Zcu.SemaError!SemaCauResult {
611 const zcu = pt.zcu;
612 const ip = &zcu.intern_pool;
613
614 const cau = ip.getCau(cau_index);
615 const anal_unit = AnalUnit.wrap(.{ .cau = cau_index });
616
617 const inst_info = cau.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
618
619 // TODO: document this elsewhere mlugg!
525 // For my own benefit, here's how a namespace update for a normal (non-file-root) type works:620 // For my own benefit, here's how a namespace update for a normal (non-file-root) type works:
526 // `const S = struct { ... };`621 // `const S = struct { ... };`
527 // We are adding or removing a declaration within this `struct`.622 // We are adding or removing a declaration within this `struct`.
...@@ -533,33 +628,12 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu...@@ -533,33 +628,12 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu
533 // * so, it uses the same `struct`628 // * so, it uses the same `struct`
534 // * but this doesn't stop it from updating the namespace!629 // * but this doesn't stop it from updating the namespace!
535 // * we basically do `scanDecls`, updating the namespace as needed630 // * we basically do `scanDecls`, updating the namespace as needed
536 // * TODO: optimize this to make sure we only do it once a generation i guess?
537 // * so everyone lived happily ever after631 // * so everyone lived happily ever after
538 const file_root_outdated = switch (cau.owner.unwrap()) {
539 .type => |ty| zcu.outdated_file_root.swapRemove(ty),
540 .nav, .none => false,
541 };
542632
543 if (zcu.fileByIndex(inst_info.file).status != .success_zir) {633 if (zcu.fileByIndex(inst_info.file).status != .success_zir) {
544 return error.AnalysisFail;634 return error.AnalysisFail;
545 }635 }
546636
547 if (!cau_outdated and !file_root_outdated) {
548 // We can trust the current information about this `Cau`.
549 if (zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit)) {
550 return error.AnalysisFail;
551 }
552 // If it wasn't failed and wasn't marked outdated, then either...
553 // * it is a type and is up-to-date, or
554 // * it is a `comptime` decl and is up-to-date, or
555 // * it is another decl and is EITHER up-to-date OR never-referenced (so unresolved)
556 // We just need to check for that last case.
557 switch (cau.owner.unwrap()) {
558 .type, .none => return,
559 .nav => |nav| if (ip.getNav(nav).status == .resolved) return,
560 }
561 }
562
563 // `cau_outdated` can be true in the initial update for `comptime` declarations,637 // `cau_outdated` can be true in the initial update for `comptime` declarations,
564 // so this isn't a `dev.check`.638 // so this isn't a `dev.check`.
565 if (cau_outdated and dev.env.supports(.incremental)) {639 if (cau_outdated and dev.env.supports(.incremental)) {
...@@ -567,73 +641,23 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu...@@ -567,73 +641,23 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu
567 // prior to re-analysis.641 // prior to re-analysis.
568 zcu.deleteUnitExports(anal_unit);642 zcu.deleteUnitExports(anal_unit);
569 zcu.deleteUnitReferences(anal_unit);643 zcu.deleteUnitReferences(anal_unit);
570 }644 if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
571645 kv.value.destroy(zcu.gpa);
572 const sema_result: SemaCauResult = res: {
573 if (inst_info.inst == .main_struct_inst) {
574 const changed = try pt.semaFileUpdate(inst_info.file, cau_outdated);
575 break :res .{
576 .invalidate_decl_val = changed,
577 .invalidate_decl_ref = changed,
578 };
579 }646 }
580647 _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
581 const decl_prog_node = zcu.sema_prog_node.start(switch (cau.owner.unwrap()) {
582 .nav => |nav| ip.getNav(nav).fqn.toSlice(ip),
583 .type => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip),
584 .none => "comptime",
585 }, 0);
586 defer decl_prog_node.end();
587
588 break :res pt.semaCau(cau_index) catch |err| switch (err) {
589 error.AnalysisFail => {
590 if (!zcu.failed_analysis.contains(anal_unit)) {
591 // If this `Cau` caused the error, it would have an entry in `failed_analysis`.
592 // Since it does not, this must be a transitive failure.
593 try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
594 }
595 return error.AnalysisFail;
596 },
597 error.GenericPoison => unreachable,
598 error.ComptimeBreak => unreachable,
599 error.ComptimeReturn => unreachable,
600 error.OutOfMemory => {
601 try zcu.failed_analysis.ensureUnusedCapacity(gpa, 1);
602 try zcu.retryable_failures.append(gpa, anal_unit);
603 zcu.failed_analysis.putAssumeCapacityNoClobber(anal_unit, try Zcu.ErrorMsg.create(
604 gpa,
605 .{ .base_node_inst = cau.zir_index, .offset = Zcu.LazySrcLoc.Offset.nodeOffset(0) },
606 "unable to analyze: OutOfMemory",
607 .{},
608 ));
609 return error.AnalysisFail;
610 },
611 };
612 };
613
614 if (!cau_outdated) {
615 // We definitely don't need to do any dependency tracking, so our work is done.
616 return;
617 }648 }
618649
619 // TODO: we do not yet have separate dependencies for decl values vs types.650 const decl_prog_node = zcu.sema_prog_node.start(switch (cau.owner.unwrap()) {
620 const invalidate = sema_result.invalidate_decl_val or sema_result.invalidate_decl_ref;651 .nav => |nav| ip.getNav(nav).fqn.toSlice(ip),
621 const dependee: InternPool.Dependee = switch (cau.owner.unwrap()) {652 .type => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip),
622 .none => return, // there are no dependencies on a `comptime` decl!653 .none => "comptime",
623 .nav => |nav_index| .{ .nav_val = nav_index },654 }, 0);
624 .type => |ty| .{ .interned = ty },655 defer decl_prog_node.end();
625 };
626656
627 if (invalidate) {657 return pt.semaCau(cau_index) catch |err| switch (err) {
628 // This dependency was marked as PO, meaning dependees were waiting658 error.GenericPoison, error.ComptimeBreak, error.ComptimeReturn => unreachable,
629 // on its analysis result, and it has turned out to be outdated.659 error.AnalysisFail, error.OutOfMemory => |e| return e,
630 // Update dependees accordingly.660 };
631 try zcu.markDependeeOutdated(dependee);
632 } else {
633 // This dependency was previously PO, but turned out to be up-to-date.
634 // We do not need to queue successive analysis.
635 try zcu.markPoDependeeUpToDate(dependee);
636 }
637}661}
638662
639pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: InternPool.Index) Zcu.SemaError!void {663pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: InternPool.Index) Zcu.SemaError!void {
...@@ -653,6 +677,63 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter...@@ -653,6 +677,63 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
653677
654 log.debug("ensureFuncBodyAnalyzed {d}", .{@intFromEnum(func_index)});678 log.debug("ensureFuncBodyAnalyzed {d}", .{@intFromEnum(func_index)});
655679
680 const anal_unit = AnalUnit.wrap(.{ .func = func_index });
681 const func_outdated = zcu.outdated.swapRemove(anal_unit) or
682 zcu.potentially_outdated.swapRemove(anal_unit);
683
684 if (func_outdated) {
685 _ = zcu.outdated_ready.swapRemove(anal_unit);
686 } else {
687 // We can trust the current information about this function.
688 if (zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit)) {
689 return error.AnalysisFail;
690 }
691 switch (func.analysisUnordered(ip).state) {
692 .unreferenced => {}, // this is the first reference
693 .queued => {}, // we're waiting on first-time analysis
694 .analyzed => return, // up-to-date
695 }
696 }
697
698 const ies_outdated, const analysis_fail = if (pt.ensureFuncBodyAnalyzedInner(func_index, func_outdated)) |result|
699 .{ result.ies_outdated, false }
700 else |err| switch (err) {
701 error.AnalysisFail => res: {
702 if (!zcu.failed_analysis.contains(anal_unit)) {
703 // If this function caused the error, it would have an entry in `failed_analysis`.
704 // Since it does not, this must be a transitive failure.
705 try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
706 }
707 break :res .{ false, true }; // we treat errors as up-to-date IES, since those uses would just trigger a transitive error
708 },
709 error.OutOfMemory => return error.OutOfMemory, // TODO: graceful handling like `ensureCauAnalyzed`
710 };
711
712 if (func_outdated) {
713 if (ies_outdated) {
714 log.debug("func IES invalidated ('{d}')", .{@intFromEnum(func_index)});
715 try zcu.markDependeeOutdated(.marked_po, .{ .interned = func_index });
716 } else {
717 log.debug("func IES up-to-date ('{d}')", .{@intFromEnum(func_index)});
718 try zcu.markPoDependeeUpToDate(.{ .interned = func_index });
719 }
720 }
721
722 if (analysis_fail) return error.AnalysisFail;
723}
724
725fn ensureFuncBodyAnalyzedInner(
726 pt: Zcu.PerThread,
727 func_index: InternPool.Index,
728 func_outdated: bool,
729) Zcu.SemaError!struct { ies_outdated: bool } {
730 const zcu = pt.zcu;
731 const gpa = zcu.gpa;
732 const ip = &zcu.intern_pool;
733
734 const func = zcu.funcInfo(func_index);
735 const anal_unit = AnalUnit.wrap(.{ .func = func_index });
736
656 // Here's an interesting question: is this function actually valid?737 // Here's an interesting question: is this function actually valid?
657 // Maybe the signature changed, so we'll end up creating a whole different `func`738 // Maybe the signature changed, so we'll end up creating a whole different `func`
658 // in the InternPool, and this one is a waste of time to analyze. Worse, we'd be739 // in the InternPool, and this one is a waste of time to analyze. Worse, we'd be
...@@ -672,8 +753,10 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter...@@ -672,8 +753,10 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
672 });753 });
673754
674 if (ip.isRemoved(func_index) or (func.generic_owner != .none and ip.isRemoved(func.generic_owner))) {755 if (ip.isRemoved(func_index) or (func.generic_owner != .none and ip.isRemoved(func.generic_owner))) {
675 try zcu.markDependeeOutdated(.{ .interned = func_index }); // IES756 if (func_outdated) {
676 ip.removeDependenciesForDepender(gpa, InternPool.AnalUnit.wrap(.{ .func = func_index }));757 try zcu.markDependeeOutdated(.marked_po, .{ .interned = func_index }); // IES
758 }
759 ip.removeDependenciesForDepender(gpa, AnalUnit.wrap(.{ .func = func_index }));
677 ip.remove(pt.tid, func_index);760 ip.remove(pt.tid, func_index);
678 @panic("TODO: remove orphaned function from binary");761 @panic("TODO: remove orphaned function from binary");
679 }762 }
...@@ -685,15 +768,14 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter...@@ -685,15 +768,14 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
685 else768 else
686 .none;769 .none;
687770
688 const anal_unit = InternPool.AnalUnit.wrap(.{ .func = func_index });
689 const func_outdated = zcu.outdated.swapRemove(anal_unit) or
690 zcu.potentially_outdated.swapRemove(anal_unit);
691
692 if (func_outdated) {771 if (func_outdated) {
693 dev.check(.incremental);772 dev.check(.incremental);
694 _ = zcu.outdated_ready.swapRemove(anal_unit);
695 zcu.deleteUnitExports(anal_unit);773 zcu.deleteUnitExports(anal_unit);
696 zcu.deleteUnitReferences(anal_unit);774 zcu.deleteUnitReferences(anal_unit);
775 if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
776 kv.value.destroy(gpa);
777 }
778 _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
697 }779 }
698780
699 if (!func_outdated) {781 if (!func_outdated) {
...@@ -704,7 +786,7 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter...@@ -704,7 +786,7 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
704 switch (func.analysisUnordered(ip).state) {786 switch (func.analysisUnordered(ip).state) {
705 .unreferenced => {}, // this is the first reference787 .unreferenced => {}, // this is the first reference
706 .queued => {}, // we're waiting on first-time analysis788 .queued => {}, // we're waiting on first-time analysis
707 .analyzed => return, // up-to-date789 .analyzed => return .{ .ies_outdated = false }, // up-to-date
708 }790 }
709 }791 }
710792
...@@ -713,28 +795,11 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter...@@ -713,28 +795,11 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
713 if (func_outdated) "outdated" else "never analyzed",795 if (func_outdated) "outdated" else "never analyzed",
714 });796 });
715797
716 var air = pt.analyzeFnBody(func_index) catch |err| switch (err) {798 var air = try pt.analyzeFnBody(func_index);
717 error.AnalysisFail => {
718 if (!zcu.failed_analysis.contains(anal_unit)) {
719 // If this function caused the error, it would have an entry in `failed_analysis`.
720 // Since it does not, this must be a transitive failure.
721 try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
722 }
723 return error.AnalysisFail;
724 },
725 error.OutOfMemory => return error.OutOfMemory,
726 };
727 errdefer air.deinit(gpa);799 errdefer air.deinit(gpa);
728800
729 if (func_outdated) {801 const ies_outdated = func_outdated and
730 if (!func.analysisUnordered(ip).inferred_error_set or func.resolvedErrorSetUnordered(ip) != old_resolved_ies) {802 (!func.analysisUnordered(ip).inferred_error_set or func.resolvedErrorSetUnordered(ip) != old_resolved_ies);
731 log.debug("func IES invalidated ('{d}')", .{@intFromEnum(func_index)});
732 try zcu.markDependeeOutdated(.{ .interned = func_index });
733 } else {
734 log.debug("func IES up-to-date ('{d}')", .{@intFromEnum(func_index)});
735 try zcu.markPoDependeeUpToDate(.{ .interned = func_index });
736 }
737 }
738803
739 const comp = zcu.comp;804 const comp = zcu.comp;
740805
...@@ -743,13 +808,15 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter...@@ -743,13 +808,15 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
743808
744 if (comp.bin_file == null and zcu.llvm_object == null and !dump_air and !dump_llvm_ir) {809 if (comp.bin_file == null and zcu.llvm_object == null and !dump_air and !dump_llvm_ir) {
745 air.deinit(gpa);810 air.deinit(gpa);
746 return;811 return .{ .ies_outdated = ies_outdated };
747 }812 }
748813
749 try comp.queueJob(.{ .codegen_func = .{814 try comp.queueJob(.{ .codegen_func = .{
750 .func = func_index,815 .func = func_index,
751 .air = air,816 .air = air,
752 } });817 } });
818
819 return .{ .ies_outdated = ies_outdated };
753}820}
754821
755/// Takes ownership of `air`, even on error.822/// Takes ownership of `air`, even on error.
...@@ -824,7 +891,7 @@ pub fn linkerUpdateFunc(pt: Zcu.PerThread, func_index: InternPool.Index, air: Ai...@@ -824,7 +891,7 @@ pub fn linkerUpdateFunc(pt: Zcu.PerThread, func_index: InternPool.Index, air: Ai
824 "unable to codegen: {s}",891 "unable to codegen: {s}",
825 .{@errorName(err)},892 .{@errorName(err)},
826 ));893 ));
827 try zcu.retryable_failures.append(zcu.gpa, InternPool.AnalUnit.wrap(.{ .func = func_index }));894 try zcu.retryable_failures.append(zcu.gpa, AnalUnit.wrap(.{ .func = func_index }));
828 },895 },
829 };896 };
830 } else if (zcu.llvm_object) |llvm_object| {897 } else if (zcu.llvm_object) |llvm_object| {
...@@ -848,6 +915,7 @@ fn createFileRootStruct(...@@ -848,6 +915,7 @@ fn createFileRootStruct(
848 pt: Zcu.PerThread,915 pt: Zcu.PerThread,
849 file_index: Zcu.File.Index,916 file_index: Zcu.File.Index,
850 namespace_index: Zcu.Namespace.Index,917 namespace_index: Zcu.Namespace.Index,
918 replace_existing: bool,
851) Allocator.Error!InternPool.Index {919) Allocator.Error!InternPool.Index {
852 const zcu = pt.zcu;920 const zcu = pt.zcu;
853 const gpa = zcu.gpa;921 const gpa = zcu.gpa;
...@@ -891,7 +959,7 @@ fn createFileRootStruct(...@@ -891,7 +959,7 @@ fn createFileRootStruct(
891 .zir_index = tracked_inst,959 .zir_index = tracked_inst,
892 .captures = &.{},960 .captures = &.{},
893 } },961 } },
894 })) {962 }, replace_existing)) {
895 .existing => unreachable, // we wouldn't be analysing the file root if this type existed963 .existing => unreachable, // we wouldn't be analysing the file root if this type existed
896 .wip => |wip| wip,964 .wip => |wip| wip,
897 };965 };
...@@ -904,7 +972,7 @@ fn createFileRootStruct(...@@ -904,7 +972,7 @@ fn createFileRootStruct(
904 if (zcu.comp.incremental) {972 if (zcu.comp.incremental) {
905 try ip.addDependency(973 try ip.addDependency(
906 gpa,974 gpa,
907 InternPool.AnalUnit.wrap(.{ .cau = new_cau_index }),975 AnalUnit.wrap(.{ .cau = new_cau_index }),
908 .{ .src_hash = tracked_inst },976 .{ .src_hash = tracked_inst },
909 );977 );
910 }978 }
...@@ -920,66 +988,42 @@ fn createFileRootStruct(...@@ -920,66 +988,42 @@ fn createFileRootStruct(
920 return wip_ty.finish(ip, new_cau_index.toOptional(), namespace_index);988 return wip_ty.finish(ip, new_cau_index.toOptional(), namespace_index);
921}989}
922990
923/// Re-analyze the root type of a file on an incremental update.991/// Re-scan the namespace of a file's root struct type on an incremental update.
924/// If `type_outdated`, the struct type itself is considered outdated and is992/// The file must have successfully populated ZIR.
925/// reconstructed at a new InternPool index. Otherwise, the namespace is just993/// If the file's root struct type is not populated (the file is unreferenced), nothing is done.
926/// re-analyzed. Returns whether the decl's tyval was invalidated.994/// This is called by `updateZirRefs` for all updated files before the main work loop.
927/// Returns `error.AnalysisFail` if the file has an error.995/// This function does not perform any semantic analysis.
928fn semaFileUpdate(pt: Zcu.PerThread, file_index: Zcu.File.Index, type_outdated: bool) Zcu.SemaError!bool {996fn updateFileNamespace(pt: Zcu.PerThread, file_index: Zcu.File.Index) Allocator.Error!void {
929 const zcu = pt.zcu;997 const zcu = pt.zcu;
930 const ip = &zcu.intern_pool;998
931 const file = zcu.fileByIndex(file_index);999 const file = zcu.fileByIndex(file_index);
1000 assert(file.status == .success_zir);
932 const file_root_type = zcu.fileRootType(file_index);1001 const file_root_type = zcu.fileRootType(file_index);
933 const namespace_index = Type.fromInterned(file_root_type).getNamespaceIndex(zcu);1002 if (file_root_type == .none) return;
9341003
935 assert(file_root_type != .none);1004 log.debug("updateFileNamespace mod={s} sub_file_path={s}", .{
936
937 log.debug("semaFileUpdate mod={s} sub_file_path={s} type_outdated={}", .{
938 file.mod.fully_qualified_name,1005 file.mod.fully_qualified_name,
939 file.sub_file_path,1006 file.sub_file_path,
940 type_outdated,
941 });1007 });
9421008
943 if (file.status != .success_zir) {1009 const namespace_index = Type.fromInterned(file_root_type).getNamespaceIndex(zcu);
944 return error.AnalysisFail;1010 const decls = decls: {
945 }1011 const extended = file.zir.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended;
9461012 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
947 if (type_outdated) {1013
948 // Invalidate the existing type, reusing its namespace.1014 var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).Struct.fields.len;
949 const file_root_type_cau = ip.loadStructType(file_root_type).cau.unwrap().?;1015 extra_index += @intFromBool(small.has_fields_len);
950 ip.removeDependenciesForDepender(1016 const decls_len = if (small.has_decls_len) blk: {
951 zcu.gpa,1017 const decls_len = file.zir.extra[extra_index];
952 InternPool.AnalUnit.wrap(.{ .cau = file_root_type_cau }),1018 extra_index += 1;
953 );1019 break :blk decls_len;
954 ip.remove(pt.tid, file_root_type);1020 } else 0;
955 _ = try pt.createFileRootStruct(file_index, namespace_index);1021 break :decls file.zir.bodySlice(extra_index, decls_len);
956 return true;1022 };
957 }1023 try pt.scanNamespace(namespace_index, decls);
9581024 zcu.namespacePtr(namespace_index).generation = zcu.generation;
959 // Only the struct's namespace is outdated.
960 // Preserve the type - just scan the namespace again.
961
962 const extended = file.zir.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended;
963 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
964
965 var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).Struct.fields.len;
966 extra_index += @intFromBool(small.has_fields_len);
967 const decls_len = if (small.has_decls_len) blk: {
968 const decls_len = file.zir.extra[extra_index];
969 extra_index += 1;
970 break :blk decls_len;
971 } else 0;
972 const decls = file.zir.bodySlice(extra_index, decls_len);
973
974 if (!type_outdated) {
975 try pt.scanNamespace(namespace_index, decls);
976 }
977
978 return false;
979}1025}
9801026
981/// Regardless of the file status, will create a `Decl` if none exists so that we can track
982/// dependencies and re-analyze when the file becomes outdated.
983fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {1027fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {
984 const tracy = trace(@src());1028 const tracy = trace(@src());
985 defer tracy.end();1029 defer tracy.end();
...@@ -998,8 +1042,9 @@ fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {...@@ -998,8 +1042,9 @@ fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {
998 .parent = .none,1042 .parent = .none,
999 .owner_type = undefined, // set in `createFileRootStruct`1043 .owner_type = undefined, // set in `createFileRootStruct`
1000 .file_scope = file_index,1044 .file_scope = file_index,
1045 .generation = zcu.generation,
1001 });1046 });
1002 const struct_ty = try pt.createFileRootStruct(file_index, new_namespace_index);1047 const struct_ty = try pt.createFileRootStruct(file_index, new_namespace_index, false);
1003 errdefer zcu.intern_pool.remove(pt.tid, struct_ty);1048 errdefer zcu.intern_pool.remove(pt.tid, struct_ty);
10041049
1005 switch (zcu.comp.cache_use) {1050 switch (zcu.comp.cache_use) {
...@@ -1049,10 +1094,10 @@ fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult {...@@ -1049,10 +1094,10 @@ fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult {
1049 const gpa = zcu.gpa;1094 const gpa = zcu.gpa;
1050 const ip = &zcu.intern_pool;1095 const ip = &zcu.intern_pool;
10511096
1052 const anal_unit = InternPool.AnalUnit.wrap(.{ .cau = cau_index });1097 const anal_unit = AnalUnit.wrap(.{ .cau = cau_index });
10531098
1054 const cau = ip.getCau(cau_index);1099 const cau = ip.getCau(cau_index);
1055 const inst_info = cau.zir_index.resolveFull(ip);1100 const inst_info = cau.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
1056 const file = zcu.fileByIndex(inst_info.file);1101 const file = zcu.fileByIndex(inst_info.file);
1057 const zir = file.zir;1102 const zir = file.zir;
10581103
...@@ -1071,9 +1116,10 @@ fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult {...@@ -1071,9 +1116,10 @@ fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult {
1071 },1116 },
1072 .type => |ty| {1117 .type => |ty| {
1073 // This is an incremental update, and this type is being re-analyzed because it is outdated.1118 // This is an incremental update, and this type is being re-analyzed because it is outdated.
1074 // The type must be recreated at a new `InternPool.Index`.1119 // Create a new type in its place, and mark the old one as outdated so that use sites will
1075 // Remove it from the InternPool and mark it outdated so that creation sites are re-analyzed.1120 // be re-analyzed and discover an up-to-date type.
1076 ip.remove(pt.tid, ty);1121 const new_ty = try pt.ensureTypeUpToDate(ty, true);
1122 assert(new_ty != ty);
1077 return .{1123 return .{
1078 .invalidate_decl_val = true,1124 .invalidate_decl_val = true,
1079 .invalidate_decl_ref = true,1125 .invalidate_decl_ref = true,
...@@ -1919,21 +1965,25 @@ const ScanDeclIter = struct {...@@ -1919,21 +1965,25 @@ const ScanDeclIter = struct {
1919 .@"comptime" => cau: {1965 .@"comptime" => cau: {
1920 const cau = existing_cau orelse try ip.createComptimeCau(gpa, pt.tid, tracked_inst, namespace_index);1966 const cau = existing_cau orelse try ip.createComptimeCau(gpa, pt.tid, tracked_inst, namespace_index);
19211967
1922 // For a `comptime` declaration, whether to re-analyze is based solely on whether the1968 try namespace.other_decls.append(gpa, cau);
1923 // `Cau` is outdated. So, add this one to `outdated` and `outdated_ready` if not already.1969
1924 const unit = InternPool.AnalUnit.wrap(.{ .cau = cau });1970 if (existing_cau == null) {
1925 if (zcu.potentially_outdated.fetchSwapRemove(unit)) |kv| {1971 // For a `comptime` declaration, whether to analyze is based solely on whether the
1926 try zcu.outdated.ensureUnusedCapacity(gpa, 1);1972 // `Cau` is outdated. So, add this one to `outdated` and `outdated_ready` if not already.
1927 try zcu.outdated_ready.ensureUnusedCapacity(gpa, 1);1973 const unit = AnalUnit.wrap(.{ .cau = cau });
1928 zcu.outdated.putAssumeCapacityNoClobber(unit, kv.value);1974 if (zcu.potentially_outdated.fetchSwapRemove(unit)) |kv| {
1929 if (kv.value == 0) { // no PO deps1975 try zcu.outdated.ensureUnusedCapacity(gpa, 1);
1976 try zcu.outdated_ready.ensureUnusedCapacity(gpa, 1);
1977 zcu.outdated.putAssumeCapacityNoClobber(unit, kv.value);
1978 if (kv.value == 0) { // no PO deps
1979 zcu.outdated_ready.putAssumeCapacityNoClobber(unit, {});
1980 }
1981 } else if (!zcu.outdated.contains(unit)) {
1982 try zcu.outdated.ensureUnusedCapacity(gpa, 1);
1983 try zcu.outdated_ready.ensureUnusedCapacity(gpa, 1);
1984 zcu.outdated.putAssumeCapacityNoClobber(unit, 0);
1930 zcu.outdated_ready.putAssumeCapacityNoClobber(unit, {});1985 zcu.outdated_ready.putAssumeCapacityNoClobber(unit, {});
1931 }1986 }
1932 } else if (!zcu.outdated.contains(unit)) {
1933 try zcu.outdated.ensureUnusedCapacity(gpa, 1);
1934 try zcu.outdated_ready.ensureUnusedCapacity(gpa, 1);
1935 zcu.outdated.putAssumeCapacityNoClobber(unit, 0);
1936 zcu.outdated_ready.putAssumeCapacityNoClobber(unit, {});
1937 }1987 }
19381988
1939 break :cau .{ cau, true };1989 break :cau .{ cau, true };
...@@ -1951,6 +2001,9 @@ const ScanDeclIter = struct {...@@ -1951,6 +2001,9 @@ const ScanDeclIter = struct {
1951 const want_analysis = switch (kind) {2001 const want_analysis = switch (kind) {
1952 .@"comptime" => unreachable,2002 .@"comptime" => unreachable,
1953 .@"usingnamespace" => a: {2003 .@"usingnamespace" => a: {
2004 if (comp.incremental) {
2005 @panic("'usingnamespace' is not supported by incremental compilation");
2006 }
1954 if (declaration.flags.is_pub) {2007 if (declaration.flags.is_pub) {
1955 try namespace.pub_usingnamespace.append(gpa, nav);2008 try namespace.pub_usingnamespace.append(gpa, nav);
1956 } else {2009 } else {
...@@ -1989,7 +2042,7 @@ const ScanDeclIter = struct {...@@ -1989,7 +2042,7 @@ const ScanDeclIter = struct {
1989 },2042 },
1990 };2043 };
19912044
1992 if (want_analysis or declaration.flags.is_export) {2045 if (existing_cau == null and (want_analysis or declaration.flags.is_export)) {
1993 log.debug(2046 log.debug(
1994 "scanDecl queue analyze_cau file='{s}' cau_index={d}",2047 "scanDecl queue analyze_cau file='{s}' cau_index={d}",
1995 .{ namespace.fileScope(zcu).sub_file_path, cau },2048 .{ namespace.fileScope(zcu).sub_file_path, cau },
...@@ -2009,9 +2062,9 @@ fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!...@@ -2009,9 +2062,9 @@ fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!
2009 const gpa = zcu.gpa;2062 const gpa = zcu.gpa;
2010 const ip = &zcu.intern_pool;2063 const ip = &zcu.intern_pool;
20112064
2012 const anal_unit = InternPool.AnalUnit.wrap(.{ .func = func_index });2065 const anal_unit = AnalUnit.wrap(.{ .func = func_index });
2013 const func = zcu.funcInfo(func_index);2066 const func = zcu.funcInfo(func_index);
2014 const inst_info = func.zir_body_inst.resolveFull(ip);2067 const inst_info = func.zir_body_inst.resolveFull(ip) orelse return error.AnalysisFail;
2015 const file = zcu.fileByIndex(inst_info.file);2068 const file = zcu.fileByIndex(inst_info.file);
2016 const zir = file.zir;2069 const zir = file.zir;
20172070
...@@ -2097,7 +2150,7 @@ fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!...@@ -2097,7 +2150,7 @@ fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!
2097 };2150 };
2098 defer inner_block.instructions.deinit(gpa);2151 defer inner_block.instructions.deinit(gpa);
20992152
2100 const fn_info = sema.code.getFnInfo(func.zirBodyInstUnordered(ip).resolve(ip));2153 const fn_info = sema.code.getFnInfo(func.zirBodyInstUnordered(ip).resolve(ip) orelse return error.AnalysisFail);
21012154
2102 // Here we are performing "runtime semantic analysis" for a function body, which means2155 // Here we are performing "runtime semantic analysis" for a function body, which means
2103 // we must map the parameter ZIR instructions to `arg` AIR instructions.2156 // we must map the parameter ZIR instructions to `arg` AIR instructions.
...@@ -2395,7 +2448,7 @@ fn processExportsInner(...@@ -2395,7 +2448,7 @@ fn processExportsInner(
2395 const nav = ip.getNav(nav_index);2448 const nav = ip.getNav(nav_index);
2396 if (zcu.failed_codegen.contains(nav_index)) break :failed true;2449 if (zcu.failed_codegen.contains(nav_index)) break :failed true;
2397 if (nav.analysis_owner.unwrap()) |cau| {2450 if (nav.analysis_owner.unwrap()) |cau| {
2398 const cau_unit = InternPool.AnalUnit.wrap(.{ .cau = cau });2451 const cau_unit = AnalUnit.wrap(.{ .cau = cau });
2399 if (zcu.failed_analysis.contains(cau_unit)) break :failed true;2452 if (zcu.failed_analysis.contains(cau_unit)) break :failed true;
2400 if (zcu.transitive_failed_analysis.contains(cau_unit)) break :failed true;2453 if (zcu.transitive_failed_analysis.contains(cau_unit)) break :failed true;
2401 }2454 }
...@@ -2405,7 +2458,7 @@ fn processExportsInner(...@@ -2405,7 +2458,7 @@ fn processExportsInner(
2405 };2458 };
2406 // If the value is a function, we also need to check if that function succeeded analysis.2459 // If the value is a function, we also need to check if that function succeeded analysis.
2407 if (val.typeOf(zcu).zigTypeTag(zcu) == .Fn) {2460 if (val.typeOf(zcu).zigTypeTag(zcu) == .Fn) {
2408 const func_unit = InternPool.AnalUnit.wrap(.{ .func = val.toIntern() });2461 const func_unit = AnalUnit.wrap(.{ .func = val.toIntern() });
2409 if (zcu.failed_analysis.contains(func_unit)) break :failed true;2462 if (zcu.failed_analysis.contains(func_unit)) break :failed true;
2410 if (zcu.transitive_failed_analysis.contains(func_unit)) break :failed true;2463 if (zcu.transitive_failed_analysis.contains(func_unit)) break :failed true;
2411 }2464 }
...@@ -2580,7 +2633,7 @@ pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void...@@ -2580,7 +2633,7 @@ pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void
2580 .{@errorName(err)},2633 .{@errorName(err)},
2581 ));2634 ));
2582 if (nav.analysis_owner.unwrap()) |cau| {2635 if (nav.analysis_owner.unwrap()) |cau| {
2583 try zcu.retryable_failures.append(zcu.gpa, InternPool.AnalUnit.wrap(.{ .cau = cau }));2636 try zcu.retryable_failures.append(zcu.gpa, AnalUnit.wrap(.{ .cau = cau }));
2584 } else {2637 } else {
2585 // TODO: we don't have a way to indicate that this failure is retryable!2638 // TODO: we don't have a way to indicate that this failure is retryable!
2586 // Since these are really rare, we could as a cop-out retry the whole build next update.2639 // Since these are really rare, we could as a cop-out retry the whole build next update.
...@@ -2693,7 +2746,7 @@ pub fn reportRetryableFileError(...@@ -2693,7 +2746,7 @@ pub fn reportRetryableFileError(
2693 gop.value_ptr.* = err_msg;2746 gop.value_ptr.* = err_msg;
2694}2747}
26952748
2696/// Shortcut for calling `intern_pool.get`.2749///Shortcut for calling `intern_pool.get`.
2697pub fn intern(pt: Zcu.PerThread, key: InternPool.Key) Allocator.Error!InternPool.Index {2750pub fn intern(pt: Zcu.PerThread, key: InternPool.Key) Allocator.Error!InternPool.Index {
2698 return pt.zcu.intern_pool.get(pt.zcu.gpa, pt.tid, key);2751 return pt.zcu.intern_pool.get(pt.zcu.gpa, pt.tid, key);
2699}2752}
...@@ -3278,6 +3331,532 @@ pub fn navAlignment(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) InternPo...@@ -3278,6 +3331,532 @@ pub fn navAlignment(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) InternPo
3278 return Value.fromInterned(r.val).typeOf(zcu).abiAlignment(pt);3331 return Value.fromInterned(r.val).typeOf(zcu).abiAlignment(pt);
3279}3332}
32803333
3334/// Given a container type requiring resolution, ensures that it is up-to-date.
3335/// If not, the type is recreated at a new `InternPool.Index`.
3336/// The new index is returned. This is the same as the old index if the fields were up-to-date.
3337/// If `already_updating` is set, assumes the type is already outdated and undergoing re-analysis rather than checking `zcu.outdated`.
3338pub fn ensureTypeUpToDate(pt: Zcu.PerThread, ty: InternPool.Index, already_updating: bool) Zcu.SemaError!InternPool.Index {
3339 const zcu = pt.zcu;
3340 const ip = &zcu.intern_pool;
3341 switch (ip.indexToKey(ty)) {
3342 .struct_type => |key| {
3343 const struct_obj = ip.loadStructType(ty);
3344 const outdated = already_updating or o: {
3345 const anal_unit = AnalUnit.wrap(.{ .cau = struct_obj.cau.unwrap().? });
3346 const o = zcu.outdated.swapRemove(anal_unit) or
3347 zcu.potentially_outdated.swapRemove(anal_unit);
3348 if (o) {
3349 _ = zcu.outdated_ready.swapRemove(anal_unit);
3350 try zcu.markDependeeOutdated(.marked_po, .{ .interned = ty });
3351 }
3352 break :o o;
3353 };
3354 if (!outdated) return ty;
3355 return pt.recreateStructType(ty, key, struct_obj);
3356 },
3357 .union_type => |key| {
3358 const union_obj = ip.loadUnionType(ty);
3359 const outdated = already_updating or o: {
3360 const anal_unit = AnalUnit.wrap(.{ .cau = union_obj.cau });
3361 const o = zcu.outdated.swapRemove(anal_unit) or
3362 zcu.potentially_outdated.swapRemove(anal_unit);
3363 if (o) {
3364 _ = zcu.outdated_ready.swapRemove(anal_unit);
3365 try zcu.markDependeeOutdated(.marked_po, .{ .interned = ty });
3366 }
3367 break :o o;
3368 };
3369 if (!outdated) return ty;
3370 return pt.recreateUnionType(ty, key, union_obj);
3371 },
3372 .enum_type => |key| {
3373 const enum_obj = ip.loadEnumType(ty);
3374 const outdated = already_updating or o: {
3375 const anal_unit = AnalUnit.wrap(.{ .cau = enum_obj.cau.unwrap().? });
3376 const o = zcu.outdated.swapRemove(anal_unit) or
3377 zcu.potentially_outdated.swapRemove(anal_unit);
3378 if (o) {
3379 _ = zcu.outdated_ready.swapRemove(anal_unit);
3380 try zcu.markDependeeOutdated(.marked_po, .{ .interned = ty });
3381 }
3382 break :o o;
3383 };
3384 if (!outdated) return ty;
3385 return pt.recreateEnumType(ty, key, enum_obj);
3386 },
3387 .opaque_type => {
3388 assert(!already_updating);
3389 return ty;
3390 },
3391 else => unreachable,
3392 }
3393}
3394
3395fn recreateStructType(
3396 pt: Zcu.PerThread,
3397 ty: InternPool.Index,
3398 full_key: InternPool.Key.NamespaceType,
3399 struct_obj: InternPool.LoadedStructType,
3400) Zcu.SemaError!InternPool.Index {
3401 const zcu = pt.zcu;
3402 const gpa = zcu.gpa;
3403 const ip = &zcu.intern_pool;
3404
3405 const key = switch (full_key) {
3406 .reified => unreachable, // never outdated
3407 .empty_struct => unreachable, // never outdated
3408 .generated_tag => unreachable, // not a struct
3409 .declared => |d| d,
3410 };
3411
3412 if (@intFromEnum(ty) <= InternPool.static_len) {
3413 @panic("TODO: recreate resolved builtin type");
3414 }
3415
3416 const inst_info = key.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
3417 const file = zcu.fileByIndex(inst_info.file);
3418 if (file.status != .success_zir) return error.AnalysisFail;
3419 const zir = file.zir;
3420
3421 assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
3422 const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended;
3423 assert(extended.opcode == .struct_decl);
3424 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
3425 const extra = zir.extraData(Zir.Inst.StructDecl, extended.operand);
3426 var extra_index = extra.end;
3427
3428 const captures_len = if (small.has_captures_len) blk: {
3429 const captures_len = zir.extra[extra_index];
3430 extra_index += 1;
3431 break :blk captures_len;
3432 } else 0;
3433 const fields_len = if (small.has_fields_len) blk: {
3434 const fields_len = zir.extra[extra_index];
3435 extra_index += 1;
3436 break :blk fields_len;
3437 } else 0;
3438
3439 if (captures_len != key.captures.owned.len) return error.AnalysisFail;
3440 if (fields_len != struct_obj.field_types.len) return error.AnalysisFail;
3441
3442 // The old type will be unused, so drop its dependency information.
3443 ip.removeDependenciesForDepender(gpa, AnalUnit.wrap(.{ .cau = struct_obj.cau.unwrap().? }));
3444
3445 const namespace_index = struct_obj.namespace.unwrap().?;
3446
3447 const wip_ty = switch (try ip.getStructType(gpa, pt.tid, .{
3448 .layout = small.layout,
3449 .fields_len = fields_len,
3450 .known_non_opv = small.known_non_opv,
3451 .requires_comptime = if (small.known_comptime_only) .yes else .unknown,
3452 .is_tuple = small.is_tuple,
3453 .any_comptime_fields = small.any_comptime_fields,
3454 .any_default_inits = small.any_default_inits,
3455 .inits_resolved = false,
3456 .any_aligned_fields = small.any_aligned_fields,
3457 .key = .{ .declared_owned_captures = .{
3458 .zir_index = key.zir_index,
3459 .captures = key.captures.owned,
3460 } },
3461 }, true)) {
3462 .wip => |wip| wip,
3463 .existing => unreachable, // we passed `replace_existing`
3464 };
3465 errdefer wip_ty.cancel(ip, pt.tid);
3466
3467 wip_ty.setName(ip, struct_obj.name);
3468 const new_cau_index = try ip.createTypeCau(gpa, pt.tid, key.zir_index, namespace_index, wip_ty.index);
3469 try ip.addDependency(
3470 gpa,
3471 AnalUnit.wrap(.{ .cau = new_cau_index }),
3472 .{ .src_hash = key.zir_index },
3473 );
3474 zcu.namespacePtr(namespace_index).owner_type = wip_ty.index;
3475 // No need to re-scan the namespace -- `zirStructDecl` will ultimately do that if the type is still alive.
3476 try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
3477
3478 const new_ty = wip_ty.finish(ip, new_cau_index.toOptional(), namespace_index);
3479 if (inst_info.inst == .main_struct_inst) {
3480 // This is the root type of a file! Update the reference.
3481 zcu.setFileRootType(inst_info.file, new_ty);
3482 }
3483 return new_ty;
3484}
3485
3486fn recreateUnionType(
3487 pt: Zcu.PerThread,
3488 ty: InternPool.Index,
3489 full_key: InternPool.Key.NamespaceType,
3490 union_obj: InternPool.LoadedUnionType,
3491) Zcu.SemaError!InternPool.Index {
3492 const zcu = pt.zcu;
3493 const gpa = zcu.gpa;
3494 const ip = &zcu.intern_pool;
3495
3496 const key = switch (full_key) {
3497 .reified => unreachable, // never outdated
3498 .empty_struct => unreachable, // never outdated
3499 .generated_tag => unreachable, // not a union
3500 .declared => |d| d,
3501 };
3502
3503 if (@intFromEnum(ty) <= InternPool.static_len) {
3504 @panic("TODO: recreate resolved builtin type");
3505 }
3506
3507 const inst_info = key.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
3508 const file = zcu.fileByIndex(inst_info.file);
3509 if (file.status != .success_zir) return error.AnalysisFail;
3510 const zir = file.zir;
3511
3512 assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
3513 const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended;
3514 assert(extended.opcode == .union_decl);
3515 const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small);
3516 const extra = zir.extraData(Zir.Inst.UnionDecl, extended.operand);
3517 var extra_index = extra.end;
3518
3519 extra_index += @intFromBool(small.has_tag_type);
3520 const captures_len = if (small.has_captures_len) blk: {
3521 const captures_len = zir.extra[extra_index];
3522 extra_index += 1;
3523 break :blk captures_len;
3524 } else 0;
3525 extra_index += @intFromBool(small.has_body_len);
3526 const fields_len = if (small.has_fields_len) blk: {
3527 const fields_len = zir.extra[extra_index];
3528 extra_index += 1;
3529 break :blk fields_len;
3530 } else 0;
3531
3532 if (captures_len != key.captures.owned.len) return error.AnalysisFail;
3533 if (fields_len != union_obj.field_types.len) return error.AnalysisFail;
3534
3535 // The old type will be unused, so drop its dependency information.
3536 ip.removeDependenciesForDepender(gpa, AnalUnit.wrap(.{ .cau = union_obj.cau }));
3537
3538 const namespace_index = union_obj.namespace;
3539
3540 const wip_ty = switch (try ip.getUnionType(gpa, pt.tid, .{
3541 .flags = .{
3542 .layout = small.layout,
3543 .status = .none,
3544 .runtime_tag = if (small.has_tag_type or small.auto_enum_tag)
3545 .tagged
3546 else if (small.layout != .auto)
3547 .none
3548 else switch (true) { // TODO
3549 true => .safety,
3550 false => .none,
3551 },
3552 .any_aligned_fields = small.any_aligned_fields,
3553 .requires_comptime = .unknown,
3554 .assumed_runtime_bits = false,
3555 .assumed_pointer_aligned = false,
3556 .alignment = .none,
3557 },
3558 .fields_len = fields_len,
3559 .enum_tag_ty = .none, // set later
3560 .field_types = &.{}, // set later
3561 .field_aligns = &.{}, // set later
3562 .key = .{ .declared_owned_captures = .{
3563 .zir_index = key.zir_index,
3564 .captures = key.captures.owned,
3565 } },
3566 }, true)) {
3567 .wip => |wip| wip,
3568 .existing => unreachable, // we passed `replace_existing`
3569 };
3570 errdefer wip_ty.cancel(ip, pt.tid);
3571
3572 wip_ty.setName(ip, union_obj.name);
3573 const new_cau_index = try ip.createTypeCau(gpa, pt.tid, key.zir_index, namespace_index, wip_ty.index);
3574 try ip.addDependency(
3575 gpa,
3576 AnalUnit.wrap(.{ .cau = new_cau_index }),
3577 .{ .src_hash = key.zir_index },
3578 );
3579 zcu.namespacePtr(namespace_index).owner_type = wip_ty.index;
3580 // No need to re-scan the namespace -- `zirUnionDecl` will ultimately do that if the type is still alive.
3581 try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
3582 return wip_ty.finish(ip, new_cau_index.toOptional(), namespace_index);
3583}
3584
3585fn recreateEnumType(
3586 pt: Zcu.PerThread,
3587 ty: InternPool.Index,
3588 full_key: InternPool.Key.NamespaceType,
3589 enum_obj: InternPool.LoadedEnumType,
3590) Zcu.SemaError!InternPool.Index {
3591 const zcu = pt.zcu;
3592 const gpa = zcu.gpa;
3593 const ip = &zcu.intern_pool;
3594
3595 const key = switch (full_key) {
3596 .reified => unreachable, // never outdated
3597 .empty_struct => unreachable, // never outdated
3598 .generated_tag => unreachable, // never outdated
3599 .declared => |d| d,
3600 };
3601
3602 if (@intFromEnum(ty) <= InternPool.static_len) {
3603 @panic("TODO: recreate resolved builtin type");
3604 }
3605
3606 const inst_info = key.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
3607 const file = zcu.fileByIndex(inst_info.file);
3608 if (file.status != .success_zir) return error.AnalysisFail;
3609 const zir = file.zir;
3610
3611 assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
3612 const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended;
3613 assert(extended.opcode == .enum_decl);
3614 const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small);
3615 const extra = zir.extraData(Zir.Inst.EnumDecl, extended.operand);
3616 var extra_index = extra.end;
3617
3618 const tag_type_ref = if (small.has_tag_type) blk: {
3619 const tag_type_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
3620 extra_index += 1;
3621 break :blk tag_type_ref;
3622 } else .none;
3623
3624 const captures_len = if (small.has_captures_len) blk: {
3625 const captures_len = zir.extra[extra_index];
3626 extra_index += 1;
3627 break :blk captures_len;
3628 } else 0;
3629
3630 const body_len = if (small.has_body_len) blk: {
3631 const body_len = zir.extra[extra_index];
3632 extra_index += 1;
3633 break :blk body_len;
3634 } else 0;
3635
3636 const fields_len = if (small.has_fields_len) blk: {
3637 const fields_len = zir.extra[extra_index];
3638 extra_index += 1;
3639 break :blk fields_len;
3640 } else 0;
3641
3642 const decls_len = if (small.has_decls_len) blk: {
3643 const decls_len = zir.extra[extra_index];
3644 extra_index += 1;
3645 break :blk decls_len;
3646 } else 0;
3647
3648 if (captures_len != key.captures.owned.len) return error.AnalysisFail;
3649 if (fields_len != enum_obj.names.len) return error.AnalysisFail;
3650
3651 extra_index += captures_len;
3652 extra_index += decls_len;
3653
3654 const body = zir.bodySlice(extra_index, body_len);
3655 extra_index += body.len;
3656
3657 const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable;
3658 const body_end = extra_index;
3659 extra_index += bit_bags_count;
3660
3661 const any_values = for (zir.extra[body_end..][0..bit_bags_count]) |bag| {
3662 if (bag != 0) break true;
3663 } else false;
3664
3665 // The old type will be unused, so drop its dependency information.
3666 ip.removeDependenciesForDepender(gpa, AnalUnit.wrap(.{ .cau = enum_obj.cau.unwrap().? }));
3667
3668 const namespace_index = enum_obj.namespace;
3669
3670 const wip_ty = switch (try ip.getEnumType(gpa, pt.tid, .{
3671 .has_values = any_values,
3672 .tag_mode = if (small.nonexhaustive)
3673 .nonexhaustive
3674 else if (tag_type_ref == .none)
3675 .auto
3676 else
3677 .explicit,
3678 .fields_len = fields_len,
3679 .key = .{ .declared_owned_captures = .{
3680 .zir_index = key.zir_index,
3681 .captures = key.captures.owned,
3682 } },
3683 }, true)) {
3684 .wip => |wip| wip,
3685 .existing => unreachable, // we passed `replace_existing`
3686 };
3687 var done = true;
3688 errdefer if (!done) wip_ty.cancel(ip, pt.tid);
3689
3690 wip_ty.setName(ip, enum_obj.name);
3691
3692 const new_cau_index = try ip.createTypeCau(gpa, pt.tid, key.zir_index, namespace_index, wip_ty.index);
3693
3694 zcu.namespacePtr(namespace_index).owner_type = wip_ty.index;
3695 // No need to re-scan the namespace -- `zirEnumDecl` will ultimately do that if the type is still alive.
3696
3697 wip_ty.prepare(ip, new_cau_index, namespace_index);
3698 done = true;
3699
3700 Sema.resolveDeclaredEnum(
3701 pt,
3702 wip_ty,
3703 inst_info.inst,
3704 key.zir_index,
3705 namespace_index,
3706 enum_obj.name,
3707 new_cau_index,
3708 small,
3709 body,
3710 tag_type_ref,
3711 any_values,
3712 fields_len,
3713 zir,
3714 body_end,
3715 ) catch |err| switch (err) {
3716 error.GenericPoison => unreachable,
3717 error.ComptimeBreak => unreachable,
3718 error.ComptimeReturn => unreachable,
3719 error.AnalysisFail, error.OutOfMemory => |e| return e,
3720 };
3721
3722 return wip_ty.index;
3723}
3724
3725/// Given a namespace, re-scan its declarations from the type definition if they have not
3726/// yet been re-scanned on this update.
3727/// If the type declaration instruction has been lost, returns `error.AnalysisFail`.
3728/// This will effectively short-circuit the caller, which will be semantic analysis of a
3729/// guaranteed-unreferenced `AnalUnit`, to trigger a transitive analysis error.
3730pub fn ensureNamespaceUpToDate(pt: Zcu.PerThread, namespace_index: Zcu.Namespace.Index) Zcu.SemaError!void {
3731 const zcu = pt.zcu;
3732 const ip = &zcu.intern_pool;
3733 const namespace = zcu.namespacePtr(namespace_index);
3734
3735 if (namespace.generation == zcu.generation) return;
3736
3737 const Container = enum { @"struct", @"union", @"enum", @"opaque" };
3738 const container: Container, const full_key = switch (ip.indexToKey(namespace.owner_type)) {
3739 .struct_type => |k| .{ .@"struct", k },
3740 .union_type => |k| .{ .@"union", k },
3741 .enum_type => |k| .{ .@"enum", k },
3742 .opaque_type => |k| .{ .@"opaque", k },
3743 else => unreachable, // namespaces are owned by a container type
3744 };
3745
3746 const key = switch (full_key) {
3747 .reified, .empty_struct, .generated_tag => {
3748 // Namespace always empty, so up-to-date.
3749 namespace.generation = zcu.generation;
3750 return;
3751 },
3752 .declared => |d| d,
3753 };
3754
3755 // Namespace outdated -- re-scan the type if necessary.
3756
3757 const inst_info = key.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
3758 const file = zcu.fileByIndex(inst_info.file);
3759 if (file.status != .success_zir) return error.AnalysisFail;
3760 const zir = file.zir;
3761
3762 assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
3763 const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended;
3764
3765 const decls = switch (container) {
3766 .@"struct" => decls: {
3767 assert(extended.opcode == .struct_decl);
3768 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
3769 const extra = zir.extraData(Zir.Inst.StructDecl, extended.operand);
3770 var extra_index = extra.end;
3771 const captures_len = if (small.has_captures_len) blk: {
3772 const captures_len = zir.extra[extra_index];
3773 extra_index += 1;
3774 break :blk captures_len;
3775 } else 0;
3776 extra_index += @intFromBool(small.has_fields_len);
3777 const decls_len = if (small.has_decls_len) blk: {
3778 const decls_len = zir.extra[extra_index];
3779 extra_index += 1;
3780 break :blk decls_len;
3781 } else 0;
3782 extra_index += captures_len;
3783 if (small.has_backing_int) {
3784 const backing_int_body_len = zir.extra[extra_index];
3785 extra_index += 1; // backing_int_body_len
3786 if (backing_int_body_len == 0) {
3787 extra_index += 1; // backing_int_ref
3788 } else {
3789 extra_index += backing_int_body_len; // backing_int_body_inst
3790 }
3791 }
3792 break :decls zir.bodySlice(extra_index, decls_len);
3793 },
3794 .@"union" => decls: {
3795 assert(extended.opcode == .union_decl);
3796 const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small);
3797 const extra = zir.extraData(Zir.Inst.UnionDecl, extended.operand);
3798 var extra_index = extra.end;
3799 extra_index += @intFromBool(small.has_tag_type);
3800 const captures_len = if (small.has_captures_len) blk: {
3801 const captures_len = zir.extra[extra_index];
3802 extra_index += 1;
3803 break :blk captures_len;
3804 } else 0;
3805 extra_index += @intFromBool(small.has_body_len);
3806 extra_index += @intFromBool(small.has_fields_len);
3807 const decls_len = if (small.has_decls_len) blk: {
3808 const decls_len = zir.extra[extra_index];
3809 extra_index += 1;
3810 break :blk decls_len;
3811 } else 0;
3812 extra_index += captures_len;
3813 break :decls zir.bodySlice(extra_index, decls_len);
3814 },
3815 .@"enum" => decls: {
3816 assert(extended.opcode == .enum_decl);
3817 const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small);
3818 const extra = zir.extraData(Zir.Inst.EnumDecl, extended.operand);
3819 var extra_index = extra.end;
3820 extra_index += @intFromBool(small.has_tag_type);
3821 const captures_len = if (small.has_captures_len) blk: {
3822 const captures_len = zir.extra[extra_index];
3823 extra_index += 1;
3824 break :blk captures_len;
3825 } else 0;
3826 extra_index += @intFromBool(small.has_body_len);
3827 extra_index += @intFromBool(small.has_fields_len);
3828 const decls_len = if (small.has_decls_len) blk: {
3829 const decls_len = zir.extra[extra_index];
3830 extra_index += 1;
3831 break :blk decls_len;
3832 } else 0;
3833 extra_index += captures_len;
3834 break :decls zir.bodySlice(extra_index, decls_len);
3835 },
3836 .@"opaque" => decls: {
3837 assert(extended.opcode == .opaque_decl);
3838 const small: Zir.Inst.OpaqueDecl.Small = @bitCast(extended.small);
3839 const extra = zir.extraData(Zir.Inst.OpaqueDecl, extended.operand);
3840 var extra_index = extra.end;
3841 const captures_len = if (small.has_captures_len) blk: {
3842 const captures_len = zir.extra[extra_index];
3843 extra_index += 1;
3844 break :blk captures_len;
3845 } else 0;
3846 const decls_len = if (small.has_decls_len) blk: {
3847 const decls_len = zir.extra[extra_index];
3848 extra_index += 1;
3849 break :blk decls_len;
3850 } else 0;
3851 extra_index += captures_len;
3852 break :decls zir.bodySlice(extra_index, decls_len);
3853 },
3854 };
3855
3856 try pt.scanNamespace(namespace_index, decls);
3857 namespace.generation = zcu.generation;
3858}
3859
3281const Air = @import("../Air.zig");3860const Air = @import("../Air.zig");
3282const Allocator = std.mem.Allocator;3861const Allocator = std.mem.Allocator;
3283const assert = std.debug.assert;3862const assert = std.debug.assert;
...@@ -3290,6 +3869,7 @@ const builtin = @import("builtin");...@@ -3290,6 +3869,7 @@ const builtin = @import("builtin");
3290const Cache = std.Build.Cache;3869const Cache = std.Build.Cache;
3291const dev = @import("../dev.zig");3870const dev = @import("../dev.zig");
3292const InternPool = @import("../InternPool.zig");3871const InternPool = @import("../InternPool.zig");
3872const AnalUnit = InternPool.AnalUnit;
3293const isUpDir = @import("../introspect.zig").isUpDir;3873const isUpDir = @import("../introspect.zig").isUpDir;
3294const Liveness = @import("../Liveness.zig");3874const Liveness = @import("../Liveness.zig");
3295const log = std.log.scoped(.zcu);3875const log = std.log.scoped(.zcu);
src/codegen.zig+1-1
...@@ -98,7 +98,7 @@ pub fn generateLazyFunction(...@@ -98,7 +98,7 @@ pub fn generateLazyFunction(
98 debug_output: DebugInfoOutput,98 debug_output: DebugInfoOutput,
99) CodeGenError!Result {99) CodeGenError!Result {
100 const zcu = pt.zcu;100 const zcu = pt.zcu;
101 const file = Type.fromInterned(lazy_sym.ty).typeDeclInstAllowGeneratedTag(zcu).?.resolveFull(&zcu.intern_pool).file;101 const file = Type.fromInterned(lazy_sym.ty).typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(&zcu.intern_pool);
102 const target = zcu.fileByIndex(file).mod.resolved_target.result;102 const target = zcu.fileByIndex(file).mod.resolved_target.result;
103 switch (target_util.zigBackend(target, false)) {103 switch (target_util.zigBackend(target, false)) {
104 else => unreachable,104 else => unreachable,
src/codegen/c.zig+1-1
...@@ -2585,7 +2585,7 @@ pub fn genTypeDecl(...@@ -2585,7 +2585,7 @@ pub fn genTypeDecl(
2585 const ty = Type.fromInterned(index);2585 const ty = Type.fromInterned(index);
2586 _ = try renderTypePrefix(.flush, global_ctype_pool, zcu, writer, global_ctype, .suffix, .{});2586 _ = try renderTypePrefix(.flush, global_ctype_pool, zcu, writer, global_ctype, .suffix, .{});
2587 try writer.writeByte(';');2587 try writer.writeByte(';');
2588 const file_scope = ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFull(ip).file;2588 const file_scope = ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(ip);
2589 if (!zcu.fileByIndex(file_scope).mod.strip) try writer.print(" /* {} */", .{2589 if (!zcu.fileByIndex(file_scope).mod.strip) try writer.print(" /* {} */", .{
2590 ty.containerTypeName(ip).fmt(ip),2590 ty.containerTypeName(ip).fmt(ip),
2591 });2591 });
src/codegen/llvm.zig+3-3
...@@ -1959,7 +1959,7 @@ pub const Object = struct {...@@ -1959,7 +1959,7 @@ pub const Object = struct {
1959 );1959 );
1960 }1960 }
19611961
1962 const file = try o.getDebugFile(ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFull(ip).file);1962 const file = try o.getDebugFile(ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(ip));
1963 const scope = if (ty.getParentNamespace(zcu).unwrap()) |parent_namespace|1963 const scope = if (ty.getParentNamespace(zcu).unwrap()) |parent_namespace|
1964 try o.namespaceToDebugScope(parent_namespace)1964 try o.namespaceToDebugScope(parent_namespace)
1965 else1965 else
...@@ -2137,7 +2137,7 @@ pub const Object = struct {...@@ -2137,7 +2137,7 @@ pub const Object = struct {
2137 const name = try o.allocTypeName(ty);2137 const name = try o.allocTypeName(ty);
2138 defer gpa.free(name);2138 defer gpa.free(name);
21392139
2140 const file = try o.getDebugFile(ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFull(ip).file);2140 const file = try o.getDebugFile(ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(ip));
2141 const scope = if (ty.getParentNamespace(zcu).unwrap()) |parent_namespace|2141 const scope = if (ty.getParentNamespace(zcu).unwrap()) |parent_namespace|
2142 try o.namespaceToDebugScope(parent_namespace)2142 try o.namespaceToDebugScope(parent_namespace)
2143 else2143 else
...@@ -2772,7 +2772,7 @@ pub const Object = struct {...@@ -2772,7 +2772,7 @@ pub const Object = struct {
2772 fn makeEmptyNamespaceDebugType(o: *Object, ty: Type) !Builder.Metadata {2772 fn makeEmptyNamespaceDebugType(o: *Object, ty: Type) !Builder.Metadata {
2773 const zcu = o.pt.zcu;2773 const zcu = o.pt.zcu;
2774 const ip = &zcu.intern_pool;2774 const ip = &zcu.intern_pool;
2775 const file = try o.getDebugFile(ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFull(ip).file);2775 const file = try o.getDebugFile(ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(ip));
2776 const scope = if (ty.getParentNamespace(zcu).unwrap()) |parent_namespace|2776 const scope = if (ty.getParentNamespace(zcu).unwrap()) |parent_namespace|
2777 try o.namespaceToDebugScope(parent_namespace)2777 try o.namespaceToDebugScope(parent_namespace)
2778 else2778 else
src/crash_report.zig+14-2
...@@ -78,7 +78,13 @@ fn dumpStatusReport() !void {...@@ -78,7 +78,13 @@ fn dumpStatusReport() !void {
78 const block: *Sema.Block = anal.block;78 const block: *Sema.Block = anal.block;
79 const zcu = anal.sema.pt.zcu;79 const zcu = anal.sema.pt.zcu;
8080
81 const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu);81 const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu) orelse {
82 const file = zcu.fileByIndex(block.src_base_inst.resolveFile(&zcu.intern_pool));
83 try stderr.writeAll("Analyzing lost instruction in file '");
84 try writeFilePath(file, stderr);
85 try stderr.writeAll("'. This should not happen!\n\n");
86 return;
87 };
8288
83 try stderr.writeAll("Analyzing ");89 try stderr.writeAll("Analyzing ");
84 try writeFilePath(file, stderr);90 try writeFilePath(file, stderr);
...@@ -104,7 +110,13 @@ fn dumpStatusReport() !void {...@@ -104,7 +110,13 @@ fn dumpStatusReport() !void {
104 while (parent) |curr| {110 while (parent) |curr| {
105 fba.reset();111 fba.reset();
106 try stderr.writeAll(" in ");112 try stderr.writeAll(" in ");
107 const cur_block_file, const cur_block_src_base_node = Zcu.LazySrcLoc.resolveBaseNode(curr.block.src_base_inst, zcu);113 const cur_block_file, const cur_block_src_base_node = Zcu.LazySrcLoc.resolveBaseNode(curr.block.src_base_inst, zcu) orelse {
114 const cur_block_file = zcu.fileByIndex(curr.block.src_base_inst.resolveFile(&zcu.intern_pool));
115 try writeFilePath(cur_block_file, stderr);
116 try stderr.writeAll("\n > [lost instruction; this should not happen]\n");
117 parent = curr.parent;
118 continue;
119 };
108 try writeFilePath(cur_block_file, stderr);120 try writeFilePath(cur_block_file, stderr);
109 try stderr.writeAll("\n > ");121 try stderr.writeAll("\n > ");
110 print_zir.renderSingleInstruction(122 print_zir.renderSingleInstruction(
src/link/Dwarf.zig+11-11
...@@ -786,7 +786,7 @@ const Entry = struct {...@@ -786,7 +786,7 @@ const Entry = struct {
786 const ip = &zcu.intern_pool;786 const ip = &zcu.intern_pool;
787 for (dwarf.types.keys(), dwarf.types.values()) |ty, other_entry| {787 for (dwarf.types.keys(), dwarf.types.values()) |ty, other_entry| {
788 const ty_unit: Unit.Index = if (Type.fromInterned(ty).typeDeclInst(zcu)) |inst_index|788 const ty_unit: Unit.Index = if (Type.fromInterned(ty).typeDeclInst(zcu)) |inst_index|
789 dwarf.getUnit(zcu.fileByIndex(inst_index.resolveFull(ip).file).mod) catch unreachable789 dwarf.getUnit(zcu.fileByIndex(inst_index.resolveFile(ip)).mod) catch unreachable
790 else790 else
791 .main;791 .main;
792 if (sec.getUnit(ty_unit) == unit and unit.getEntry(other_entry) == entry)792 if (sec.getUnit(ty_unit) == unit and unit.getEntry(other_entry) == entry)
...@@ -796,7 +796,7 @@ const Entry = struct {...@@ -796,7 +796,7 @@ const Entry = struct {
796 });796 });
797 }797 }
798 for (dwarf.navs.keys(), dwarf.navs.values()) |nav, other_entry| {798 for (dwarf.navs.keys(), dwarf.navs.values()) |nav, other_entry| {
799 const nav_unit = dwarf.getUnit(zcu.fileByIndex(ip.getNav(nav).srcInst(ip).resolveFull(ip).file).mod) catch unreachable;799 const nav_unit = dwarf.getUnit(zcu.fileByIndex(ip.getNav(nav).srcInst(ip).resolveFile(ip)).mod) catch unreachable;
800 if (sec.getUnit(nav_unit) == unit and unit.getEntry(other_entry) == entry)800 if (sec.getUnit(nav_unit) == unit and unit.getEntry(other_entry) == entry)
801 log.err("missing Nav({}({d}))", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(nav) });801 log.err("missing Nav({}({d}))", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(nav) });
802 }802 }
...@@ -1201,7 +1201,7 @@ pub const WipNav = struct {...@@ -1201,7 +1201,7 @@ pub const WipNav = struct {
1201 const ip = &zcu.intern_pool;1201 const ip = &zcu.intern_pool;
1202 const maybe_inst_index = ty.typeDeclInst(zcu);1202 const maybe_inst_index = ty.typeDeclInst(zcu);
1203 const unit = if (maybe_inst_index) |inst_index|1203 const unit = if (maybe_inst_index) |inst_index|
1204 try wip_nav.dwarf.getUnit(zcu.fileByIndex(inst_index.resolveFull(ip).file).mod)1204 try wip_nav.dwarf.getUnit(zcu.fileByIndex(inst_index.resolveFile(ip)).mod)
1205 else1205 else
1206 .main;1206 .main;
1207 const gop = try wip_nav.dwarf.types.getOrPut(wip_nav.dwarf.gpa, ty.toIntern());1207 const gop = try wip_nav.dwarf.types.getOrPut(wip_nav.dwarf.gpa, ty.toIntern());
...@@ -1539,7 +1539,7 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In...@@ -1539,7 +1539,7 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
1539 const nav = ip.getNav(nav_index);1539 const nav = ip.getNav(nav_index);
1540 log.debug("initWipNav({})", .{nav.fqn.fmt(ip)});1540 log.debug("initWipNav({})", .{nav.fqn.fmt(ip)});
15411541
1542 const inst_info = nav.srcInst(ip).resolveFull(ip);1542 const inst_info = nav.srcInst(ip).resolveFull(ip).?;
1543 const file = zcu.fileByIndex(inst_info.file);1543 const file = zcu.fileByIndex(inst_info.file);
15441544
1545 const unit = try dwarf.getUnit(file.mod);1545 const unit = try dwarf.getUnit(file.mod);
...@@ -1874,7 +1874,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool...@@ -1874,7 +1874,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool
1874 const nav = ip.getNav(nav_index);1874 const nav = ip.getNav(nav_index);
1875 log.debug("updateComptimeNav({})", .{nav.fqn.fmt(ip)});1875 log.debug("updateComptimeNav({})", .{nav.fqn.fmt(ip)});
18761876
1877 const inst_info = nav.srcInst(ip).resolveFull(ip);1877 const inst_info = nav.srcInst(ip).resolveFull(ip).?;
1878 const file = zcu.fileByIndex(inst_info.file);1878 const file = zcu.fileByIndex(inst_info.file);
1879 assert(file.zir_loaded);1879 assert(file.zir_loaded);
1880 const decl_inst = file.zir.instructions.get(@intFromEnum(inst_info.inst));1880 const decl_inst = file.zir.instructions.get(@intFromEnum(inst_info.inst));
...@@ -1937,7 +1937,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool...@@ -1937,7 +1937,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool
1937 };1937 };
1938 break :value_inst value_inst;1938 break :value_inst value_inst;
1939 };1939 };
1940 const type_inst_info = loaded_struct.zir_index.unwrap().?.resolveFull(ip);1940 const type_inst_info = loaded_struct.zir_index.unwrap().?.resolveFull(ip).?;
1941 if (type_inst_info.inst != value_inst) break :decl_struct;1941 if (type_inst_info.inst != value_inst) break :decl_struct;
19421942
1943 const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern());1943 const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern());
...@@ -2053,7 +2053,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool...@@ -2053,7 +2053,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool
2053 };2053 };
2054 break :value_inst value_inst;2054 break :value_inst value_inst;
2055 };2055 };
2056 const type_inst_info = loaded_enum.zir_index.unwrap().?.resolveFull(ip);2056 const type_inst_info = loaded_enum.zir_index.unwrap().?.resolveFull(ip).?;
2057 if (type_inst_info.inst != value_inst) break :decl_enum;2057 if (type_inst_info.inst != value_inst) break :decl_enum;
20582058
2059 const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern());2059 const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern());
...@@ -2127,7 +2127,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool...@@ -2127,7 +2127,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool
2127 };2127 };
2128 break :value_inst value_inst;2128 break :value_inst value_inst;
2129 };2129 };
2130 const type_inst_info = loaded_union.zir_index.resolveFull(ip);2130 const type_inst_info = loaded_union.zir_index.resolveFull(ip).?;
2131 if (type_inst_info.inst != value_inst) break :decl_union;2131 if (type_inst_info.inst != value_inst) break :decl_union;
21322132
2133 const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern());2133 const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern());
...@@ -2240,7 +2240,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool...@@ -2240,7 +2240,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool
2240 };2240 };
2241 break :value_inst value_inst;2241 break :value_inst value_inst;
2242 };2242 };
2243 const type_inst_info = loaded_opaque.zir_index.resolveFull(ip);2243 const type_inst_info = loaded_opaque.zir_index.resolveFull(ip).?;
2244 if (type_inst_info.inst != value_inst) break :decl_opaque;2244 if (type_inst_info.inst != value_inst) break :decl_opaque;
22452245
2246 const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern());2246 const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern());
...@@ -2704,7 +2704,7 @@ pub fn updateContainerType(dwarf: *Dwarf, pt: Zcu.PerThread, type_index: InternP...@@ -2704,7 +2704,7 @@ pub fn updateContainerType(dwarf: *Dwarf, pt: Zcu.PerThread, type_index: InternP
2704 const ty = Type.fromInterned(type_index);2704 const ty = Type.fromInterned(type_index);
2705 log.debug("updateContainerType({}({d}))", .{ ty.fmt(pt), @intFromEnum(type_index) });2705 log.debug("updateContainerType({}({d}))", .{ ty.fmt(pt), @intFromEnum(type_index) });
27062706
2707 const inst_info = ty.typeDeclInst(zcu).?.resolveFull(ip);2707 const inst_info = ty.typeDeclInst(zcu).?.resolveFull(ip).?;
2708 const file = zcu.fileByIndex(inst_info.file);2708 const file = zcu.fileByIndex(inst_info.file);
2709 if (inst_info.inst == .main_struct_inst) {2709 if (inst_info.inst == .main_struct_inst) {
2710 const unit = try dwarf.getUnit(file.mod);2710 const unit = try dwarf.getUnit(file.mod);
...@@ -2922,7 +2922,7 @@ pub fn updateNavLineNumber(dwarf: *Dwarf, zcu: *Zcu, nav_index: InternPool.Nav.I...@@ -2922,7 +2922,7 @@ pub fn updateNavLineNumber(dwarf: *Dwarf, zcu: *Zcu, nav_index: InternPool.Nav.I
2922 const ip = &zcu.intern_pool;2922 const ip = &zcu.intern_pool;
29232923
2924 const zir_index = ip.getCau(ip.getNav(nav_index).analysis_owner.unwrap() orelse return).zir_index;2924 const zir_index = ip.getCau(ip.getNav(nav_index).analysis_owner.unwrap() orelse return).zir_index;
2925 const inst_info = zir_index.resolveFull(ip);2925 const inst_info = zir_index.resolveFull(ip).?;
2926 assert(inst_info.inst != .main_struct_inst);2926 assert(inst_info.inst != .main_struct_inst);
2927 const file = zcu.fileByIndex(inst_info.file);2927 const file = zcu.fileByIndex(inst_info.file);
29282928
src/main.zig+3-2
...@@ -3257,9 +3257,12 @@ fn buildOutputType(...@@ -3257,9 +3257,12 @@ fn buildOutputType(
3257 else => false,3257 else => false,
3258 };3258 };
32593259
3260 const incremental = opt_incremental orelse false;
3261
3260 const disable_lld_caching = !output_to_cache;3262 const disable_lld_caching = !output_to_cache;
32613263
3262 const cache_mode: Compilation.CacheMode = b: {3264 const cache_mode: Compilation.CacheMode = b: {
3265 if (incremental) break :b .incremental;
3263 if (disable_lld_caching) break :b .incremental;3266 if (disable_lld_caching) break :b .incremental;
3264 if (!create_module.resolved_options.have_zcu) break :b .whole;3267 if (!create_module.resolved_options.have_zcu) break :b .whole;
32653268
...@@ -3272,8 +3275,6 @@ fn buildOutputType(...@@ -3272,8 +3275,6 @@ fn buildOutputType(
3272 break :b .incremental;3275 break :b .incremental;
3273 };3276 };
32743277
3275 const incremental = opt_incremental orelse false;
3276
3277 process.raiseFileDescriptorLimit();3278 process.raiseFileDescriptorLimit();
32783279
3279 var file_system_inputs: std.ArrayListUnmanaged(u8) = .{};3280 var file_system_inputs: std.ArrayListUnmanaged(u8) = .{};
test/incremental/add_decl created+59
...@@ -0,0 +1,59 @@
1#target=x86_64-linux
2#update=initial version
3#file=main.zig
4const std = @import("std");
5pub fn main() !void {
6 try std.io.getStdOut().writeAll(foo);
7}
8const foo = "good morning\n";
9#expect_stdout="good morning\n"
10
11#update=add new declaration
12#file=main.zig
13const std = @import("std");
14pub fn main() !void {
15 try std.io.getStdOut().writeAll(foo);
16}
17const foo = "good morning\n";
18const bar = "good evening\n";
19#expect_stdout="good morning\n"
20
21#update=reference new declaration
22#file=main.zig
23const std = @import("std");
24pub fn main() !void {
25 try std.io.getStdOut().writeAll(bar);
26}
27const foo = "good morning\n";
28const bar = "good evening\n";
29#expect_stdout="good evening\n"
30
31#update=reference missing declaration
32#file=main.zig
33const std = @import("std");
34pub fn main() !void {
35 try std.io.getStdOut().writeAll(qux);
36}
37const foo = "good morning\n";
38const bar = "good evening\n";
39#expect_error=ignored
40
41#update=add missing declaration
42#file=main.zig
43const std = @import("std");
44pub fn main() !void {
45 try std.io.getStdOut().writeAll(qux);
46}
47const foo = "good morning\n";
48const bar = "good evening\n";
49const qux = "good night\n";
50#expect_stdout="good night\n"
51
52#update=remove unused declarations
53#file=main.zig
54const std = @import("std");
55pub fn main() !void {
56 try std.io.getStdOut().writeAll(qux);
57}
58const qux = "good night\n";
59#expect_stdout="good night\n"
test/incremental/add_decl_namespaced created+59
...@@ -0,0 +1,59 @@
1#target=x86_64-linux
2#update=initial version
3#file=main.zig
4const std = @import("std");
5pub fn main() !void {
6 try std.io.getStdOut().writeAll(@This().foo);
7}
8const foo = "good morning\n";
9#expect_stdout="good morning\n"
10
11#update=add new declaration
12#file=main.zig
13const std = @import("std");
14pub fn main() !void {
15 try std.io.getStdOut().writeAll(@This().foo);
16}
17const foo = "good morning\n";
18const bar = "good evening\n";
19#expect_stdout="good morning\n"
20
21#update=reference new declaration
22#file=main.zig
23const std = @import("std");
24pub fn main() !void {
25 try std.io.getStdOut().writeAll(@This().bar);
26}
27const foo = "good morning\n";
28const bar = "good evening\n";
29#expect_stdout="good evening\n"
30
31#update=reference missing declaration
32#file=main.zig
33const std = @import("std");
34pub fn main() !void {
35 try std.io.getStdOut().writeAll(@This().qux);
36}
37const foo = "good morning\n";
38const bar = "good evening\n";
39#expect_error=ignored
40
41#update=add missing declaration
42#file=main.zig
43const std = @import("std");
44pub fn main() !void {
45 try std.io.getStdOut().writeAll(@This().qux);
46}
47const foo = "good morning\n";
48const bar = "good evening\n";
49const qux = "good night\n";
50#expect_stdout="good night\n"
51
52#update=remove unused declarations
53#file=main.zig
54const std = @import("std");
55pub fn main() !void {
56 try std.io.getStdOut().writeAll(@This().qux);
57}
58const qux = "good night\n";
59#expect_stdout="good night\n"
test/incremental/delete_comptime_decls created+38
...@@ -0,0 +1,38 @@
1#target=x86_64-linux
2#update=initial version
3#file=main.zig
4pub fn main() void {}
5comptime {
6 var array = [_:0]u8{ 1, 2, 3, 4 };
7 const src_slice: [:0]u8 = &array;
8 const slice = src_slice[2..6];
9 _ = slice;
10}
11comptime {
12 var array = [_:0]u8{ 1, 2, 3, 4 };
13 const slice = array[2..6];
14 _ = slice;
15}
16comptime {
17 var array = [_]u8{ 1, 2, 3, 4 };
18 const slice = array[2..5];
19 _ = slice;
20}
21comptime {
22 var array = [_:0]u8{ 1, 2, 3, 4 };
23 const slice = array[3..2];
24 _ = slice;
25}
26#expect_error=ignored
27
28#update=delete and modify comptime decls
29#file=main.zig
30pub fn main() void {}
31comptime {
32 const x: [*c]u8 = null;
33 var runtime_len: usize = undefined;
34 runtime_len = 0;
35 const y = x[0..runtime_len];
36 _ = y;
37}
38#expect_error=ignored
test/incremental/unreferenced_error created+38
...@@ -0,0 +1,38 @@
1#target=x86_64-linux
2#update=initial version
3#file=main.zig
4const std = @import("std");
5pub fn main() !void {
6 try std.io.getStdOut().writeAll(a);
7}
8const a = "Hello, World!\n";
9#expect_stdout="Hello, World!\n"
10
11#update=introduce compile error
12#file=main.zig
13const std = @import("std");
14pub fn main() !void {
15 try std.io.getStdOut().writeAll(a);
16}
17const a = @compileError("bad a");
18#expect_error=ignored
19
20#update=remove error reference
21#file=main.zig
22const std = @import("std");
23pub fn main() !void {
24 try std.io.getStdOut().writeAll(b);
25}
26const a = @compileError("bad a");
27const b = "Hi there!\n";
28#expect_stdout="Hi there!\n"
29
30#update=introduce and remove reference to error
31#file=main.zig
32const std = @import("std");
33pub fn main() !void {
34 try std.io.getStdOut().writeAll(a);
35}
36const a = "Back to a\n";
37const b = @compileError("bad b");
38#expect_stdout="Back to a\n"
tools/incr-check.zig+190-17
...@@ -2,14 +2,55 @@ const std = @import("std");...@@ -2,14 +2,55 @@ const std = @import("std");
2const fatal = std.process.fatal;2const fatal = std.process.fatal;
3const Allocator = std.mem.Allocator;3const Allocator = std.mem.Allocator;
44
5const usage = "usage: incr-check <zig binary path> <input file> [--zig-lib-dir lib] [--debug-zcu] [--emit none|bin|c] [--zig-cc-binary /path/to/zig]";
6
7const EmitMode = enum {
8 none,
9 bin,
10 c,
11};
12
5pub fn main() !void {13pub fn main() !void {
6 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);14 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
7 defer arena_instance.deinit();15 defer arena_instance.deinit();
8 const arena = arena_instance.allocator();16 const arena = arena_instance.allocator();
917
10 const args = try std.process.argsAlloc(arena);18 var opt_zig_exe: ?[]const u8 = null;
11 const zig_exe = args[1];19 var opt_input_file_name: ?[]const u8 = null;
12 const input_file_name = args[2];20 var opt_lib_dir: ?[]const u8 = null;
21 var opt_cc_zig: ?[]const u8 = null;
22 var emit: EmitMode = .bin;
23 var debug_zcu = false;
24
25 var arg_it = try std.process.argsWithAllocator(arena);
26 _ = arg_it.skip();
27 while (arg_it.next()) |arg| {
28 if (arg.len > 0 and arg[0] == '-') {
29 if (std.mem.eql(u8, arg, "--emit")) {
30 const emit_str = arg_it.next() orelse fatal("expected arg after '--emit'\n{s}", .{usage});
31 emit = std.meta.stringToEnum(EmitMode, emit_str) orelse
32 fatal("invalid emit mode '{s}'\n{s}", .{ emit_str, usage });
33 } else if (std.mem.eql(u8, arg, "--zig-lib-dir")) {
34 opt_lib_dir = arg_it.next() orelse fatal("expected arg after '--zig-lib-dir'\n{s}", .{usage});
35 } else if (std.mem.eql(u8, arg, "--debug-zcu")) {
36 debug_zcu = true;
37 } else if (std.mem.eql(u8, arg, "--zig-cc-binary")) {
38 opt_cc_zig = arg_it.next() orelse fatal("expect arg after '--zig-cc-binary'\n{s}", .{usage});
39 } else {
40 fatal("unknown option '{s}'\n{s}", .{ arg, usage });
41 }
42 continue;
43 }
44 if (opt_zig_exe == null) {
45 opt_zig_exe = arg;
46 } else if (opt_input_file_name == null) {
47 opt_input_file_name = arg;
48 } else {
49 fatal("unknown argument '{s}'\n{s}", .{ arg, usage });
50 }
51 }
52 const zig_exe = opt_zig_exe orelse fatal("missing path to zig\n{s}", .{usage});
53 const input_file_name = opt_input_file_name orelse fatal("missing input file\n{s}", .{usage});
1354
14 const input_file_bytes = try std.fs.cwd().readFileAlloc(arena, input_file_name, std.math.maxInt(u32));55 const input_file_bytes = try std.fs.cwd().readFileAlloc(arena, input_file_name, std.math.maxInt(u32));
15 const case = try Case.parse(arena, input_file_bytes);56 const case = try Case.parse(arena, input_file_bytes);
...@@ -24,13 +65,18 @@ pub fn main() !void {...@@ -24,13 +65,18 @@ pub fn main() !void {
24 const child_prog_node = prog_node.start("zig build-exe", 0);65 const child_prog_node = prog_node.start("zig build-exe", 0);
25 defer child_prog_node.end();66 defer child_prog_node.end();
2667
27 var child = std.process.Child.init(&.{68 // Convert paths to be relative to the cwd of the subprocess.
28 // Convert incr-check-relative path to subprocess-relative path.69 const resolved_zig_exe = try std.fs.path.relative(arena, tmp_dir_path, zig_exe);
29 try std.fs.path.relative(arena, tmp_dir_path, zig_exe),70 const opt_resolved_lib_dir = if (opt_lib_dir) |lib_dir|
71 try std.fs.path.relative(arena, tmp_dir_path, lib_dir)
72 else
73 null;
74
75 var child_args: std.ArrayListUnmanaged([]const u8) = .{};
76 try child_args.appendSlice(arena, &.{
77 resolved_zig_exe,
30 "build-exe",78 "build-exe",
31 case.root_source_file,79 case.root_source_file,
32 "-fno-llvm",
33 "-fno-lld",
34 "-fincremental",80 "-fincremental",
35 "-target",81 "-target",
36 case.target_query,82 case.target_query,
...@@ -39,8 +85,20 @@ pub fn main() !void {...@@ -39,8 +85,20 @@ pub fn main() !void {
39 "--global-cache-dir",85 "--global-cache-dir",
40 ".global_cache",86 ".global_cache",
41 "--listen=-",87 "--listen=-",
42 }, arena);88 });
89 if (opt_resolved_lib_dir) |resolved_lib_dir| {
90 try child_args.appendSlice(arena, &.{ "--zig-lib-dir", resolved_lib_dir });
91 }
92 switch (emit) {
93 .bin => try child_args.appendSlice(arena, &.{ "-fno-llvm", "-fno-lld" }),
94 .none => try child_args.append(arena, "-fno-emit-bin"),
95 .c => try child_args.appendSlice(arena, &.{ "-ofmt=c", "-lc" }),
96 }
97 if (debug_zcu) {
98 try child_args.appendSlice(arena, &.{ "--debug-log", "zcu" });
99 }
43100
101 var child = std.process.Child.init(child_args.items, arena);
44 child.stdin_behavior = .Pipe;102 child.stdin_behavior = .Pipe;
45 child.stdout_behavior = .Pipe;103 child.stdout_behavior = .Pipe;
46 child.stderr_behavior = .Pipe;104 child.stderr_behavior = .Pipe;
...@@ -48,12 +106,33 @@ pub fn main() !void {...@@ -48,12 +106,33 @@ pub fn main() !void {
48 child.cwd_dir = tmp_dir;106 child.cwd_dir = tmp_dir;
49 child.cwd = tmp_dir_path;107 child.cwd = tmp_dir_path;
50108
109 var cc_child_args: std.ArrayListUnmanaged([]const u8) = .{};
110 if (emit == .c) {
111 const resolved_cc_zig_exe = if (opt_cc_zig) |cc_zig_exe|
112 try std.fs.path.relative(arena, tmp_dir_path, cc_zig_exe)
113 else
114 resolved_zig_exe;
115
116 try cc_child_args.appendSlice(arena, &.{
117 resolved_cc_zig_exe,
118 "cc",
119 "-target",
120 case.target_query,
121 "-I",
122 opt_resolved_lib_dir orelse fatal("'--zig-lib-dir' required when using '--emit c'", .{}),
123 "-o",
124 });
125 }
126
51 var eval: Eval = .{127 var eval: Eval = .{
52 .arena = arena,128 .arena = arena,
53 .case = case,129 .case = case,
54 .tmp_dir = tmp_dir,130 .tmp_dir = tmp_dir,
55 .tmp_dir_path = tmp_dir_path,131 .tmp_dir_path = tmp_dir_path,
56 .child = &child,132 .child = &child,
133 .allow_stderr = debug_zcu,
134 .emit = emit,
135 .cc_child_args = &cc_child_args,
57 };136 };
58137
59 try child.spawn();138 try child.spawn();
...@@ -65,9 +144,16 @@ pub fn main() !void {...@@ -65,9 +144,16 @@ pub fn main() !void {
65 defer poller.deinit();144 defer poller.deinit();
66145
67 for (case.updates) |update| {146 for (case.updates) |update| {
147 var update_node = prog_node.start(update.name, 0);
148 defer update_node.end();
149
150 if (debug_zcu) {
151 std.log.info("=== START UPDATE '{s}' ===", .{update.name});
152 }
153
68 eval.write(update);154 eval.write(update);
69 try eval.requestUpdate();155 try eval.requestUpdate();
70 try eval.check(&poller, update);156 try eval.check(&poller, update, update_node);
71 }157 }
72158
73 try eval.end(&poller);159 try eval.end(&poller);
...@@ -81,6 +167,11 @@ const Eval = struct {...@@ -81,6 +167,11 @@ const Eval = struct {
81 tmp_dir: std.fs.Dir,167 tmp_dir: std.fs.Dir,
82 tmp_dir_path: []const u8,168 tmp_dir_path: []const u8,
83 child: *std.process.Child,169 child: *std.process.Child,
170 allow_stderr: bool,
171 emit: EmitMode,
172 /// When `emit == .c`, this contains the first few arguments to `zig cc` to build the generated binary.
173 /// The arguments `out.c in.c` must be appended before spawning the subprocess.
174 cc_child_args: *std.ArrayListUnmanaged([]const u8),
84175
85 const StreamEnum = enum { stdout, stderr };176 const StreamEnum = enum { stdout, stderr };
86 const Poller = std.io.Poller(StreamEnum);177 const Poller = std.io.Poller(StreamEnum);
...@@ -102,7 +193,7 @@ const Eval = struct {...@@ -102,7 +193,7 @@ const Eval = struct {
102 }193 }
103 }194 }
104195
105 fn check(eval: *Eval, poller: *Poller, update: Case.Update) !void {196 fn check(eval: *Eval, poller: *Poller, update: Case.Update, prog_node: std.Progress.Node) !void {
106 const arena = eval.arena;197 const arena = eval.arena;
107 const Header = std.zig.Server.Message.Header;198 const Header = std.zig.Server.Message.Header;
108 const stdout = poller.fifo(.stdout);199 const stdout = poller.fifo(.stdout);
...@@ -136,9 +227,18 @@ const Eval = struct {...@@ -136,9 +227,18 @@ const Eval = struct {
136 };227 };
137 if (stderr.readableLength() > 0) {228 if (stderr.readableLength() > 0) {
138 const stderr_data = try stderr.toOwnedSlice();229 const stderr_data = try stderr.toOwnedSlice();
139 fatal("error_bundle included unexpected stderr:\n{s}", .{stderr_data});230 if (eval.allow_stderr) {
231 std.log.info("error_bundle included stderr:\n{s}", .{stderr_data});
232 } else {
233 fatal("error_bundle included unexpected stderr:\n{s}", .{stderr_data});
234 }
235 }
236 if (result_error_bundle.errorMessageCount() == 0) {
237 // Empty bundle indicates successful update in a `-fno-emit-bin` build.
238 try eval.checkSuccessOutcome(update, null, prog_node);
239 } else {
240 try eval.checkErrorOutcome(update, result_error_bundle);
140 }241 }
141 try eval.checkErrorOutcome(update, result_error_bundle);
142 // This message indicates the end of the update.242 // This message indicates the end of the update.
143 stdout.discard(body.len);243 stdout.discard(body.len);
144 return;244 return;
...@@ -150,9 +250,13 @@ const Eval = struct {...@@ -150,9 +250,13 @@ const Eval = struct {
150 const result_binary = try arena.dupe(u8, body[@sizeOf(EbpHdr)..]);250 const result_binary = try arena.dupe(u8, body[@sizeOf(EbpHdr)..]);
151 if (stderr.readableLength() > 0) {251 if (stderr.readableLength() > 0) {
152 const stderr_data = try stderr.toOwnedSlice();252 const stderr_data = try stderr.toOwnedSlice();
153 fatal("emit_bin_path included unexpected stderr:\n{s}", .{stderr_data});253 if (eval.allow_stderr) {
254 std.log.info("emit_bin_path included stderr:\n{s}", .{stderr_data});
255 } else {
256 fatal("emit_bin_path included unexpected stderr:\n{s}", .{stderr_data});
257 }
154 }258 }
155 try eval.checkSuccessOutcome(update, result_binary);259 try eval.checkSuccessOutcome(update, result_binary, prog_node);
156 // This message indicates the end of the update.260 // This message indicates the end of the update.
157 stdout.discard(body.len);261 stdout.discard(body.len);
158 return;262 return;
...@@ -166,7 +270,11 @@ const Eval = struct {...@@ -166,7 +270,11 @@ const Eval = struct {
166270
167 if (stderr.readableLength() > 0) {271 if (stderr.readableLength() > 0) {
168 const stderr_data = try stderr.toOwnedSlice();272 const stderr_data = try stderr.toOwnedSlice();
169 fatal("update '{s}' failed:\n{s}", .{ update.name, stderr_data });273 if (eval.allow_stderr) {
274 std.log.info("update '{s}' included stderr:\n{s}", .{ update.name, stderr_data });
275 } else {
276 fatal("update '{s}' failed:\n{s}", .{ update.name, stderr_data });
277 }
170 }278 }
171279
172 waitChild(eval.child);280 waitChild(eval.child);
...@@ -191,12 +299,28 @@ const Eval = struct {...@@ -191,12 +299,28 @@ const Eval = struct {
191 }299 }
192 }300 }
193301
194 fn checkSuccessOutcome(eval: *Eval, update: Case.Update, binary_path: []const u8) !void {302 fn checkSuccessOutcome(eval: *Eval, update: Case.Update, opt_emitted_path: ?[]const u8, prog_node: std.Progress.Node) !void {
195 switch (update.outcome) {303 switch (update.outcome) {
196 .unknown => return,304 .unknown => return,
197 .compile_errors => fatal("expected compile errors but compilation incorrectly succeeded", .{}),305 .compile_errors => fatal("expected compile errors but compilation incorrectly succeeded", .{}),
198 .stdout, .exit_code => {},306 .stdout, .exit_code => {},
199 }307 }
308 const emitted_path = opt_emitted_path orelse {
309 std.debug.assert(eval.emit == .none);
310 return;
311 };
312
313 const binary_path = switch (eval.emit) {
314 .none => unreachable,
315 .bin => emitted_path,
316 .c => bin: {
317 const rand_int = std.crypto.random.int(u64);
318 const out_bin_name = "./out_" ++ std.fmt.hex(rand_int);
319 try eval.buildCOutput(update, emitted_path, out_bin_name, prog_node);
320 break :bin out_bin_name;
321 },
322 };
323
200 const result = std.process.Child.run(.{324 const result = std.process.Child.run(.{
201 .allocator = eval.arena,325 .allocator = eval.arena,
202 .argv = &.{binary_path},326 .argv = &.{binary_path},
...@@ -266,6 +390,50 @@ const Eval = struct {...@@ -266,6 +390,50 @@ const Eval = struct {
266 fatal("unexpected stderr:\n{s}", .{stderr_data});390 fatal("unexpected stderr:\n{s}", .{stderr_data});
267 }391 }
268 }392 }
393
394 fn buildCOutput(eval: *Eval, update: Case.Update, c_path: []const u8, out_path: []const u8, prog_node: std.Progress.Node) !void {
395 std.debug.assert(eval.cc_child_args.items.len > 0);
396
397 const child_prog_node = prog_node.start("build cbe output", 0);
398 defer child_prog_node.end();
399
400 try eval.cc_child_args.appendSlice(eval.arena, &.{ out_path, c_path });
401 defer eval.cc_child_args.items.len -= 2;
402
403 const result = std.process.Child.run(.{
404 .allocator = eval.arena,
405 .argv = eval.cc_child_args.items,
406 .cwd_dir = eval.tmp_dir,
407 .cwd = eval.tmp_dir_path,
408 .progress_node = child_prog_node,
409 }) catch |err| {
410 fatal("update '{s}': failed to spawn zig cc for '{s}': {s}", .{
411 update.name, c_path, @errorName(err),
412 });
413 };
414 switch (result.term) {
415 .Exited => |code| if (code != 0) {
416 if (result.stderr.len != 0) {
417 std.log.err("update '{s}': zig cc stderr:\n{s}", .{
418 update.name, result.stderr,
419 });
420 }
421 fatal("update '{s}': zig cc for '{s}' failed with code {d}", .{
422 update.name, c_path, code,
423 });
424 },
425 .Signal, .Stopped, .Unknown => {
426 if (result.stderr.len != 0) {
427 std.log.err("update '{s}': zig cc stderr:\n{s}", .{
428 update.name, result.stderr,
429 });
430 }
431 fatal("update '{s}': zig cc for '{s}' terminated unexpectedly", .{
432 update.name, c_path,
433 });
434 },
435 }
436 }
269};437};
270438
271const Case = struct {439const Case = struct {
...@@ -357,6 +525,11 @@ const Case = struct {...@@ -357,6 +525,11 @@ const Case = struct {
357 fatal("line {d}: bad string literal: {s}", .{ line_n, @errorName(err) });525 fatal("line {d}: bad string literal: {s}", .{ line_n, @errorName(err) });
358 },526 },
359 };527 };
528 } else if (std.mem.eql(u8, key, "expect_error")) {
529 if (updates.items.len == 0) fatal("line {d}: expect directive before update", .{line_n});
530 const last_update = &updates.items[updates.items.len - 1];
531 if (last_update.outcome != .unknown) fatal("line {d}: conflicting expect directive", .{line_n});
532 last_update.outcome = .{ .compile_errors = &.{} };
360 } else {533 } else {
361 fatal("line {d}: unrecognized key '{s}'", .{ line_n, key });534 fatal("line {d}: unrecognized key '{s}'", .{ line_n, key });
362 }535 }