| ... | @@ -27959,9 +27959,8 @@ fn coerceInMemoryAllowedErrorSets( | ... | @@ -27959,9 +27959,8 @@ fn coerceInMemoryAllowedErrorSets( |
| 27959 | | 27959 | |
| 27960 | switch (src_ty.toIntern()) { | 27960 | switch (src_ty.toIntern()) { |
| 27961 | .anyerror_type => switch (ip.indexToKey(dest_ty.toIntern())) { | 27961 | .anyerror_type => switch (ip.indexToKey(dest_ty.toIntern())) { |
| 27962 | .inferred_error_set_type => unreachable, // Caught by dest_ty.isAnyError(mod) above. | | |
| 27963 | .simple_type => unreachable, // filtered out above | 27962 | .simple_type => unreachable, // filtered out above |
| 27964 | .error_set_type => return .from_anyerror, | 27963 | .error_set_type, .inferred_error_set_type => return .from_anyerror, |
| 27965 | else => unreachable, | 27964 | else => unreachable, |
| 27966 | }, | 27965 | }, |
| 27967 | | 27966 | |
| ... | @@ -28008,8 +28007,6 @@ fn coerceInMemoryAllowedErrorSets( | ... | @@ -28008,8 +28007,6 @@ fn coerceInMemoryAllowedErrorSets( |
| 28008 | else => unreachable, | 28007 | else => unreachable, |
| 28009 | }, | 28008 | }, |
| 28010 | } | 28009 | } |
| 28011 | | | |
| 28012 | unreachable; | | |
| 28013 | } | 28010 | } |
| 28014 | | 28011 | |
| 28015 | fn coerceInMemoryAllowedFns( | 28012 | fn coerceInMemoryAllowedFns( |
| ... | @@ -31586,601 +31583,1523 @@ fn unionToTag( | ... | @@ -31586,601 +31583,1523 @@ fn unionToTag( |
| 31586 | return block.addTyOp(.get_union_tag, enum_ty, un); | 31583 | return block.addTyOp(.get_union_tag, enum_ty, un); |
| 31587 | } | 31584 | } |
| 31588 | | 31585 | |
| 31589 | fn resolvePeerTypes( | 31586 | const PeerResolveStrategy = enum { |
| 31590 | sema: *Sema, | 31587 | /// The type is not known. |
| 31591 | block: *Block, | 31588 | /// If refined no further, this is equivalent to `exact`. |
| 31592 | src: LazySrcLoc, | 31589 | unknown, |
| 31593 | instructions: []const Air.Inst.Ref, | 31590 | /// The type may be an error set or error union. |
| 31594 | candidate_srcs: Module.PeerTypeCandidateSrc, | 31591 | /// If refined no further, it is an error set. |
| 31595 | ) !Type { | 31592 | error_set, |
| 31596 | const mod = sema.mod; | 31593 | /// The type must be some error union. |
| 31597 | switch (instructions.len) { | 31594 | error_union, |
| 31598 | 0 => return Type.noreturn, | 31595 | /// The type may be @TypeOf(null), an optional or a C pointer. |
| 31599 | 1 => return sema.typeOf(instructions[0]), | 31596 | /// If refined no further, it is @TypeOf(null). |
| 31600 | else => {}, | 31597 | nullable, |
| 31601 | } | 31598 | /// The type must be some optional or a C pointer. |
| 31602 | | 31599 | /// If refined no further, it is an optional. |
| 31603 | const target = mod.getTarget(); | 31600 | optional, |
| 31604 | | 31601 | /// The type must be either an array or a vector. |
| 31605 | var chosen = instructions[0]; | 31602 | /// If refined no further, it is an array. |
| 31606 | // If this is non-null then it does the following thing, depending on the chosen zigTypeTag(mod). | 31603 | array, |
| 31607 | // * ErrorSet: this is an override | 31604 | /// The type must be a vector. |
| 31608 | // * ErrorUnion: this is an override of the error set only | 31605 | vector, |
| 31609 | // * other: at the end we make an ErrorUnion with the other thing and this | 31606 | /// The type must be a C pointer. |
| 31610 | var err_set_ty: ?Type = null; | 31607 | c_ptr, |
| 31611 | var any_are_null = false; | 31608 | /// The type must be a pointer (C or not). |
| 31612 | var seen_const = false; | 31609 | /// If refined no further, it is a non-C pointer. |
| 31613 | var convert_to_slice = false; | 31610 | ptr, |
| 31614 | var chosen_i: usize = 0; | 31611 | /// The type must be a function or a pointer to a function. |
| 31615 | for (instructions[1..], 0..) |candidate, candidate_i| { | 31612 | /// If refined no further, it is a function. |
| 31616 | const candidate_ty = sema.typeOf(candidate); | 31613 | func, |
| 31617 | const chosen_ty = sema.typeOf(chosen); | 31614 | /// The type must be an enum literal, or some specific enum or union. Which one is decided |
| 31618 | | 31615 | /// afterwards based on the types in question. |
| 31619 | const candidate_ty_tag = try candidate_ty.zigTypeTagOrPoison(mod); | 31616 | enum_or_union, |
| 31620 | const chosen_ty_tag = try chosen_ty.zigTypeTagOrPoison(mod); | 31617 | /// The type must be some integer or float type. |
| 31621 | | 31618 | /// If refined no further, it is `comptime_int`. |
| 31622 | // If the candidate can coerce into our chosen type, we're done. | 31619 | comptime_int, |
| 31623 | // If the chosen type can coerce into the candidate, use that. | 31620 | /// The type must be some float type. |
| 31624 | if ((try sema.coerceInMemoryAllowed(block, chosen_ty, candidate_ty, false, target, src, src)) == .ok) { | 31621 | /// If refined no further, it is `comptime_float`. |
| 31625 | continue; | 31622 | comptime_float, |
| 31626 | } | 31623 | /// The type must be some float or fixed-width integer type. |
| 31627 | if ((try sema.coerceInMemoryAllowed(block, candidate_ty, chosen_ty, false, target, src, src)) == .ok) { | 31624 | /// If refined no further, it is some fixed-width integer type. |
| 31628 | chosen = candidate; | 31625 | fixed_int, |
| 31629 | chosen_i = candidate_i + 1; | 31626 | /// The type must be some fixed-width float type. |
| 31630 | continue; | 31627 | fixed_float, |
| | 31628 | /// The type must be a struct literal or tuple type. |
| | 31629 | coercible_struct, |
| | 31630 | /// The peers must all be of the same type. |
| | 31631 | exact, |
| | 31632 | |
| | 31633 | const Reason = struct { |
| | 31634 | peers: std.DynamicBitSet, |
| | 31635 | fn reset(r: *Reason) void { |
| | 31636 | r.peers.setRangeValue(.{ .start = 0, .end = r.peers.capacity() }, false); |
| 31631 | } | 31637 | } |
| | 31638 | }; |
| 31632 | | 31639 | |
| 31633 | switch (candidate_ty_tag) { | 31640 | fn name(s: PeerResolveStrategy) []const u8 { |
| 31634 | .NoReturn, .Undefined => continue, | 31641 | return switch (s) { |
| 31635 | | 31642 | .unknown, .exact => "exact", |
| 31636 | .Null => { | 31643 | .error_set => "error set", |
| 31637 | any_are_null = true; | 31644 | .error_union => "error union", |
| 31638 | continue; | 31645 | .nullable => "null", |
| 31639 | }, | 31646 | .optional => "optional", |
| | 31647 | .array => "array", |
| | 31648 | .vector => "vector", |
| | 31649 | .c_ptr => "C pointer", |
| | 31650 | .ptr => "pointer", |
| | 31651 | .func => "function", |
| | 31652 | .enum_or_union => "enum or union", |
| | 31653 | .comptime_int => "comptime_int", |
| | 31654 | .comptime_float => "comptime_float", |
| | 31655 | .fixed_int => "fixed-width int", |
| | 31656 | .fixed_float => "fixed-width float", |
| | 31657 | .coercible_struct => "anonymous struct or tuple", |
| | 31658 | }; |
| | 31659 | } |
| 31640 | | 31660 | |
| 31641 | .Int => switch (chosen_ty_tag) { | 31661 | /// Given two strategies, find a strategy that satisfies both, if one exists. If no such |
| 31642 | .ComptimeInt => { | 31662 | /// strategy exists, any strategy may be returned; an error will be emitted when the caller |
| 31643 | chosen = candidate; | 31663 | /// attempts to use the strategy to resolve the type. |
| 31644 | chosen_i = candidate_i + 1; | 31664 | /// Strategy `a` comes from the peers set in `reason`, while strategy `b` comes from the peer at |
| 31645 | continue; | 31665 | /// index `b_peer_idx`. `reason` will be updated to reflect the reason for the new strategy. |
| 31646 | }, | 31666 | fn merge(a: PeerResolveStrategy, b: PeerResolveStrategy, reason: *Reason, b_peer_idx: usize) PeerResolveStrategy { |
| 31647 | .Int => { | 31667 | // Our merging should be order-independent. Thus, even though the union order is arbitrary, |
| 31648 | const chosen_info = chosen_ty.intInfo(mod); | 31668 | // by sorting the tags and switching first on the smaller, we have half as many cases to |
| 31649 | const candidate_info = candidate_ty.intInfo(mod); | 31669 | // worry about (since we avoid the duplicates). |
| | 31670 | const s0_is_a = @enumToInt(a) <= @enumToInt(b); |
| | 31671 | const s0 = if (s0_is_a) a else b; |
| | 31672 | const s1 = if (s0_is_a) b else a; |
| | 31673 | |
| | 31674 | const ReasonMethod = enum { |
| | 31675 | all_s0, |
| | 31676 | all_s1, |
| | 31677 | either, |
| | 31678 | both, |
| | 31679 | }; |
| 31650 | | 31680 | |
| 31651 | if (chosen_info.bits < candidate_info.bits) { | 31681 | const res: struct { ReasonMethod, PeerResolveStrategy } = switch (s0) { |
| 31652 | chosen = candidate; | 31682 | .unknown => .{ .all_s1, s1 }, |
| 31653 | chosen_i = candidate_i + 1; | 31683 | .error_set => switch (s1) { |
| 31654 | } | 31684 | .error_set => .{ .either, .error_set }, |
| 31655 | continue; | 31685 | else => .{ .both, .error_union }, |
| 31656 | }, | | |
| 31657 | .Pointer => if (chosen_ty.ptrSize(mod) == .C) continue, | | |
| 31658 | else => {}, | | |
| 31659 | }, | 31686 | }, |
| 31660 | .ComptimeInt => switch (chosen_ty_tag) { | 31687 | .error_union => switch (s1) { |
| 31661 | .Int, .Float, .ComptimeFloat => continue, | 31688 | .error_union => .{ .either, .error_union }, |
| 31662 | .Pointer => if (chosen_ty.ptrSize(mod) == .C) continue, | 31689 | else => .{ .all_s0, .error_union }, |
| 31663 | else => {}, | | |
| 31664 | }, | 31690 | }, |
| 31665 | .Float => switch (chosen_ty_tag) { | 31691 | .nullable => switch (s1) { |
| 31666 | .Float => { | 31692 | .nullable => .{ .either, .nullable }, |
| 31667 | if (chosen_ty.floatBits(target) < candidate_ty.floatBits(target)) { | 31693 | .c_ptr => .{ .all_s1, .c_ptr }, |
| 31668 | chosen = candidate; | 31694 | else => .{ .both, .optional }, |
| 31669 | chosen_i = candidate_i + 1; | | |
| 31670 | } | | |
| 31671 | continue; | | |
| 31672 | }, | | |
| 31673 | .ComptimeFloat, .ComptimeInt => { | | |
| 31674 | chosen = candidate; | | |
| 31675 | chosen_i = candidate_i + 1; | | |
| 31676 | continue; | | |
| 31677 | }, | | |
| 31678 | else => {}, | | |
| 31679 | }, | 31695 | }, |
| 31680 | .ComptimeFloat => switch (chosen_ty_tag) { | 31696 | .optional => switch (s1) { |
| 31681 | .Float => continue, | 31697 | .optional => .{ .either, .optional }, |
| 31682 | .ComptimeInt => { | 31698 | .c_ptr => .{ .all_s1, .c_ptr }, |
| 31683 | chosen = candidate; | 31699 | else => .{ .all_s0, .optional }, |
| 31684 | chosen_i = candidate_i + 1; | | |
| 31685 | continue; | | |
| 31686 | }, | | |
| 31687 | else => {}, | | |
| 31688 | }, | 31700 | }, |
| 31689 | .Enum => switch (chosen_ty_tag) { | 31701 | .array => switch (s1) { |
| 31690 | .EnumLiteral => { | 31702 | .array => .{ .either, .array }, |
| 31691 | chosen = candidate; | 31703 | .vector => .{ .all_s1, .vector }, |
| 31692 | chosen_i = candidate_i + 1; | 31704 | else => .{ .all_s0, .array }, |
| 31693 | continue; | | |
| 31694 | }, | | |
| 31695 | .Union => continue, | | |
| 31696 | else => {}, | | |
| 31697 | }, | 31705 | }, |
| 31698 | .EnumLiteral => switch (chosen_ty_tag) { | 31706 | .vector => switch (s1) { |
| 31699 | .Enum, .Union => continue, | 31707 | .vector => .{ .either, .vector }, |
| 31700 | else => {}, | 31708 | else => .{ .all_s0, .vector }, |
| 31701 | }, | 31709 | }, |
| 31702 | .Union => switch (chosen_ty_tag) { | 31710 | .c_ptr => switch (s1) { |
| 31703 | .Enum, .EnumLiteral => { | 31711 | .c_ptr => .{ .either, .c_ptr }, |
| 31704 | chosen = candidate; | 31712 | else => .{ .all_s0, .c_ptr }, |
| 31705 | chosen_i = candidate_i + 1; | | |
| 31706 | continue; | | |
| 31707 | }, | | |
| 31708 | else => {}, | | |
| 31709 | }, | 31713 | }, |
| 31710 | .ErrorSet => switch (chosen_ty_tag) { | 31714 | .ptr => switch (s1) { |
| 31711 | .ErrorSet => { | 31715 | .ptr => .{ .either, .ptr }, |
| 31712 | // If chosen is superset of candidate, keep it. | 31716 | else => .{ .all_s0, .ptr }, |
| 31713 | // If candidate is superset of chosen, switch it. | 31717 | }, |
| 31714 | // If neither is a superset, merge errors. | 31718 | .func => switch (s1) { |
| 31715 | const chosen_set_ty = err_set_ty orelse chosen_ty; | 31719 | .func => .{ .either, .func }, |
| | 31720 | else => .{ .all_s1, s1 }, // doesn't override anything later |
| | 31721 | }, |
| | 31722 | .enum_or_union => switch (s1) { |
| | 31723 | .enum_or_union => .{ .either, .enum_or_union }, |
| | 31724 | else => .{ .all_s0, .enum_or_union }, |
| | 31725 | }, |
| | 31726 | .comptime_int => switch (s1) { |
| | 31727 | .comptime_int => .{ .either, .comptime_int }, |
| | 31728 | else => .{ .all_s1, s1 }, // doesn't override anything later |
| | 31729 | }, |
| | 31730 | .comptime_float => switch (s1) { |
| | 31731 | .comptime_float => .{ .either, .comptime_float }, |
| | 31732 | else => .{ .all_s1, s1 }, // doesn't override anything later |
| | 31733 | }, |
| | 31734 | .fixed_int => switch (s1) { |
| | 31735 | .fixed_int => .{ .either, .fixed_int }, |
| | 31736 | else => .{ .all_s1, s1 }, // doesn't override anything later |
| | 31737 | }, |
| | 31738 | .fixed_float => switch (s1) { |
| | 31739 | .fixed_float => .{ .either, .fixed_float }, |
| | 31740 | else => .{ .all_s1, s1 }, // doesn't override anything later |
| | 31741 | }, |
| | 31742 | .coercible_struct => switch (s1) { |
| | 31743 | .exact => .{ .all_s1, .exact }, |
| | 31744 | else => .{ .all_s0, .coercible_struct }, |
| | 31745 | }, |
| | 31746 | .exact => .{ .all_s0, .exact }, |
| | 31747 | }; |
| 31716 | | 31748 | |
| 31717 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_ty, src, src)) { | 31749 | switch (res[0]) { |
| 31718 | continue; | 31750 | .all_s0 => { |
| 31719 | } | 31751 | if (!s0_is_a) { |
| 31720 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_ty, chosen_set_ty, src, src)) { | 31752 | reason.reset(); |
| 31721 | err_set_ty = null; | 31753 | reason.peers.set(b_peer_idx); |
| 31722 | chosen = candidate; | 31754 | } |
| 31723 | chosen_i = candidate_i + 1; | 31755 | }, |
| 31724 | continue; | 31756 | .all_s1 => { |
| 31725 | } | 31757 | if (s0_is_a) { |
| | 31758 | reason.reset(); |
| | 31759 | reason.peers.set(b_peer_idx); |
| | 31760 | } |
| | 31761 | }, |
| | 31762 | .either => { |
| | 31763 | // Prefer b, since it's a single peer |
| | 31764 | reason.reset(); |
| | 31765 | reason.peers.set(b_peer_idx); |
| | 31766 | }, |
| | 31767 | .both => { |
| | 31768 | reason.peers.set(b_peer_idx); |
| | 31769 | }, |
| | 31770 | } |
| 31726 | | 31771 | |
| 31727 | err_set_ty = try sema.errorSetMerge(chosen_set_ty, candidate_ty); | 31772 | return res[1]; |
| 31728 | continue; | 31773 | } |
| 31729 | }, | | |
| 31730 | .ErrorUnion => { | | |
| 31731 | const chosen_set_ty = err_set_ty orelse chosen_ty.errorUnionSet(mod); | | |
| 31732 | | 31774 | |
| 31733 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_ty, src, src)) { | 31775 | fn select(ty: Type, mod: *Module) PeerResolveStrategy { |
| 31734 | continue; | 31776 | return switch (ty.zigTypeTag(mod)) { |
| 31735 | } | 31777 | .Type, .Void, .Bool, .Opaque, .Frame, .AnyFrame => .exact, |
| 31736 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_ty, chosen_set_ty, src, src)) { | 31778 | .NoReturn, .Undefined => .unknown, |
| 31737 | err_set_ty = candidate_ty; | 31779 | .Null => .nullable, |
| 31738 | continue; | 31780 | .ComptimeInt => .comptime_int, |
| | 31781 | .Int => .fixed_int, |
| | 31782 | .ComptimeFloat => .comptime_float, |
| | 31783 | .Float => .fixed_float, |
| | 31784 | .Pointer => if (ty.ptrInfo(mod).size == .C) .c_ptr else .ptr, |
| | 31785 | .Array => .array, |
| | 31786 | .Vector => .vector, |
| | 31787 | .Optional => .optional, |
| | 31788 | .ErrorSet => .error_set, |
| | 31789 | .ErrorUnion => .error_union, |
| | 31790 | .EnumLiteral, .Enum, .Union => .enum_or_union, |
| | 31791 | .Struct => if (ty.isTupleOrAnonStruct(mod)) .coercible_struct else .exact, |
| | 31792 | .Fn => .func, |
| | 31793 | }; |
| | 31794 | } |
| | 31795 | }; |
| | 31796 | |
| | 31797 | const PeerResolveResult = union(enum) { |
| | 31798 | /// The peer type resolution was successful, and resulted in the given type. |
| | 31799 | success: Type, |
| | 31800 | /// The chosen strategy was incompatible with the given peer. |
| | 31801 | bad_strat: struct { |
| | 31802 | strat: PeerResolveStrategy, |
| | 31803 | peer_idx: usize, |
| | 31804 | }, |
| | 31805 | /// There was some conflict between two specific peers. |
| | 31806 | conflict: struct { |
| | 31807 | peer_idx_a: usize, |
| | 31808 | peer_idx_b: usize, |
| | 31809 | }, |
| | 31810 | /// There was an error when resolving the type of a struct or tuple field. |
| | 31811 | field_error: struct { |
| | 31812 | /// The name of the field which caused the failure. |
| | 31813 | field_name: []const u8, |
| | 31814 | /// The type of this field in each peer. |
| | 31815 | field_types: []Type, |
| | 31816 | /// The error from resolving the field type. Guaranteed not to be `success`. |
| | 31817 | sub_result: *PeerResolveResult, |
| | 31818 | }, |
| | 31819 | |
| | 31820 | fn report( |
| | 31821 | result: PeerResolveResult, |
| | 31822 | sema: *Sema, |
| | 31823 | block: *Block, |
| | 31824 | src: LazySrcLoc, |
| | 31825 | instructions: []const Air.Inst.Ref, |
| | 31826 | candidate_srcs: Module.PeerTypeCandidateSrc, |
| | 31827 | strat_reason: PeerResolveStrategy.Reason, |
| | 31828 | ) !*Module.ErrorMsg { |
| | 31829 | const mod = sema.mod; |
| | 31830 | const decl_ptr = mod.declPtr(block.src_decl); |
| | 31831 | |
| | 31832 | var opt_msg: ?*Module.ErrorMsg = null; |
| | 31833 | errdefer if (opt_msg) |msg| msg.destroy(sema.gpa); |
| | 31834 | |
| | 31835 | // If we mention fields we'll want to include field types, so put peer types in a buffer |
| | 31836 | var peer_tys = try sema.arena.alloc(Type, instructions.len); |
| | 31837 | for (peer_tys, instructions) |*ty, inst| { |
| | 31838 | ty.* = sema.typeOf(inst); |
| | 31839 | } |
| | 31840 | |
| | 31841 | var cur = result; |
| | 31842 | while (true) { |
| | 31843 | var conflict_idx: [2]usize = undefined; |
| | 31844 | |
| | 31845 | switch (cur) { |
| | 31846 | .success => unreachable, |
| | 31847 | .bad_strat => |bad_strat| bad_strat: { |
| | 31848 | if (strat_reason.peers.count() == 1) { |
| | 31849 | // We can write this error more simply as a conflict between two peers |
| | 31850 | conflict_idx = .{ |
| | 31851 | strat_reason.peers.findFirstSet().?, |
| | 31852 | bad_strat.peer_idx, |
| | 31853 | }; |
| | 31854 | break :bad_strat; |
| 31739 | } | 31855 | } |
| 31740 | | 31856 | |
| 31741 | err_set_ty = try sema.errorSetMerge(chosen_set_ty, candidate_ty); | 31857 | const fmt = "type resolution strategy failed"; |
| 31742 | continue; | 31858 | const msg = if (opt_msg) |msg| msg: { |
| 31743 | }, | 31859 | try sema.errNote(block, src, msg, fmt, .{}); |
| 31744 | else => { | 31860 | break :msg msg; |
| 31745 | if (err_set_ty) |chosen_set_ty| { | 31861 | } else msg: { |
| 31746 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_ty, src, src)) { | 31862 | const msg = try sema.errMsg(block, src, fmt, .{}); |
| 31747 | continue; | 31863 | opt_msg = msg; |
| 31748 | } | 31864 | break :msg msg; |
| 31749 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_ty, chosen_set_ty, src, src)) { | 31865 | }; |
| 31750 | err_set_ty = candidate_ty; | | |
| 31751 | continue; | | |
| 31752 | } | | |
| 31753 | | 31866 | |
| 31754 | err_set_ty = try sema.errorSetMerge(chosen_set_ty, candidate_ty); | 31867 | const peer_ty = peer_tys[bad_strat.peer_idx]; |
| 31755 | continue; | 31868 | const peer_src = candidate_srcs.resolve(mod, decl_ptr, bad_strat.peer_idx) orelse src; |
| 31756 | } else { | 31869 | try sema.errNote(block, peer_src, msg, "strategy '{s}' failed for type '{}' here", .{ bad_strat.strat.name(), peer_ty.fmt(mod) }); |
| 31757 | err_set_ty = candidate_ty; | 31870 | |
| 31758 | continue; | 31871 | try sema.errNote(block, src, msg, "strategy chosen using {} peers", .{strat_reason.peers.count()}); |
| | 31872 | var it = strat_reason.peers.iterator(.{}); |
| | 31873 | while (it.next()) |strat_peer_idx| { |
| | 31874 | const strat_peer_ty = peer_tys[strat_peer_idx]; |
| | 31875 | const strat_peer_src = candidate_srcs.resolve(mod, decl_ptr, strat_peer_idx) orelse src; |
| | 31876 | try sema.errNote(block, strat_peer_src, msg, "peer of type '{}' here", .{strat_peer_ty.fmt(mod)}); |
| 31759 | } | 31877 | } |
| 31760 | }, | | |
| 31761 | }, | | |
| 31762 | .ErrorUnion => switch (chosen_ty_tag) { | | |
| 31763 | .ErrorSet => { | | |
| 31764 | const chosen_set_ty = err_set_ty orelse chosen_ty; | | |
| 31765 | const candidate_set_ty = candidate_ty.errorUnionSet(mod); | | |
| 31766 | | 31878 | |
| 31767 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_set_ty, src, src)) { | 31879 | // No child error |
| 31768 | err_set_ty = chosen_set_ty; | 31880 | break; |
| 31769 | } else if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_set_ty, chosen_set_ty, src, src)) { | 31881 | }, |
| 31770 | err_set_ty = null; | 31882 | .conflict => |conflict| { |
| | 31883 | // Fall through to two-peer conflict handling below |
| | 31884 | conflict_idx = .{ |
| | 31885 | conflict.peer_idx_a, |
| | 31886 | conflict.peer_idx_b, |
| | 31887 | }; |
| | 31888 | }, |
| | 31889 | .field_error => |field_error| { |
| | 31890 | const fmt = "struct field '{s}' has conflicting types"; |
| | 31891 | const args = .{field_error.field_name}; |
| | 31892 | if (opt_msg) |msg| { |
| | 31893 | try sema.errNote(block, src, msg, fmt, args); |
| 31771 | } else { | 31894 | } else { |
| 31772 | err_set_ty = try sema.errorSetMerge(chosen_set_ty, candidate_set_ty); | 31895 | opt_msg = try sema.errMsg(block, src, fmt, args); |
| 31773 | } | 31896 | } |
| 31774 | chosen = candidate; | 31897 | |
| 31775 | chosen_i = candidate_i + 1; | 31898 | // Continue on to child error |
| | 31899 | cur = field_error.sub_result.*; |
| | 31900 | peer_tys = field_error.field_types; |
| 31776 | continue; | 31901 | continue; |
| 31777 | }, | 31902 | }, |
| | 31903 | } |
| 31778 | | 31904 | |
| 31779 | .ErrorUnion => { | 31905 | // This is the path for reporting a conflict between two peers. |
| 31780 | const chosen_payload_ty = chosen_ty.errorUnionPayload(mod); | | |
| 31781 | const candidate_payload_ty = candidate_ty.errorUnionPayload(mod); | | |
| 31782 | | 31906 | |
| 31783 | const coerce_chosen = (try sema.coerceInMemoryAllowed(block, chosen_payload_ty, candidate_payload_ty, false, target, src, src)) == .ok; | 31907 | if (conflict_idx[1] < conflict_idx[0]) { |
| 31784 | const coerce_candidate = (try sema.coerceInMemoryAllowed(block, candidate_payload_ty, chosen_payload_ty, false, target, src, src)) == .ok; | 31908 | // b comes first in source, so it's better if it comes first in the error |
| | 31909 | std.mem.swap(usize, &conflict_idx[0], &conflict_idx[1]); |
| | 31910 | } |
| 31785 | | 31911 | |
| 31786 | if (coerce_chosen or coerce_candidate) { | 31912 | const conflict_tys: [2]Type = .{ |
| 31787 | // If we can coerce to the candidate, we switch to that | 31913 | peer_tys[conflict_idx[0]], |
| 31788 | // type. This is the same logic as the bare (non-union) | 31914 | peer_tys[conflict_idx[1]], |
| 31789 | // coercion check we do at the top of this func. | 31915 | }; |
| 31790 | if (coerce_candidate) { | 31916 | const conflict_srcs: [2]?LazySrcLoc = .{ |
| 31791 | chosen = candidate; | 31917 | candidate_srcs.resolve(mod, decl_ptr, conflict_idx[0]), |
| 31792 | chosen_i = candidate_i + 1; | 31918 | candidate_srcs.resolve(mod, decl_ptr, conflict_idx[1]), |
| 31793 | } | 31919 | }; |
| 31794 | | 31920 | |
| 31795 | const chosen_set_ty = err_set_ty orelse chosen_ty.errorUnionSet(mod); | 31921 | const fmt = "incompatible types: '{}' and '{}'"; |
| 31796 | const candidate_set_ty = candidate_ty.errorUnionSet(mod); | 31922 | const args = .{ |
| | 31923 | conflict_tys[0].fmt(mod), |
| | 31924 | conflict_tys[1].fmt(mod), |
| | 31925 | }; |
| | 31926 | const msg = if (opt_msg) |msg| msg: { |
| | 31927 | try sema.errNote(block, src, msg, fmt, args); |
| | 31928 | break :msg msg; |
| | 31929 | } else msg: { |
| | 31930 | const msg = try sema.errMsg(block, src, fmt, args); |
| | 31931 | opt_msg = msg; |
| | 31932 | break :msg msg; |
| | 31933 | }; |
| 31797 | | 31934 | |
| 31798 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_set_ty, src, src)) { | 31935 | if (conflict_srcs[0]) |src_loc| try sema.errNote(block, src_loc, msg, "type '{}' here", .{conflict_tys[0].fmt(mod)}); |
| 31799 | err_set_ty = chosen_set_ty; | 31936 | if (conflict_srcs[1]) |src_loc| try sema.errNote(block, src_loc, msg, "type '{}' here", .{conflict_tys[1].fmt(mod)}); |
| 31800 | } else if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_set_ty, chosen_set_ty, src, src)) { | | |
| 31801 | err_set_ty = candidate_set_ty; | | |
| 31802 | } else { | | |
| 31803 | err_set_ty = try sema.errorSetMerge(chosen_set_ty, candidate_set_ty); | | |
| 31804 | } | | |
| 31805 | continue; | | |
| 31806 | } | | |
| 31807 | }, | | |
| 31808 | | 31937 | |
| 31809 | else => { | 31938 | // No child error |
| 31810 | if (err_set_ty) |chosen_set_ty| { | 31939 | break; |
| 31811 | const candidate_set_ty = candidate_ty.errorUnionSet(mod); | 31940 | } |
| 31812 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_set_ty, src, src)) { | | |
| 31813 | err_set_ty = chosen_set_ty; | | |
| 31814 | } else if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_set_ty, chosen_set_ty, src, src)) { | | |
| 31815 | err_set_ty = null; | | |
| 31816 | } else { | | |
| 31817 | err_set_ty = try sema.errorSetMerge(chosen_set_ty, candidate_set_ty); | | |
| 31818 | } | | |
| 31819 | } | | |
| 31820 | seen_const = seen_const or chosen_ty.isConstPtr(mod); | | |
| 31821 | chosen = candidate; | | |
| 31822 | chosen_i = candidate_i + 1; | | |
| 31823 | continue; | | |
| 31824 | }, | | |
| 31825 | }, | | |
| 31826 | .Pointer => { | | |
| 31827 | const cand_info = candidate_ty.ptrInfo(mod); | | |
| 31828 | switch (chosen_ty_tag) { | | |
| 31829 | .Pointer => { | | |
| 31830 | const chosen_info = chosen_ty.ptrInfo(mod); | | |
| 31831 | | 31941 | |
| 31832 | seen_const = seen_const or !chosen_info.mutable or !cand_info.mutable; | 31942 | return opt_msg.?; |
| | 31943 | } |
| | 31944 | }; |
| 31833 | | 31945 | |
| 31834 | // *[N]T to [*]T | 31946 | fn resolvePeerTypes( |
| 31835 | // *[N]T to []T | 31947 | sema: *Sema, |
| 31836 | if ((cand_info.size == .Many or cand_info.size == .Slice) and | 31948 | block: *Block, |
| 31837 | chosen_info.size == .One and | 31949 | src: LazySrcLoc, |
| 31838 | chosen_info.pointee_type.zigTypeTag(mod) == .Array) | 31950 | instructions: []const Air.Inst.Ref, |
| 31839 | { | 31951 | candidate_srcs: Module.PeerTypeCandidateSrc, |
| 31840 | // In case we see i.e.: `*[1]T`, `*[2]T`, `[*]T` | 31952 | ) !Type { |
| 31841 | convert_to_slice = false; | 31953 | switch (instructions.len) { |
| 31842 | chosen = candidate; | 31954 | 0 => return Type.noreturn, |
| 31843 | chosen_i = candidate_i + 1; | 31955 | 1 => return sema.typeOf(instructions[0]), |
| 31844 | continue; | 31956 | else => {}, |
| 31845 | } | 31957 | } |
| 31846 | if (cand_info.size == .One and | | |
| 31847 | cand_info.pointee_type.zigTypeTag(mod) == .Array and | | |
| 31848 | (chosen_info.size == .Many or chosen_info.size == .Slice)) | | |
| 31849 | { | | |
| 31850 | // In case we see i.e.: `*[1]T`, `*[2]T`, `[*]T` | | |
| 31851 | convert_to_slice = false; | | |
| 31852 | continue; | | |
| 31853 | } | | |
| 31854 | | 31958 | |
| 31855 | // *[N]T and *[M]T | 31959 | var peer_tys = try sema.arena.alloc(?Type, instructions.len); |
| 31856 | // Verify both are single-pointers to arrays. | 31960 | var peer_vals = try sema.arena.alloc(?Value, instructions.len); |
| 31857 | // Keep the one whose element type can be coerced into. | | |
| 31858 | if (chosen_info.size == .One and | | |
| 31859 | cand_info.size == .One and | | |
| 31860 | chosen_info.pointee_type.zigTypeTag(mod) == .Array and | | |
| 31861 | cand_info.pointee_type.zigTypeTag(mod) == .Array) | | |
| 31862 | { | | |
| 31863 | const chosen_elem_ty = chosen_info.pointee_type.childType(mod); | | |
| 31864 | const cand_elem_ty = cand_info.pointee_type.childType(mod); | | |
| 31865 | | 31961 | |
| 31866 | const chosen_ok = .ok == try sema.coerceInMemoryAllowed(block, chosen_elem_ty, cand_elem_ty, chosen_info.mutable, target, src, src); | 31962 | for (instructions, peer_tys, peer_vals) |inst, *ty, *val| { |
| 31867 | if (chosen_ok) { | 31963 | ty.* = sema.typeOf(inst); |
| 31868 | convert_to_slice = true; | 31964 | val.* = try sema.resolveMaybeUndefVal(inst); |
| 31869 | continue; | 31965 | } |
| 31870 | } | | |
| 31871 | | 31966 | |
| 31872 | const cand_ok = .ok == try sema.coerceInMemoryAllowed(block, cand_elem_ty, chosen_elem_ty, cand_info.mutable, target, src, src); | 31967 | var strat_reason: PeerResolveStrategy.Reason = .{ |
| 31873 | if (cand_ok) { | 31968 | .peers = try std.DynamicBitSet.initEmpty(sema.arena, instructions.len), |
| 31874 | convert_to_slice = true; | 31969 | }; |
| 31875 | chosen = candidate; | | |
| 31876 | chosen_i = candidate_i + 1; | | |
| 31877 | continue; | | |
| 31878 | } | | |
| 31879 | | 31970 | |
| 31880 | // They're both bad. Report error. | 31971 | switch (try sema.resolvePeerTypesInner(block, src, peer_tys, peer_vals, &strat_reason)) { |
| 31881 | // In the future we probably want to use the | 31972 | .success => |ty| return ty, |
| 31882 | // coerceInMemoryAllowed error reporting mechanism, | 31973 | else => |result| { |
| 31883 | // however, for now we just fall through for the | 31974 | const msg = try result.report(sema, block, src, instructions, candidate_srcs, strat_reason); |
| 31884 | // "incompatible types" error below. | 31975 | return sema.failWithOwnedErrorMsg(msg); |
| 31885 | } | 31976 | }, |
| | 31977 | } |
| | 31978 | } |
| 31886 | | 31979 | |
| 31887 | // [*c]T and any other pointer size | 31980 | fn resolvePeerTypesInner( |
| 31888 | // Whichever element type can coerce to the other one, is | 31981 | sema: *Sema, |
| 31889 | // the one we will keep. If they're both OK then we keep the | 31982 | block: *Block, |
| 31890 | // C pointer since it matches both single and many pointers. | 31983 | src: LazySrcLoc, |
| 31891 | if (cand_info.size == .C or chosen_info.size == .C) { | 31984 | peer_tys: []?Type, |
| 31892 | const cand_ok = .ok == try sema.coerceInMemoryAllowed(block, cand_info.pointee_type, chosen_info.pointee_type, cand_info.mutable, target, src, src); | 31985 | peer_vals: []?Value, |
| 31893 | const chosen_ok = .ok == try sema.coerceInMemoryAllowed(block, chosen_info.pointee_type, cand_info.pointee_type, chosen_info.mutable, target, src, src); | 31986 | strat_reason: *PeerResolveStrategy.Reason, |
| 31894 | | 31987 | ) !PeerResolveResult { |
| 31895 | if (cand_ok) { | 31988 | const mod = sema.mod; |
| 31896 | if (chosen_ok) { | 31989 | |
| 31897 | if (chosen_info.size == .C) { | 31990 | strat_reason.reset(); |
| 31898 | continue; | 31991 | |
| 31899 | } else { | 31992 | var s: PeerResolveStrategy = .unknown; |
| 31900 | chosen = candidate; | 31993 | for (peer_tys, 0..) |opt_ty, i| { |
| 31901 | chosen_i = candidate_i + 1; | 31994 | const ty = opt_ty orelse continue; |
| 31902 | continue; | 31995 | s = s.merge(PeerResolveStrategy.select(ty, mod), strat_reason, i); |
| 31903 | } | 31996 | } |
| 31904 | } else { | 31997 | |
| 31905 | chosen = candidate; | 31998 | if (s == .unknown) { |
| 31906 | chosen_i = candidate_i + 1; | 31999 | // The whole thing was noreturn or undefined - try to do an exact match |
| 31907 | continue; | 32000 | s = .exact; |
| 31908 | } | 32001 | } else { |
| 31909 | } else { | 32002 | // There was something other than noreturn and undefined, so we can ignore those peers |
| 31910 | if (chosen_ok) { | 32003 | for (peer_tys) |*ty_ptr| { |
| 31911 | continue; | 32004 | const ty = ty_ptr.* orelse continue; |
| 31912 | } else { | 32005 | switch (ty.zigTypeTag(mod)) { |
| 31913 | // They're both bad. Report error. | 32006 | .NoReturn, .Undefined => ty_ptr.* = null, |
| 31914 | // In the future we probably want to use the | 32007 | else => {}, |
| 31915 | // coerceInMemoryAllowed error reporting mechanism, | 32008 | } |
| 31916 | // however, for now we just fall through for the | 32009 | } |
| 31917 | // "incompatible types" error below. | 32010 | } |
| 31918 | } | 32011 | |
| 31919 | } | 32012 | const target = mod.getTarget(); |
| 31920 | } | 32013 | |
| | 32014 | switch (s) { |
| | 32015 | .unknown => unreachable, |
| | 32016 | |
| | 32017 | .error_set => { |
| | 32018 | var final_set: ?Type = null; |
| | 32019 | for (peer_tys, 0..) |opt_ty, i| { |
| | 32020 | const ty = opt_ty orelse continue; |
| | 32021 | if (ty.zigTypeTag(mod) != .ErrorSet) return .{ .bad_strat = .{ |
| | 32022 | .strat = s, |
| | 32023 | .peer_idx = i, |
| | 32024 | } }; |
| | 32025 | if (final_set) |cur_set| { |
| | 32026 | final_set = try sema.maybeMergeErrorSets(block, src, cur_set, ty); |
| | 32027 | } else { |
| | 32028 | final_set = ty; |
| | 32029 | } |
| | 32030 | } |
| | 32031 | return .{ .success = final_set.? }; |
| | 32032 | }, |
| | 32033 | |
| | 32034 | .error_union => { |
| | 32035 | var final_set: ?Type = null; |
| | 32036 | for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| { |
| | 32037 | const ty = ty_ptr.* orelse continue; |
| | 32038 | const set_ty = switch (ty.zigTypeTag(mod)) { |
| | 32039 | .ErrorSet => blk: { |
| | 32040 | ty_ptr.* = null; // no payload to decide on |
| | 32041 | val_ptr.* = null; |
| | 32042 | break :blk ty; |
| 31921 | }, | 32043 | }, |
| 31922 | .Int, .ComptimeInt => { | 32044 | .ErrorUnion => blk: { |
| 31923 | if (cand_info.size == .C) { | 32045 | const set_ty = ty.errorUnionSet(mod); |
| 31924 | chosen = candidate; | 32046 | ty_ptr.* = ty.errorUnionPayload(mod); |
| 31925 | chosen_i = candidate_i + 1; | 32047 | if (val_ptr.*) |eu_val| switch (mod.intern_pool.indexToKey(eu_val.toIntern())) { |
| 31926 | continue; | 32048 | .error_union => |eu| switch (eu.val) { |
| 31927 | } | 32049 | .payload => |payload_ip| val_ptr.* = payload_ip.toValue(), |
| | 32050 | .err_name => val_ptr.* = null, |
| | 32051 | }, |
| | 32052 | .undef => val_ptr.* = (try sema.mod.intern(.{ .undef = ty_ptr.*.?.toIntern() })).toValue(), |
| | 32053 | else => unreachable, |
| | 32054 | }; |
| | 32055 | break :blk set_ty; |
| 31928 | }, | 32056 | }, |
| 31929 | .Optional => { | 32057 | else => continue, // whole type is the payload |
| 31930 | const chosen_ptr_ty = chosen_ty.optionalChild(mod); | 32058 | }; |
| 31931 | if (chosen_ptr_ty.zigTypeTag(mod) == .Pointer) { | 32059 | if (final_set) |cur_set| { |
| 31932 | const chosen_info = chosen_ptr_ty.ptrInfo(mod); | 32060 | final_set = try sema.maybeMergeErrorSets(block, src, cur_set, set_ty); |
| | 32061 | } else { |
| | 32062 | final_set = set_ty; |
| | 32063 | } |
| | 32064 | } |
| | 32065 | assert(final_set != null); |
| | 32066 | const final_payload = switch (try sema.resolvePeerTypesInner( |
| | 32067 | block, |
| | 32068 | src, |
| | 32069 | peer_tys, |
| | 32070 | peer_vals, |
| | 32071 | strat_reason, |
| | 32072 | )) { |
| | 32073 | .success => |ty| ty, |
| | 32074 | else => |result| return result, |
| | 32075 | }; |
| | 32076 | return .{ .success = try mod.errorUnionType(final_set.?, final_payload) }; |
| | 32077 | }, |
| 31933 | | 32078 | |
| 31934 | seen_const = seen_const or !chosen_info.mutable or !cand_info.mutable; | 32079 | .nullable => { |
| | 32080 | for (peer_tys, 0..) |opt_ty, i| { |
| | 32081 | const ty = opt_ty orelse continue; |
| | 32082 | if (!ty.eql(Type.null, mod)) return .{ .bad_strat = .{ |
| | 32083 | .strat = s, |
| | 32084 | .peer_idx = i, |
| | 32085 | } }; |
| | 32086 | } |
| | 32087 | return .{ .success = Type.null }; |
| | 32088 | }, |
| 31935 | | 32089 | |
| 31936 | // *[N]T to ?![*]T | 32090 | .optional => { |
| 31937 | // *[N]T to ?![]T | 32091 | for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| { |
| 31938 | if (cand_info.size == .One and | 32092 | const ty = ty_ptr.* orelse continue; |
| 31939 | cand_info.pointee_type.zigTypeTag(mod) == .Array and | 32093 | switch (ty.zigTypeTag(mod)) { |
| 31940 | (chosen_info.size == .Many or chosen_info.size == .Slice)) | 32094 | .Null => { |
| 31941 | { | 32095 | ty_ptr.* = null; |
| 31942 | continue; | 32096 | val_ptr.* = null; |
| 31943 | } | | |
| 31944 | } | | |
| 31945 | }, | | |
| 31946 | .ErrorUnion => { | | |
| 31947 | const chosen_ptr_ty = chosen_ty.errorUnionPayload(mod); | | |
| 31948 | if (chosen_ptr_ty.zigTypeTag(mod) == .Pointer) { | | |
| 31949 | const chosen_info = chosen_ptr_ty.ptrInfo(mod); | | |
| 31950 | | | |
| 31951 | seen_const = seen_const or !chosen_info.mutable or !cand_info.mutable; | | |
| 31952 | | | |
| 31953 | // *[N]T to E![*]T | | |
| 31954 | // *[N]T to E![]T | | |
| 31955 | if (cand_info.size == .One and | | |
| 31956 | cand_info.pointee_type.zigTypeTag(mod) == .Array and | | |
| 31957 | (chosen_info.size == .Many or chosen_info.size == .Slice)) | | |
| 31958 | { | | |
| 31959 | continue; | | |
| 31960 | } | | |
| 31961 | } | | |
| 31962 | }, | 32097 | }, |
| 31963 | .Fn => { | 32098 | .Optional => { |
| 31964 | if (!cand_info.mutable and cand_info.pointee_type.zigTypeTag(mod) == .Fn and .ok == try sema.coerceInMemoryAllowedFns(block, chosen_ty, cand_info.pointee_type, target, src, src)) { | 32099 | ty_ptr.* = ty.optionalChild(mod); |
| 31965 | chosen = candidate; | 32100 | if (val_ptr.*) |opt_val| val_ptr.* = if (!opt_val.isUndef(mod)) opt_val.optionalValue(mod) else null; |
| 31966 | chosen_i = candidate_i + 1; | | |
| 31967 | continue; | | |
| 31968 | } | | |
| 31969 | }, | 32101 | }, |
| 31970 | else => {}, | 32102 | else => {}, |
| 31971 | } | 32103 | } |
| 31972 | }, | 32104 | } |
| 31973 | .Optional => { | 32105 | const child_ty = switch (try sema.resolvePeerTypesInner( |
| 31974 | const opt_child_ty = candidate_ty.optionalChild(mod); | 32106 | block, |
| 31975 | if ((try sema.coerceInMemoryAllowed(block, chosen_ty, opt_child_ty, false, target, src, src)) == .ok) { | 32107 | src, |
| 31976 | seen_const = seen_const or opt_child_ty.isConstPtr(mod); | 32108 | peer_tys, |
| 31977 | any_are_null = true; | 32109 | peer_vals, |
| 31978 | continue; | 32110 | strat_reason, |
| 31979 | } | 32111 | )) { |
| | 32112 | .success => |ty| ty, |
| | 32113 | else => |result| return result, |
| | 32114 | }; |
| | 32115 | return .{ .success = try mod.optionalType(child_ty.toIntern()) }; |
| | 32116 | }, |
| 31980 | | 32117 | |
| 31981 | seen_const = seen_const or chosen_ty.isConstPtr(mod); | 32118 | .array => { |
| 31982 | any_are_null = false; | 32119 | // Index of the first non-null peer |
| 31983 | chosen = candidate; | 32120 | var opt_first_idx: ?usize = null; |
| 31984 | chosen_i = candidate_i + 1; | 32121 | // Index of the first array or vector peer (i.e. not a tuple) |
| 31985 | continue; | 32122 | var opt_first_arr_idx: ?usize = null; |
| 31986 | }, | 32123 | // Set to non-null once we see any peer, even a tuple |
| 31987 | .Vector => switch (chosen_ty_tag) { | 32124 | var len: u64 = undefined; |
| 31988 | .Vector => { | 32125 | var sentinel: ?Value = undefined; |
| 31989 | const chosen_len = chosen_ty.vectorLen(mod); | 32126 | // Only set once we see a non-tuple peer |
| 31990 | const candidate_len = candidate_ty.vectorLen(mod); | 32127 | var elem_ty: Type = undefined; |
| 31991 | if (chosen_len != candidate_len) | 32128 | |
| 31992 | continue; | 32129 | for (peer_tys, 0..) |*ty_ptr, i| { |
| | 32130 | const ty = ty_ptr.* orelse continue; |
| | 32131 | |
| | 32132 | if (!ty.isArrayOrVector(mod)) { |
| | 32133 | // We allow tuples of the correct length. We won't validate their elem type, since the elements can be coerced. |
| | 32134 | const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .bad_strat = .{ |
| | 32135 | .strat = s, |
| | 32136 | .peer_idx = i, |
| | 32137 | } }; |
| 31993 | | 32138 | |
| 31994 | const chosen_child_ty = chosen_ty.childType(mod); | 32139 | if (opt_first_idx) |first_idx| { |
| 31995 | const candidate_child_ty = candidate_ty.childType(mod); | 32140 | if (arr_like.len != len) return .{ .conflict = .{ |
| 31996 | if (chosen_child_ty.zigTypeTag(mod) == .Int and candidate_child_ty.zigTypeTag(mod) == .Int) { | 32141 | .peer_idx_a = first_idx, |
| 31997 | const chosen_info = chosen_child_ty.intInfo(mod); | 32142 | .peer_idx_b = i, |
| 31998 | const candidate_info = candidate_child_ty.intInfo(mod); | 32143 | } }; |
| 31999 | if (chosen_info.bits < candidate_info.bits) { | 32144 | } else { |
| 32000 | chosen = candidate; | 32145 | opt_first_idx = i; |
| 32001 | chosen_i = candidate_i + 1; | 32146 | len = arr_like.len; |
| 32002 | } | | |
| 32003 | continue; | | |
| 32004 | } | 32147 | } |
| 32005 | if (chosen_child_ty.zigTypeTag(mod) == .Float and candidate_child_ty.zigTypeTag(mod) == .Float) { | 32148 | |
| 32006 | if (chosen_ty.floatBits(target) < candidate_ty.floatBits(target)) { | 32149 | sentinel = null; |
| 32007 | chosen = candidate; | 32150 | |
| 32008 | chosen_i = candidate_i + 1; | 32151 | continue; |
| 32009 | } | 32152 | } |
| 32010 | continue; | 32153 | |
| | 32154 | const first_arr_idx = opt_first_arr_idx orelse { |
| | 32155 | if (opt_first_idx == null) { |
| | 32156 | opt_first_idx = i; |
| | 32157 | len = ty.arrayLen(mod); |
| | 32158 | sentinel = ty.sentinel(mod); |
| 32011 | } | 32159 | } |
| 32012 | }, | 32160 | opt_first_arr_idx = i; |
| 32013 | .Array => { | 32161 | elem_ty = ty.childType(mod); |
| 32014 | chosen = candidate; | | |
| 32015 | chosen_i = candidate_i + 1; | | |
| 32016 | continue; | | |
| 32017 | }, | | |
| 32018 | else => {}, | | |
| 32019 | }, | | |
| 32020 | .Array => switch (chosen_ty_tag) { | | |
| 32021 | .Vector => continue, | | |
| 32022 | else => {}, | | |
| 32023 | }, | | |
| 32024 | .Fn => if (chosen_ty.isSinglePointer(mod) and chosen_ty.isConstPtr(mod) and chosen_ty.childType(mod).zigTypeTag(mod) == .Fn) { | | |
| 32025 | if (.ok == try sema.coerceInMemoryAllowedFns(block, chosen_ty.childType(mod), candidate_ty, target, src, src)) { | | |
| 32026 | continue; | 32162 | continue; |
| | 32163 | }; |
| | 32164 | |
| | 32165 | if (ty.arrayLen(mod) != len) return .{ .conflict = .{ |
| | 32166 | .peer_idx_a = first_arr_idx, |
| | 32167 | .peer_idx_b = i, |
| | 32168 | } }; |
| | 32169 | |
| | 32170 | if (!ty.childType(mod).eql(elem_ty, mod)) { |
| | 32171 | return .{ .conflict = .{ |
| | 32172 | .peer_idx_a = first_arr_idx, |
| | 32173 | .peer_idx_b = i, |
| | 32174 | } }; |
| 32027 | } | 32175 | } |
| 32028 | }, | | |
| 32029 | else => {}, | | |
| 32030 | } | | |
| 32031 | | 32176 | |
| 32032 | switch (chosen_ty_tag) { | 32177 | if (sentinel) |cur_sent| { |
| 32033 | .NoReturn, .Undefined => { | 32178 | if (ty.sentinel(mod)) |peer_sent| { |
| 32034 | chosen = candidate; | 32179 | if (!peer_sent.eql(cur_sent, elem_ty, mod)) sentinel = null; |
| 32035 | chosen_i = candidate_i + 1; | 32180 | } else { |
| 32036 | continue; | 32181 | sentinel = null; |
| 32037 | }, | 32182 | } |
| 32038 | .Null => { | 32183 | } |
| 32039 | any_are_null = true; | 32184 | } |
| 32040 | chosen = candidate; | 32185 | |
| 32041 | chosen_i = candidate_i + 1; | 32186 | // There should always be at least one array or vector peer |
| 32042 | continue; | 32187 | assert(opt_first_arr_idx != null); |
| 32043 | }, | 32188 | |
| 32044 | .Optional => { | 32189 | return .{ .success = try mod.arrayType(.{ |
| 32045 | const opt_child_ty = chosen_ty.optionalChild(mod); | 32190 | .len = len, |
| 32046 | if ((try sema.coerceInMemoryAllowed(block, opt_child_ty, candidate_ty, false, target, src, src)) == .ok) { | 32191 | .child = elem_ty.toIntern(), |
| | 32192 | .sentinel = if (sentinel) |sent_val| sent_val.toIntern() else .none, |
| | 32193 | }) }; |
| | 32194 | }, |
| | 32195 | |
| | 32196 | .vector => { |
| | 32197 | var len: ?u64 = null; |
| | 32198 | var first_idx: usize = undefined; |
| | 32199 | for (peer_tys, peer_vals, 0..) |*ty_ptr, *val_ptr, i| { |
| | 32200 | const ty = ty_ptr.* orelse continue; |
| | 32201 | |
| | 32202 | if (!ty.isArrayOrVector(mod)) { |
| | 32203 | // Allow tuples of the correct length |
| | 32204 | const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .bad_strat = .{ |
| | 32205 | .strat = s, |
| | 32206 | .peer_idx = i, |
| | 32207 | } }; |
| | 32208 | |
| | 32209 | if (len) |expect_len| { |
| | 32210 | if (arr_like.len != expect_len) return .{ .conflict = .{ |
| | 32211 | .peer_idx_a = first_idx, |
| | 32212 | .peer_idx_b = i, |
| | 32213 | } }; |
| | 32214 | } else { |
| | 32215 | len = arr_like.len; |
| | 32216 | first_idx = i; |
| | 32217 | } |
| | 32218 | |
| | 32219 | // Tuples won't participate in the child type resolution. We'll resolve without |
| | 32220 | // them, and if the tuples have a bad type, we'll get a coercion error later. |
| | 32221 | ty_ptr.* = null; |
| | 32222 | val_ptr.* = null; |
| | 32223 | |
| 32047 | continue; | 32224 | continue; |
| 32048 | } | 32225 | } |
| 32049 | if ((try sema.coerceInMemoryAllowed(block, candidate_ty, opt_child_ty, false, target, src, src)) == .ok) { | 32226 | |
| 32050 | any_are_null = true; | 32227 | if (len) |expect_len| { |
| 32051 | chosen = candidate; | 32228 | if (ty.arrayLen(mod) != expect_len) return .{ .conflict = .{ |
| 32052 | chosen_i = candidate_i + 1; | 32229 | .peer_idx_a = first_idx, |
| | 32230 | .peer_idx_b = i, |
| | 32231 | } }; |
| | 32232 | } else { |
| | 32233 | len = ty.arrayLen(mod); |
| | 32234 | first_idx = i; |
| | 32235 | } |
| | 32236 | |
| | 32237 | ty_ptr.* = ty.childType(mod); |
| | 32238 | val_ptr.* = null; // multiple child vals, so we can't easily use them in PTR |
| | 32239 | } |
| | 32240 | |
| | 32241 | const child_ty = switch (try sema.resolvePeerTypesInner( |
| | 32242 | block, |
| | 32243 | src, |
| | 32244 | peer_tys, |
| | 32245 | peer_vals, |
| | 32246 | strat_reason, |
| | 32247 | )) { |
| | 32248 | .success => |ty| ty, |
| | 32249 | else => |result| return result, |
| | 32250 | }; |
| | 32251 | |
| | 32252 | return .{ .success = try mod.vectorType(.{ |
| | 32253 | .len = @intCast(u32, len.?), |
| | 32254 | .child = child_ty.toIntern(), |
| | 32255 | }) }; |
| | 32256 | }, |
| | 32257 | |
| | 32258 | .c_ptr => { |
| | 32259 | var opt_ptr_info: ?Type.Payload.Pointer.Data = null; |
| | 32260 | var first_idx: usize = undefined; |
| | 32261 | for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| { |
| | 32262 | const ty = opt_ty orelse continue; |
| | 32263 | switch (ty.zigTypeTag(mod)) { |
| | 32264 | .ComptimeInt => continue, // comptime-known integers can always coerce to C pointers |
| | 32265 | .Int => { |
| | 32266 | if (opt_val != null) { |
| | 32267 | // Always allow the coercion for comptime-known ints |
| | 32268 | continue; |
| | 32269 | } else { |
| | 32270 | // Runtime-known, so check if the type is no bigger than a usize |
| | 32271 | const ptr_bits = target.ptrBitWidth(); |
| | 32272 | const bits = ty.intInfo(mod).bits; |
| | 32273 | if (bits <= ptr_bits) continue; |
| | 32274 | } |
| | 32275 | }, |
| | 32276 | .Null => continue, |
| | 32277 | else => {}, |
| | 32278 | } |
| | 32279 | |
| | 32280 | if (!ty.isPtrAtRuntime(mod)) return .{ .bad_strat = .{ |
| | 32281 | .strat = s, |
| | 32282 | .peer_idx = i, |
| | 32283 | } }; |
| | 32284 | |
| | 32285 | // Goes through optionals |
| | 32286 | const peer_info = ty.ptrInfo(mod); |
| | 32287 | |
| | 32288 | var ptr_info = opt_ptr_info orelse { |
| | 32289 | opt_ptr_info = peer_info; |
| | 32290 | opt_ptr_info.?.size = .C; |
| | 32291 | first_idx = i; |
| 32053 | continue; | 32292 | continue; |
| | 32293 | }; |
| | 32294 | |
| | 32295 | // Try peer -> cur, then cur -> peer |
| | 32296 | const old_pointee_type = ptr_info.pointee_type; |
| | 32297 | ptr_info.pointee_type = (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.pointee_type, peer_info.pointee_type)) orelse { |
| | 32298 | return .{ .conflict = .{ |
| | 32299 | .peer_idx_a = first_idx, |
| | 32300 | .peer_idx_b = i, |
| | 32301 | } }; |
| | 32302 | }; |
| | 32303 | |
| | 32304 | if (ptr_info.sentinel != null and peer_info.sentinel != null) { |
| | 32305 | const peer_sent = try sema.coerceValueInMemory(block, ptr_info.sentinel.?, old_pointee_type, ptr_info.pointee_type, .unneeded); |
| | 32306 | const ptr_sent = try sema.coerceValueInMemory(block, peer_info.sentinel.?, peer_info.pointee_type, ptr_info.pointee_type, .unneeded); |
| | 32307 | if (ptr_sent.eql(peer_sent, ptr_info.pointee_type, mod)) { |
| | 32308 | ptr_info.sentinel = ptr_sent; |
| | 32309 | } else { |
| | 32310 | ptr_info.sentinel = null; |
| | 32311 | } |
| | 32312 | } else { |
| | 32313 | ptr_info.sentinel = null; |
| 32054 | } | 32314 | } |
| 32055 | }, | 32315 | |
| 32056 | .ErrorUnion => { | 32316 | // Note that the align can be always non-zero; Type.ptr will canonicalize it |
| 32057 | const payload_ty = chosen_ty.errorUnionPayload(mod); | 32317 | ptr_info.@"align" = @min(ptr_info.alignment(mod), peer_info.alignment(mod)); |
| 32058 | if ((try sema.coerceInMemoryAllowed(block, payload_ty, candidate_ty, false, target, src, src)) == .ok) { | 32318 | if (ptr_info.@"addrspace" != peer_info.@"addrspace") { |
| | 32319 | return .{ .conflict = .{ |
| | 32320 | .peer_idx_a = first_idx, |
| | 32321 | .peer_idx_b = i, |
| | 32322 | } }; |
| | 32323 | } |
| | 32324 | |
| | 32325 | if (ptr_info.bit_offset != peer_info.bit_offset or |
| | 32326 | ptr_info.host_size != peer_info.host_size) |
| | 32327 | { |
| | 32328 | return .{ .conflict = .{ |
| | 32329 | .peer_idx_a = first_idx, |
| | 32330 | .peer_idx_b = i, |
| | 32331 | } }; |
| | 32332 | } |
| | 32333 | |
| | 32334 | ptr_info.mutable = ptr_info.mutable and peer_info.mutable; |
| | 32335 | ptr_info.@"volatile" = ptr_info.@"volatile" or peer_info.@"volatile"; |
| | 32336 | |
| | 32337 | opt_ptr_info = ptr_info; |
| | 32338 | } |
| | 32339 | return .{ .success = try Type.ptr(sema.arena, mod, opt_ptr_info.?) }; |
| | 32340 | }, |
| | 32341 | |
| | 32342 | .ptr => { |
| | 32343 | // If we've resolved to a `[]T` but then see a `[*]T`, we can resolve to a `[*]T` only |
| | 32344 | // if there were no actual slices. Else, we want the slice index to report a conflict. |
| | 32345 | var opt_slice_idx: ?usize = null; |
| | 32346 | |
| | 32347 | var opt_ptr_info: ?Type.Payload.Pointer.Data = null; |
| | 32348 | var first_idx: usize = undefined; |
| | 32349 | var other_idx: usize = undefined; // We sometimes need a second peer index to report a generic error |
| | 32350 | |
| | 32351 | for (peer_tys, 0..) |opt_ty, i| { |
| | 32352 | const ty = opt_ty orelse continue; |
| | 32353 | const peer_info: Type.Payload.Pointer.Data = switch (ty.zigTypeTag(mod)) { |
| | 32354 | .Pointer => ty.ptrInfo(mod), |
| | 32355 | .Fn => .{ |
| | 32356 | .pointee_type = ty, |
| | 32357 | .@"addrspace" = target_util.defaultAddressSpace(target, .global_constant), |
| | 32358 | }, |
| | 32359 | else => return .{ .bad_strat = .{ |
| | 32360 | .strat = s, |
| | 32361 | .peer_idx = i, |
| | 32362 | } }, |
| | 32363 | }; |
| | 32364 | |
| | 32365 | switch (peer_info.size) { |
| | 32366 | .One, .Many => {}, |
| | 32367 | .Slice => opt_slice_idx = i, |
| | 32368 | .C => return .{ .bad_strat = .{ |
| | 32369 | .strat = s, |
| | 32370 | .peer_idx = i, |
| | 32371 | } }, |
| | 32372 | } |
| | 32373 | |
| | 32374 | var ptr_info = opt_ptr_info orelse { |
| | 32375 | opt_ptr_info = peer_info; |
| | 32376 | first_idx = i; |
| 32059 | continue; | 32377 | continue; |
| | 32378 | }; |
| | 32379 | |
| | 32380 | other_idx = i; |
| | 32381 | |
| | 32382 | // We want to return this in a lot of cases, so alias it here for convenience |
| | 32383 | const generic_err: PeerResolveResult = .{ .conflict = .{ |
| | 32384 | .peer_idx_a = first_idx, |
| | 32385 | .peer_idx_b = i, |
| | 32386 | } }; |
| | 32387 | |
| | 32388 | // Note that the align can be always non-zero; Type.ptr will canonicalize it |
| | 32389 | ptr_info.@"align" = @min(ptr_info.alignment(mod), peer_info.alignment(mod)); |
| | 32390 | |
| | 32391 | if (ptr_info.@"addrspace" != peer_info.@"addrspace") { |
| | 32392 | return generic_err; |
| 32060 | } | 32393 | } |
| 32061 | }, | 32394 | |
| 32062 | .ErrorSet => { | 32395 | if (ptr_info.bit_offset != peer_info.bit_offset or |
| 32063 | chosen = candidate; | 32396 | ptr_info.host_size != peer_info.host_size) |
| 32064 | chosen_i = candidate_i + 1; | 32397 | { |
| 32065 | if (err_set_ty) |chosen_set_ty| { | 32398 | return generic_err; |
| 32066 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, chosen_ty, src, src)) { | 32399 | } |
| 32067 | continue; | 32400 | |
| | 32401 | ptr_info.mutable = ptr_info.mutable and peer_info.mutable; |
| | 32402 | ptr_info.@"volatile" = ptr_info.@"volatile" or peer_info.@"volatile"; |
| | 32403 | |
| | 32404 | const peer_sentinel: ?Value = switch (peer_info.size) { |
| | 32405 | .One => switch (peer_info.pointee_type.zigTypeTag(mod)) { |
| | 32406 | .Array => peer_info.pointee_type.sentinel(mod), |
| | 32407 | else => null, |
| | 32408 | }, |
| | 32409 | .Many, .Slice => peer_info.sentinel, |
| | 32410 | .C => unreachable, |
| | 32411 | }; |
| | 32412 | |
| | 32413 | const cur_sentinel: ?Value = switch (ptr_info.size) { |
| | 32414 | .One => switch (ptr_info.pointee_type.zigTypeTag(mod)) { |
| | 32415 | .Array => ptr_info.pointee_type.sentinel(mod), |
| | 32416 | else => null, |
| | 32417 | }, |
| | 32418 | .Many, .Slice => ptr_info.sentinel, |
| | 32419 | .C => unreachable, |
| | 32420 | }; |
| | 32421 | |
| | 32422 | // We abstract array handling slightly so that tuple pointers can work like array pointers |
| | 32423 | const peer_pointee_array = sema.typeIsArrayLike(peer_info.pointee_type); |
| | 32424 | const cur_pointee_array = sema.typeIsArrayLike(ptr_info.pointee_type); |
| | 32425 | |
| | 32426 | // This switch is just responsible for deciding the size and pointee (not including |
| | 32427 | // single-pointer array sentinel). |
| | 32428 | good: { |
| | 32429 | switch (peer_info.size) { |
| | 32430 | .One => switch (ptr_info.size) { |
| | 32431 | .One => { |
| | 32432 | if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.pointee_type, peer_info.pointee_type)) |pointee| { |
| | 32433 | ptr_info.pointee_type = pointee; |
| | 32434 | break :good; |
| | 32435 | } |
| | 32436 | |
| | 32437 | const cur_arr = cur_pointee_array orelse return generic_err; |
| | 32438 | const peer_arr = peer_pointee_array orelse return generic_err; |
| | 32439 | |
| | 32440 | if (try sema.resolvePairInMemoryCoercible(block, src, cur_arr.elem_ty, peer_arr.elem_ty)) |elem_ty| { |
| | 32441 | // *[n:x]T + *[n:y]T = *[n]T |
| | 32442 | if (cur_arr.len == peer_arr.len) { |
| | 32443 | ptr_info.pointee_type = try mod.arrayType(.{ |
| | 32444 | .len = cur_arr.len, |
| | 32445 | .child = elem_ty.toIntern(), |
| | 32446 | }); |
| | 32447 | break :good; |
| | 32448 | } |
| | 32449 | // *[a]T + *[b]T = []T |
| | 32450 | ptr_info.size = .Slice; |
| | 32451 | ptr_info.pointee_type = elem_ty; |
| | 32452 | break :good; |
| | 32453 | } |
| | 32454 | |
| | 32455 | if (peer_arr.elem_ty.toIntern() == .noreturn_type) { |
| | 32456 | // *struct{} + *[a]T = []T |
| | 32457 | ptr_info.size = .Slice; |
| | 32458 | ptr_info.pointee_type = cur_arr.elem_ty; |
| | 32459 | break :good; |
| | 32460 | } |
| | 32461 | |
| | 32462 | if (cur_arr.elem_ty.toIntern() == .noreturn_type) { |
| | 32463 | // *[a]T + *struct{} = []T |
| | 32464 | ptr_info.size = .Slice; |
| | 32465 | ptr_info.pointee_type = peer_arr.elem_ty; |
| | 32466 | break :good; |
| | 32467 | } |
| | 32468 | |
| | 32469 | return generic_err; |
| | 32470 | }, |
| | 32471 | .Many => { |
| | 32472 | // Only works for *[n]T + [*]T -> [*]T |
| | 32473 | const arr = peer_pointee_array orelse return generic_err; |
| | 32474 | if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.pointee_type, arr.elem_ty)) |pointee| { |
| | 32475 | ptr_info.pointee_type = pointee; |
| | 32476 | break :good; |
| | 32477 | } |
| | 32478 | if (arr.elem_ty.toIntern() == .noreturn_type) { |
| | 32479 | // *struct{} + [*]T -> [*]T |
| | 32480 | break :good; |
| | 32481 | } |
| | 32482 | return generic_err; |
| | 32483 | }, |
| | 32484 | .Slice => { |
| | 32485 | // Only works for *[n]T + []T -> []T |
| | 32486 | const arr = peer_pointee_array orelse return generic_err; |
| | 32487 | if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.pointee_type, arr.elem_ty)) |pointee| { |
| | 32488 | ptr_info.pointee_type = pointee; |
| | 32489 | break :good; |
| | 32490 | } |
| | 32491 | if (arr.elem_ty.toIntern() == .noreturn_type) { |
| | 32492 | // *struct{} + []T -> []T |
| | 32493 | break :good; |
| | 32494 | } |
| | 32495 | return generic_err; |
| | 32496 | }, |
| | 32497 | .C => unreachable, |
| | 32498 | }, |
| | 32499 | .Many => switch (ptr_info.size) { |
| | 32500 | .One => { |
| | 32501 | // Only works for [*]T + *[n]T -> [*]T |
| | 32502 | const arr = cur_pointee_array orelse return generic_err; |
| | 32503 | if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, peer_info.pointee_type)) |pointee| { |
| | 32504 | ptr_info.size = .Many; |
| | 32505 | ptr_info.pointee_type = pointee; |
| | 32506 | break :good; |
| | 32507 | } |
| | 32508 | if (arr.elem_ty.toIntern() == .noreturn_type) { |
| | 32509 | // [*]T + *struct{} -> [*]T |
| | 32510 | ptr_info.size = .Many; |
| | 32511 | ptr_info.pointee_type = peer_info.pointee_type; |
| | 32512 | break :good; |
| | 32513 | } |
| | 32514 | return generic_err; |
| | 32515 | }, |
| | 32516 | .Many => { |
| | 32517 | if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.pointee_type, peer_info.pointee_type)) |pointee| { |
| | 32518 | ptr_info.pointee_type = pointee; |
| | 32519 | break :good; |
| | 32520 | } |
| | 32521 | return generic_err; |
| | 32522 | }, |
| | 32523 | .Slice => { |
| | 32524 | // Only works if no peers are actually slices |
| | 32525 | if (opt_slice_idx) |slice_idx| { |
| | 32526 | return .{ .conflict = .{ |
| | 32527 | .peer_idx_a = slice_idx, |
| | 32528 | .peer_idx_b = i, |
| | 32529 | } }; |
| | 32530 | } |
| | 32531 | // Okay, then works for [*]T + "[]T" -> [*]T |
| | 32532 | if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.pointee_type, peer_info.pointee_type)) |pointee| { |
| | 32533 | ptr_info.size = .Many; |
| | 32534 | ptr_info.pointee_type = pointee; |
| | 32535 | break :good; |
| | 32536 | } |
| | 32537 | return generic_err; |
| | 32538 | }, |
| | 32539 | .C => unreachable, |
| | 32540 | }, |
| | 32541 | .Slice => switch (ptr_info.size) { |
| | 32542 | .One => { |
| | 32543 | // Only works for []T + *[n]T -> []T |
| | 32544 | const arr = cur_pointee_array orelse return generic_err; |
| | 32545 | if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, peer_info.pointee_type)) |pointee| { |
| | 32546 | ptr_info.size = .Slice; |
| | 32547 | ptr_info.pointee_type = pointee; |
| | 32548 | break :good; |
| | 32549 | } |
| | 32550 | if (arr.elem_ty.toIntern() == .noreturn_type) { |
| | 32551 | // []T + *struct{} -> []T |
| | 32552 | ptr_info.size = .Slice; |
| | 32553 | ptr_info.pointee_type = peer_info.pointee_type; |
| | 32554 | break :good; |
| | 32555 | } |
| | 32556 | return generic_err; |
| | 32557 | }, |
| | 32558 | .Many => { |
| | 32559 | // Impossible! (current peer is an actual slice) |
| | 32560 | return generic_err; |
| | 32561 | }, |
| | 32562 | .Slice => { |
| | 32563 | if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.pointee_type, peer_info.pointee_type)) |pointee| { |
| | 32564 | ptr_info.pointee_type = pointee; |
| | 32565 | break :good; |
| | 32566 | } |
| | 32567 | return generic_err; |
| | 32568 | }, |
| | 32569 | .C => unreachable, |
| | 32570 | }, |
| | 32571 | .C => unreachable, |
| 32068 | } | 32572 | } |
| 32069 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_ty, chosen_set_ty, src, src)) { | 32573 | } |
| 32070 | err_set_ty = chosen_ty; | 32574 | |
| 32071 | continue; | 32575 | const sentinel_ty = if (ptr_info.size == .One and ptr_info.pointee_type.zigTypeTag(mod) == .Array) blk: { |
| | 32576 | break :blk ptr_info.pointee_type.childType(mod); |
| | 32577 | } else ptr_info.pointee_type; |
| | 32578 | |
| | 32579 | // TODO: once InternPool is in, we need to cast the sentinels to sentinel_ty |
| | 32580 | |
| | 32581 | sentinel: { |
| | 32582 | no_sentinel: { |
| | 32583 | if (peer_sentinel == null) break :no_sentinel; |
| | 32584 | if (cur_sentinel == null) break :no_sentinel; |
| | 32585 | const peer_sent_ty = mod.intern_pool.typeOf(peer_sentinel.?.toIntern()).toType(); |
| | 32586 | const cur_sent_ty = mod.intern_pool.typeOf(cur_sentinel.?.toIntern()).toType(); |
| | 32587 | const peer_sent_coerced = try sema.coerceValueInMemory(block, peer_sentinel.?, peer_sent_ty, sentinel_ty, .unneeded); |
| | 32588 | const cur_sent_coerced = try sema.coerceValueInMemory(block, cur_sentinel.?, cur_sent_ty, sentinel_ty, .unneeded); |
| | 32589 | if (!peer_sent_coerced.eql(cur_sent_coerced, sentinel_ty, mod)) break :no_sentinel; |
| | 32590 | // Sentinels match |
| | 32591 | if (ptr_info.size == .One) { |
| | 32592 | assert(ptr_info.pointee_type.zigTypeTag(mod) == .Array); |
| | 32593 | ptr_info.pointee_type = try mod.arrayType(.{ |
| | 32594 | .len = ptr_info.pointee_type.arrayLen(mod), |
| | 32595 | .child = ptr_info.pointee_type.childType(mod).toIntern(), |
| | 32596 | .sentinel = cur_sent_coerced.toIntern(), |
| | 32597 | }); |
| | 32598 | } else { |
| | 32599 | ptr_info.sentinel = cur_sent_coerced; |
| | 32600 | } |
| | 32601 | break :sentinel; |
| 32072 | } | 32602 | } |
| | 32603 | // Clear existing sentinel |
| | 32604 | ptr_info.sentinel = null; |
| | 32605 | if (ptr_info.pointee_type.zigTypeTag(mod) == .Array) { |
| | 32606 | ptr_info.pointee_type = try mod.arrayType(.{ |
| | 32607 | .len = ptr_info.pointee_type.arrayLen(mod), |
| | 32608 | .child = ptr_info.pointee_type.childType(mod).toIntern(), |
| | 32609 | .sentinel = .none, |
| | 32610 | }); |
| | 32611 | } |
| | 32612 | } |
| | 32613 | |
| | 32614 | opt_ptr_info = ptr_info; |
| | 32615 | } |
| 32073 | | 32616 | |
| 32074 | err_set_ty = try sema.errorSetMerge(chosen_set_ty, chosen_ty); | 32617 | // Before we succeed, check the pointee type. If we tried to apply PTR to (for instance) |
| | 32618 | // &.{} and &.{}, we'll currently have a pointer type of `*[0]noreturn` - we wanted to |
| | 32619 | // coerce the empty struct to a specific type, but no peer provided one. We need to |
| | 32620 | // detect this case and emit an error. |
| | 32621 | const pointee = opt_ptr_info.?.pointee_type; |
| | 32622 | if (pointee.toIntern() == .noreturn_type or |
| | 32623 | (pointee.zigTypeTag(mod) == .Array and pointee.childType(mod).toIntern() == .noreturn_type)) |
| | 32624 | { |
| | 32625 | return .{ .conflict = .{ |
| | 32626 | .peer_idx_a = first_idx, |
| | 32627 | .peer_idx_b = other_idx, |
| | 32628 | } }; |
| | 32629 | } |
| | 32630 | |
| | 32631 | return .{ .success = try Type.ptr(sema.arena, mod, opt_ptr_info.?) }; |
| | 32632 | }, |
| | 32633 | |
| | 32634 | .func => { |
| | 32635 | var opt_cur_ty: ?Type = null; |
| | 32636 | var first_idx: usize = undefined; |
| | 32637 | for (peer_tys, 0..) |opt_ty, i| { |
| | 32638 | const ty = opt_ty orelse continue; |
| | 32639 | const cur_ty = opt_cur_ty orelse { |
| | 32640 | opt_cur_ty = ty; |
| | 32641 | first_idx = i; |
| 32075 | continue; | 32642 | continue; |
| 32076 | } else { | 32643 | }; |
| 32077 | err_set_ty = chosen_ty; | 32644 | if (ty.zigTypeTag(mod) != .Fn) return .{ .bad_strat = .{ |
| | 32645 | .strat = s, |
| | 32646 | .peer_idx = i, |
| | 32647 | } }; |
| | 32648 | // ty -> cur_ty |
| | 32649 | if (.ok == try sema.coerceInMemoryAllowedFns(block, cur_ty, ty, target, src, src)) { |
| 32078 | continue; | 32650 | continue; |
| 32079 | } | 32651 | } |
| 32080 | }, | 32652 | // cur_ty -> ty |
| 32081 | else => {}, | 32653 | if (.ok == try sema.coerceInMemoryAllowedFns(block, ty, cur_ty, target, src, src)) { |
| 32082 | } | 32654 | opt_cur_ty = ty; |
| | 32655 | continue; |
| | 32656 | } |
| | 32657 | return .{ .conflict = .{ |
| | 32658 | .peer_idx_a = first_idx, |
| | 32659 | .peer_idx_b = i, |
| | 32660 | } }; |
| | 32661 | } |
| | 32662 | return .{ .success = opt_cur_ty.? }; |
| | 32663 | }, |
| 32083 | | 32664 | |
| 32084 | // At this point, we hit a compile error. We need to recover | 32665 | .enum_or_union => { |
| 32085 | // the source locations. | 32666 | var opt_cur_ty: ?Type = null; |
| 32086 | const chosen_src = candidate_srcs.resolve( | 32667 | // The peer index which gave the current type |
| 32087 | mod, | 32668 | var cur_ty_idx: usize = undefined; |
| 32088 | mod.declPtr(block.src_decl), | 32669 | |
| 32089 | chosen_i, | 32670 | for (peer_tys, 0..) |opt_ty, i| { |
| 32090 | ); | 32671 | const ty = opt_ty orelse continue; |
| 32091 | const candidate_src = candidate_srcs.resolve( | 32672 | switch (ty.zigTypeTag(mod)) { |
| 32092 | mod, | 32673 | .EnumLiteral, .Enum, .Union => {}, |
| 32093 | mod.declPtr(block.src_decl), | 32674 | else => return .{ .bad_strat = .{ |
| 32094 | candidate_i + 1, | 32675 | .strat = s, |
| 32095 | ); | 32676 | .peer_idx = i, |
| | 32677 | } }, |
| | 32678 | } |
| | 32679 | const cur_ty = opt_cur_ty orelse { |
| | 32680 | opt_cur_ty = ty; |
| | 32681 | cur_ty_idx = i; |
| | 32682 | continue; |
| | 32683 | }; |
| 32096 | | 32684 | |
| 32097 | const msg = msg: { | 32685 | // We want to return this in a lot of cases, so alias it here for convenience |
| 32098 | const msg = try sema.errMsg(block, src, "incompatible types: '{}' and '{}'", .{ | 32686 | const generic_err: PeerResolveResult = .{ .conflict = .{ |
| 32099 | chosen_ty.fmt(mod), | 32687 | .peer_idx_a = cur_ty_idx, |
| 32100 | candidate_ty.fmt(mod), | 32688 | .peer_idx_b = i, |
| 32101 | }); | 32689 | } }; |
| 32102 | errdefer msg.destroy(sema.gpa); | 32690 | |
| | 32691 | switch (cur_ty.zigTypeTag(mod)) { |
| | 32692 | .EnumLiteral => { |
| | 32693 | opt_cur_ty = ty; |
| | 32694 | cur_ty_idx = i; |
| | 32695 | }, |
| | 32696 | .Enum => switch (ty.zigTypeTag(mod)) { |
| | 32697 | .EnumLiteral => {}, |
| | 32698 | .Enum => { |
| | 32699 | if (!ty.eql(cur_ty, mod)) return generic_err; |
| | 32700 | }, |
| | 32701 | .Union => { |
| | 32702 | const tag_ty = ty.unionTagTypeHypothetical(mod); |
| | 32703 | if (!tag_ty.eql(cur_ty, mod)) return generic_err; |
| | 32704 | opt_cur_ty = ty; |
| | 32705 | cur_ty_idx = i; |
| | 32706 | }, |
| | 32707 | else => unreachable, |
| | 32708 | }, |
| | 32709 | .Union => switch (ty.zigTypeTag(mod)) { |
| | 32710 | .EnumLiteral => {}, |
| | 32711 | .Enum => { |
| | 32712 | const cur_tag_ty = cur_ty.unionTagTypeHypothetical(mod); |
| | 32713 | if (!ty.eql(cur_tag_ty, mod)) return generic_err; |
| | 32714 | }, |
| | 32715 | .Union => { |
| | 32716 | if (!ty.eql(cur_ty, mod)) return generic_err; |
| | 32717 | }, |
| | 32718 | else => unreachable, |
| | 32719 | }, |
| | 32720 | else => unreachable, |
| | 32721 | } |
| | 32722 | } |
| | 32723 | return .{ .success = opt_cur_ty.? }; |
| | 32724 | }, |
| 32103 | | 32725 | |
| 32104 | if (chosen_src) |src_loc| | 32726 | .comptime_int => { |
| 32105 | try sema.errNote(block, src_loc, msg, "type '{}' here", .{chosen_ty.fmt(mod)}); | 32727 | for (peer_tys, 0..) |opt_ty, i| { |
| | 32728 | const ty = opt_ty orelse continue; |
| | 32729 | switch (ty.zigTypeTag(mod)) { |
| | 32730 | .ComptimeInt => {}, |
| | 32731 | else => return .{ .bad_strat = .{ |
| | 32732 | .strat = s, |
| | 32733 | .peer_idx = i, |
| | 32734 | } }, |
| | 32735 | } |
| | 32736 | } |
| | 32737 | return .{ .success = Type.comptime_int }; |
| | 32738 | }, |
| 32106 | | 32739 | |
| 32107 | if (candidate_src) |src_loc| | 32740 | .comptime_float => { |
| 32108 | try sema.errNote(block, src_loc, msg, "type '{}' here", .{candidate_ty.fmt(mod)}); | 32741 | for (peer_tys, 0..) |opt_ty, i| { |
| | 32742 | const ty = opt_ty orelse continue; |
| | 32743 | switch (ty.zigTypeTag(mod)) { |
| | 32744 | .ComptimeInt, .ComptimeFloat => {}, |
| | 32745 | else => return .{ .bad_strat = .{ |
| | 32746 | .strat = s, |
| | 32747 | .peer_idx = i, |
| | 32748 | } }, |
| | 32749 | } |
| | 32750 | } |
| | 32751 | return .{ .success = Type.comptime_float }; |
| | 32752 | }, |
| 32109 | | 32753 | |
| 32110 | break :msg msg; | 32754 | .fixed_int => { |
| 32111 | }; | 32755 | var idx_unsigned: ?usize = null; |
| 32112 | return sema.failWithOwnedErrorMsg(msg); | 32756 | var idx_signed: ?usize = null; |
| 32113 | } | | |
| 32114 | | 32757 | |
| 32115 | const chosen_ty = sema.typeOf(chosen); | 32758 | // TODO: this is for compatibility with legacy behavior. See beneath the loop. |
| | 32759 | var any_comptime_known = false; |
| 32116 | | 32760 | |
| 32117 | if (convert_to_slice) { | 32761 | for (peer_tys, peer_vals, 0..) |opt_ty, *ptr_opt_val, i| { |
| 32118 | // turn *[N]T => []T | 32762 | const ty = opt_ty orelse continue; |
| 32119 | const chosen_child_ty = chosen_ty.childType(mod); | 32763 | const opt_val = ptr_opt_val.*; |
| 32120 | var info = chosen_ty.ptrInfo(mod); | | |
| 32121 | info.sentinel = chosen_child_ty.sentinel(mod); | | |
| 32122 | info.size = .Slice; | | |
| 32123 | info.mutable = !(seen_const or chosen_child_ty.isConstPtr(mod)); | | |
| 32124 | info.pointee_type = chosen_child_ty.elemType2(mod); | | |
| 32125 | | 32764 | |
| 32126 | const new_ptr_ty = try Type.ptr(sema.arena, mod, info); | 32765 | const peer_tag = ty.zigTypeTag(mod); |
| 32127 | const opt_ptr_ty = if (any_are_null) | 32766 | switch (peer_tag) { |
| 32128 | try Type.optional(sema.arena, new_ptr_ty, mod) | 32767 | .ComptimeInt => { |
| 32129 | else | 32768 | // If the value is undefined, we can't refine to a fixed-width int |
| 32130 | new_ptr_ty; | 32769 | if (opt_val == null or opt_val.?.isUndef(mod)) return .{ .bad_strat = .{ |
| 32131 | const set_ty = err_set_ty orelse return opt_ptr_ty; | 32770 | .strat = s, |
| 32132 | return try mod.errorUnionType(set_ty, opt_ptr_ty); | 32771 | .peer_idx = i, |
| | 32772 | } }; |
| | 32773 | any_comptime_known = true; |
| | 32774 | ptr_opt_val.* = try sema.resolveLazyValue(opt_val.?); |
| | 32775 | continue; |
| | 32776 | }, |
| | 32777 | .Int => {}, |
| | 32778 | else => return .{ .bad_strat = .{ |
| | 32779 | .strat = s, |
| | 32780 | .peer_idx = i, |
| | 32781 | } }, |
| | 32782 | } |
| | 32783 | |
| | 32784 | if (opt_val != null) any_comptime_known = true; |
| | 32785 | |
| | 32786 | const info = ty.intInfo(mod); |
| | 32787 | |
| | 32788 | const idx_ptr = switch (info.signedness) { |
| | 32789 | .unsigned => &idx_unsigned, |
| | 32790 | .signed => &idx_signed, |
| | 32791 | }; |
| | 32792 | |
| | 32793 | const largest_idx = idx_ptr.* orelse { |
| | 32794 | idx_ptr.* = i; |
| | 32795 | continue; |
| | 32796 | }; |
| | 32797 | |
| | 32798 | const cur_info = peer_tys[largest_idx].?.intInfo(mod); |
| | 32799 | if (info.bits > cur_info.bits) { |
| | 32800 | idx_ptr.* = i; |
| | 32801 | } |
| | 32802 | } |
| | 32803 | |
| | 32804 | if (idx_signed == null) { |
| | 32805 | return .{ .success = peer_tys[idx_unsigned.?].? }; |
| | 32806 | } |
| | 32807 | |
| | 32808 | if (idx_unsigned == null) { |
| | 32809 | return .{ .success = peer_tys[idx_signed.?].? }; |
| | 32810 | } |
| | 32811 | |
| | 32812 | const unsigned_info = peer_tys[idx_unsigned.?].?.intInfo(mod); |
| | 32813 | const signed_info = peer_tys[idx_signed.?].?.intInfo(mod); |
| | 32814 | if (signed_info.bits > unsigned_info.bits) { |
| | 32815 | return .{ .success = peer_tys[idx_signed.?].? }; |
| | 32816 | } |
| | 32817 | |
| | 32818 | // TODO: this is for compatibility with legacy behavior. Before this version of PTR was |
| | 32819 | // implemented, the algorithm very often returned false positives, with the expectation |
| | 32820 | // that you'd just hit a coercion error later. One of these was that for integers, the |
| | 32821 | // largest type would always be returned, even if it couldn't fit everything. This had |
| | 32822 | // an unintentional consequence to semantics, which is that if values were known at |
| | 32823 | // comptime, they would be coerced down to the smallest type where possible. This |
| | 32824 | // behavior is unintuitive and order-dependent, so in my opinion should be eliminated, |
| | 32825 | // but for now we'll retain compatibility. |
| | 32826 | if (any_comptime_known) { |
| | 32827 | if (unsigned_info.bits > signed_info.bits) { |
| | 32828 | return .{ .success = peer_tys[idx_unsigned.?].? }; |
| | 32829 | } |
| | 32830 | const idx = @min(idx_unsigned.?, idx_signed.?); |
| | 32831 | return .{ .success = peer_tys[idx].? }; |
| | 32832 | } |
| | 32833 | |
| | 32834 | return .{ .conflict = .{ |
| | 32835 | .peer_idx_a = idx_unsigned.?, |
| | 32836 | .peer_idx_b = idx_signed.?, |
| | 32837 | } }; |
| | 32838 | }, |
| | 32839 | |
| | 32840 | .fixed_float => { |
| | 32841 | var opt_cur_ty: ?Type = null; |
| | 32842 | |
| | 32843 | for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| { |
| | 32844 | const ty = opt_ty orelse continue; |
| | 32845 | switch (ty.zigTypeTag(mod)) { |
| | 32846 | .ComptimeFloat, .ComptimeInt => {}, |
| | 32847 | .Int => { |
| | 32848 | if (opt_val == null) return .{ .bad_strat = .{ |
| | 32849 | .strat = s, |
| | 32850 | .peer_idx = i, |
| | 32851 | } }; |
| | 32852 | }, |
| | 32853 | .Float => { |
| | 32854 | if (opt_cur_ty) |cur_ty| { |
| | 32855 | if (cur_ty.eql(ty, mod)) continue; |
| | 32856 | // Recreate the type so we eliminate any c_longdouble |
| | 32857 | const bits = @max(cur_ty.floatBits(target), ty.floatBits(target)); |
| | 32858 | opt_cur_ty = switch (bits) { |
| | 32859 | 16 => Type.f16, |
| | 32860 | 32 => Type.f32, |
| | 32861 | 64 => Type.f64, |
| | 32862 | 80 => Type.f80, |
| | 32863 | 128 => Type.f128, |
| | 32864 | else => unreachable, |
| | 32865 | }; |
| | 32866 | } else { |
| | 32867 | opt_cur_ty = ty; |
| | 32868 | } |
| | 32869 | }, |
| | 32870 | else => return .{ .bad_strat = .{ |
| | 32871 | .strat = s, |
| | 32872 | .peer_idx = i, |
| | 32873 | } }, |
| | 32874 | } |
| | 32875 | } |
| | 32876 | |
| | 32877 | // Note that fixed_float is only chosen if there is at least one fixed-width float peer, |
| | 32878 | // so opt_cur_ty must be non-null. |
| | 32879 | return .{ .success = opt_cur_ty.? }; |
| | 32880 | }, |
| | 32881 | |
| | 32882 | .coercible_struct => { |
| | 32883 | // First, check that every peer has the same approximate structure (field count and names) |
| | 32884 | |
| | 32885 | var opt_first_idx: ?usize = null; |
| | 32886 | var is_tuple: bool = undefined; |
| | 32887 | var field_count: usize = undefined; |
| | 32888 | // Only defined for non-tuples. |
| | 32889 | var field_names: []InternPool.NullTerminatedString = undefined; |
| | 32890 | |
| | 32891 | for (peer_tys, 0..) |opt_ty, i| { |
| | 32892 | const ty = opt_ty orelse continue; |
| | 32893 | |
| | 32894 | if (!ty.isTupleOrAnonStruct(mod)) { |
| | 32895 | return .{ .bad_strat = .{ |
| | 32896 | .strat = s, |
| | 32897 | .peer_idx = i, |
| | 32898 | } }; |
| | 32899 | } |
| | 32900 | |
| | 32901 | const first_idx = opt_first_idx orelse { |
| | 32902 | opt_first_idx = i; |
| | 32903 | is_tuple = ty.isTuple(mod); |
| | 32904 | field_count = ty.structFieldCount(mod); |
| | 32905 | if (!is_tuple) { |
| | 32906 | const names = mod.intern_pool.indexToKey(ty.toIntern()).anon_struct_type.names; |
| | 32907 | field_names = try sema.arena.dupe(InternPool.NullTerminatedString, names); |
| | 32908 | } |
| | 32909 | continue; |
| | 32910 | }; |
| | 32911 | |
| | 32912 | if (ty.isTuple(mod) != is_tuple or ty.structFieldCount(mod) != field_count) { |
| | 32913 | return .{ .conflict = .{ |
| | 32914 | .peer_idx_a = first_idx, |
| | 32915 | .peer_idx_b = i, |
| | 32916 | } }; |
| | 32917 | } |
| | 32918 | |
| | 32919 | if (!is_tuple) { |
| | 32920 | for (field_names, 0..) |expected, field_idx| { |
| | 32921 | const actual = ty.structFieldName(field_idx, mod); |
| | 32922 | if (actual == expected) continue; |
| | 32923 | return .{ .conflict = .{ |
| | 32924 | .peer_idx_a = first_idx, |
| | 32925 | .peer_idx_b = i, |
| | 32926 | } }; |
| | 32927 | } |
| | 32928 | } |
| | 32929 | } |
| | 32930 | |
| | 32931 | assert(opt_first_idx != null); |
| | 32932 | |
| | 32933 | // Now, we'll recursively resolve the field types |
| | 32934 | const field_types = try sema.arena.alloc(InternPool.Index, field_count); |
| | 32935 | // Values for `comptime` fields - `.none` used for non-comptime fields |
| | 32936 | const field_vals = try sema.arena.alloc(InternPool.Index, field_count); |
| | 32937 | const sub_peer_tys = try sema.arena.alloc(?Type, peer_tys.len); |
| | 32938 | const sub_peer_vals = try sema.arena.alloc(?Value, peer_vals.len); |
| | 32939 | |
| | 32940 | for (field_types, field_vals, 0..) |*field_ty, *field_val, field_idx| { |
| | 32941 | // Fill buffers with types and values of the field |
| | 32942 | for (peer_tys, peer_vals, sub_peer_tys, sub_peer_vals) |opt_ty, opt_val, *peer_field_ty, *peer_field_val| { |
| | 32943 | const ty = opt_ty orelse { |
| | 32944 | peer_field_ty.* = null; |
| | 32945 | peer_field_val.* = null; |
| | 32946 | continue; |
| | 32947 | }; |
| | 32948 | peer_field_ty.* = ty.structFieldType(field_idx, mod); |
| | 32949 | peer_field_val.* = if (opt_val) |val| try val.fieldValue(mod, field_idx) else null; |
| | 32950 | } |
| | 32951 | |
| | 32952 | // Resolve field type recursively |
| | 32953 | field_ty.* = switch (try sema.resolvePeerTypesInner(block, src, sub_peer_tys, sub_peer_vals, strat_reason)) { |
| | 32954 | .success => |ty| ty.toIntern(), |
| | 32955 | else => |result| { |
| | 32956 | const result_buf = try sema.arena.create(PeerResolveResult); |
| | 32957 | result_buf.* = result; |
| | 32958 | const field_name = if (is_tuple) name: { |
| | 32959 | break :name try std.fmt.allocPrint(sema.arena, "{d}", .{field_idx}); |
| | 32960 | } else try sema.arena.dupe(u8, mod.intern_pool.stringToSlice(field_names[field_idx])); |
| | 32961 | |
| | 32962 | // The error info needs the field types, but we can't reuse sub_peer_tys |
| | 32963 | // since the recursive call may have clobbered it. |
| | 32964 | const peer_field_tys = try sema.arena.alloc(Type, peer_tys.len); |
| | 32965 | for (peer_tys, peer_field_tys) |opt_ty, *peer_field_ty| { |
| | 32966 | // Already-resolved types won't be referenced by the error so it's fine |
| | 32967 | // to leave them undefined. |
| | 32968 | const ty = opt_ty orelse continue; |
| | 32969 | peer_field_ty.* = ty.structFieldType(field_idx, mod); |
| | 32970 | } |
| | 32971 | |
| | 32972 | return .{ .field_error = .{ |
| | 32973 | .field_name = field_name, |
| | 32974 | .field_types = peer_field_tys, |
| | 32975 | .sub_result = result_buf, |
| | 32976 | } }; |
| | 32977 | }, |
| | 32978 | }; |
| | 32979 | |
| | 32980 | // Decide if this is a comptime field. If it is comptime in all peers, and the |
| | 32981 | // coerced comptime values are all the same, we say it is comptime, else not. |
| | 32982 | |
| | 32983 | var comptime_val: ?Value = null; |
| | 32984 | for (peer_tys) |opt_ty| { |
| | 32985 | const struct_ty = opt_ty orelse continue; |
| | 32986 | const uncoerced_field_val = try struct_ty.structFieldValueComptime(mod, field_idx) orelse { |
| | 32987 | comptime_val = null; |
| | 32988 | break; |
| | 32989 | }; |
| | 32990 | const uncoerced_field_ty = struct_ty.structFieldType(field_idx, mod); |
| | 32991 | const uncoerced_field = try sema.addConstant(uncoerced_field_ty, uncoerced_field_val); |
| | 32992 | const coerced_inst = sema.coerceExtra(block, field_ty.toType(), uncoerced_field, src, .{ .report_err = false }) catch |err| switch (err) { |
| | 32993 | // It's possible for PTR to give false positives. Just give up on making this a comptime field, we'll get an error later anyway |
| | 32994 | error.NotCoercible => { |
| | 32995 | comptime_val = null; |
| | 32996 | break; |
| | 32997 | }, |
| | 32998 | else => |e| return e, |
| | 32999 | }; |
| | 33000 | const coerced_val = (try sema.resolveMaybeUndefVal(coerced_inst)) orelse continue; |
| | 33001 | const existing = comptime_val orelse { |
| | 33002 | comptime_val = coerced_val; |
| | 33003 | continue; |
| | 33004 | }; |
| | 33005 | if (!coerced_val.eql(existing, field_ty.toType(), mod)) { |
| | 33006 | comptime_val = null; |
| | 33007 | break; |
| | 33008 | } |
| | 33009 | } |
| | 33010 | |
| | 33011 | field_val.* = if (comptime_val) |v| v.toIntern() else .none; |
| | 33012 | } |
| | 33013 | |
| | 33014 | const final_ty = try mod.intern(.{ .anon_struct_type = .{ |
| | 33015 | .types = field_types, |
| | 33016 | .names = if (is_tuple) &.{} else field_names, |
| | 33017 | .values = field_vals, |
| | 33018 | } }); |
| | 33019 | |
| | 33020 | return .{ .success = final_ty.toType() }; |
| | 33021 | }, |
| | 33022 | |
| | 33023 | .exact => { |
| | 33024 | var expect_ty: ?Type = null; |
| | 33025 | var first_idx: usize = undefined; |
| | 33026 | for (peer_tys, 0..) |opt_ty, i| { |
| | 33027 | const ty = opt_ty orelse continue; |
| | 33028 | if (expect_ty) |expect| { |
| | 33029 | if (!ty.eql(expect, mod)) return .{ .conflict = .{ |
| | 33030 | .peer_idx_a = first_idx, |
| | 33031 | .peer_idx_b = i, |
| | 33032 | } }; |
| | 33033 | } else { |
| | 33034 | expect_ty = ty; |
| | 33035 | first_idx = i; |
| | 33036 | } |
| | 33037 | } |
| | 33038 | return .{ .success = expect_ty.? }; |
| | 33039 | }, |
| 32133 | } | 33040 | } |
| | 33041 | } |
| 32134 | | 33042 | |
| 32135 | if (seen_const) { | 33043 | fn maybeMergeErrorSets(sema: *Sema, block: *Block, src: LazySrcLoc, e0: Type, e1: Type) !Type { |
| 32136 | // turn []T => []const T | 33044 | // e0 -> e1 |
| 32137 | switch (chosen_ty.zigTypeTag(mod)) { | 33045 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e1, e0, src, src)) { |
| 32138 | .ErrorUnion => { | 33046 | return e1; |
| 32139 | const ptr_ty = chosen_ty.errorUnionPayload(mod); | | |
| 32140 | var info = ptr_ty.ptrInfo(mod); | | |
| 32141 | info.mutable = false; | | |
| 32142 | const new_ptr_ty = try Type.ptr(sema.arena, mod, info); | | |
| 32143 | const opt_ptr_ty = if (any_are_null) | | |
| 32144 | try Type.optional(sema.arena, new_ptr_ty, mod) | | |
| 32145 | else | | |
| 32146 | new_ptr_ty; | | |
| 32147 | const set_ty = err_set_ty orelse chosen_ty.errorUnionSet(mod); | | |
| 32148 | return try mod.errorUnionType(set_ty, opt_ptr_ty); | | |
| 32149 | }, | | |
| 32150 | .Pointer => { | | |
| 32151 | var info = chosen_ty.ptrInfo(mod); | | |
| 32152 | info.mutable = false; | | |
| 32153 | const new_ptr_ty = try Type.ptr(sema.arena, mod, info); | | |
| 32154 | const opt_ptr_ty = if (any_are_null) | | |
| 32155 | try Type.optional(sema.arena, new_ptr_ty, mod) | | |
| 32156 | else | | |
| 32157 | new_ptr_ty; | | |
| 32158 | const set_ty = err_set_ty orelse return opt_ptr_ty; | | |
| 32159 | return try mod.errorUnionType(set_ty, opt_ptr_ty); | | |
| 32160 | }, | | |
| 32161 | else => return chosen_ty, | | |
| 32162 | } | | |
| 32163 | } | 33047 | } |
| 32164 | | 33048 | |
| 32165 | if (any_are_null) { | 33049 | // e1 -> e0 |
| 32166 | const opt_ty = switch (chosen_ty.zigTypeTag(mod)) { | 33050 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e0, e1, src, src)) { |
| 32167 | .Null, .Optional => chosen_ty, | 33051 | return e0; |
| 32168 | else => try Type.optional(sema.arena, chosen_ty, mod), | | |
| 32169 | }; | | |
| 32170 | const set_ty = err_set_ty orelse return opt_ty; | | |
| 32171 | return try mod.errorUnionType(set_ty, opt_ty); | | |
| 32172 | } | 33052 | } |
| 32173 | | 33053 | |
| 32174 | if (err_set_ty) |ty| switch (chosen_ty.zigTypeTag(mod)) { | 33054 | return sema.errorSetMerge(e0, e1); |
| 32175 | .ErrorSet => return ty, | 33055 | } |
| 32176 | .ErrorUnion => { | 33056 | |
| 32177 | const payload_ty = chosen_ty.errorUnionPayload(mod); | 33057 | fn resolvePairInMemoryCoercible(sema: *Sema, block: *Block, src: LazySrcLoc, ty_a: Type, ty_b: Type) !?Type { |
| 32178 | return try mod.errorUnionType(ty, payload_ty); | 33058 | // ty_b -> ty_a |
| | 33059 | if (.ok == try sema.coerceInMemoryAllowed(block, ty_a, ty_b, true, sema.mod.getTarget(), src, src)) { |
| | 33060 | return ty_a; |
| | 33061 | } |
| | 33062 | |
| | 33063 | // ty_a -> ty_b |
| | 33064 | if (.ok == try sema.coerceInMemoryAllowed(block, ty_b, ty_a, true, sema.mod.getTarget(), src, src)) { |
| | 33065 | return ty_b; |
| | 33066 | } |
| | 33067 | |
| | 33068 | return null; |
| | 33069 | } |
| | 33070 | |
| | 33071 | const ArrayLike = struct { |
| | 33072 | len: u64, |
| | 33073 | /// `noreturn` indicates that this type is `struct{}` so can coerce to anything |
| | 33074 | elem_ty: Type, |
| | 33075 | }; |
| | 33076 | fn typeIsArrayLike(sema: *Sema, ty: Type) ?ArrayLike { |
| | 33077 | const mod = sema.mod; |
| | 33078 | return switch (ty.zigTypeTag(mod)) { |
| | 33079 | .Array => .{ |
| | 33080 | .len = ty.arrayLen(mod), |
| | 33081 | .elem_ty = ty.childType(mod), |
| | 33082 | }, |
| | 33083 | .Struct => { |
| | 33084 | const field_count = ty.structFieldCount(mod); |
| | 33085 | if (field_count == 0) return .{ |
| | 33086 | .len = 0, |
| | 33087 | .elem_ty = Type.noreturn, |
| | 33088 | }; |
| | 33089 | if (!ty.isTuple(mod)) return null; |
| | 33090 | const elem_ty = ty.structFieldType(0, mod); |
| | 33091 | for (1..field_count) |i| { |
| | 33092 | if (!ty.structFieldType(i, mod).eql(elem_ty, mod)) { |
| | 33093 | return null; |
| | 33094 | } |
| | 33095 | } |
| | 33096 | return .{ |
| | 33097 | .len = field_count, |
| | 33098 | .elem_ty = elem_ty, |
| | 33099 | }; |
| 32179 | }, | 33100 | }, |
| 32180 | else => return try mod.errorUnionType(ty, chosen_ty), | 33101 | else => null, |
| 32181 | }; | 33102 | }; |
| 32182 | | | |
| 32183 | return chosen_ty; | | |
| 32184 | } | 33103 | } |
| 32185 | | 33104 | |
| 32186 | pub fn resolveFnTypes(sema: *Sema, fn_ty: Type) CompileError!void { | 33105 | pub fn resolveFnTypes(sema: *Sema, fn_ty: Type) CompileError!void { |