authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-06-13 15:17:31+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-06-13 21:48:18+01:00
logc9531eb833e1e2c432dc2bfb0ca3b25622b7001e
tree7cdae51f8762b670aed003b3654ff429a731146d
parent854f26ad8ac8dc39ef7ad60f86588c0c5dba131a
signaturelock-open Commit is signed but in an unrecognized format.

Sema: rewrite peer type resolution

The existing logic for peer type resolution was quite convoluted and buggy. This rewrite makes it much more resilient, readable, and extensible. The algorithm works by first iterating over the types to select a "strategy", then applying that strategy, possibly applying peer resolution recursively. Several new tests have been added to cover cases which the old logic did not correctly handle. Resolves: #15138 Resolves: #15644 Resolves: #15693 Resolves: #15709 Resolves: #15752

6 files changed, 2111 insertions(+), 558 deletions(-)

src/Module.zig+1-1
...@@ -6978,7 +6978,7 @@ pub fn intBitsForValue(mod: *Module, val: Value, sign: bool) u16 {...@@ -6978,7 +6978,7 @@ pub fn intBitsForValue(mod: *Module, val: Value, sign: bool) u16 {
6978 assert(sign);6978 assert(sign);
6979 // Protect against overflow in the following negation.6979 // Protect against overflow in the following negation.
6980 if (x == std.math.minInt(i64)) return 64;6980 if (x == std.math.minInt(i64)) return 64;
6981 return Type.smallestUnsignedBits(@intCast(u64, -x - 1)) + 1;6981 return Type.smallestUnsignedBits(@intCast(u64, -(x + 1))) + 1;
6982 },6982 },
6983 .u64 => |x| {6983 .u64 => |x| {
6984 return Type.smallestUnsignedBits(x) + @boolToInt(sign);6984 return Type.smallestUnsignedBits(x) + @boolToInt(sign);
src/Sema.zig+1439-520
...@@ -27959,9 +27959,8 @@ fn coerceInMemoryAllowedErrorSets(...@@ -27959,9 +27959,8 @@ fn coerceInMemoryAllowedErrorSets(
2795927959
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 above27962 .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 },
2796727966
...@@ -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}
2801428011
28015fn coerceInMemoryAllowedFns(28012fn 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}
3158831585
31589fn resolvePeerTypes(31586const 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.
3160231599 /// If refined no further, it is an optional.
31603 const target = mod.getTarget();31600 optional,
3160431601 /// 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 override31604 /// The type must be a vector.
31608 // * ErrorUnion: this is an override of the error set only31605 vector,
31609 // * other: at the end we make an ErrorUnion with the other thing and this31606 /// 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
3161831615 /// 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.
3162131618 /// 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 };
3163231639
31633 switch (candidate_ty_tag) {31640 fn name(s: PeerResolveStrategy) []const u8 {
31634 .NoReturn, .Undefined => continue,31641 return switch (s) {
3163531642 .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 }
3164031660
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 };
3165031680
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 };
3171631748
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 }
3172631771
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);
3173231774
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
31797const 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 }
3174031856
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 }
3175331866
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);
3176631878
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 }
3177831904
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);
3178231906
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 }
3178531911
31786 if (coerce_chosen or coerce_candidate) {31912 const conflict_tys: [2]Type = .{
31787 // If we can coerce to the candidate, we switch to that31913 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 };
3179431920
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 };
3179731934
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 },
3180831937
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);
3183131941
31832 seen_const = seen_const or !chosen_info.mutable or !cand_info.mutable;31942 return opt_msg.?;
31943 }
31944};
3183331945
31834 // *[N]T to [*]T31946fn resolvePeerTypes(
31835 // *[N]T to []T31947 sema: *Sema,
31836 if ((cand_info.size == .Many or cand_info.size == .Slice) and31948 block: *Block,
31837 chosen_info.size == .One and31949 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 }
3185431958
31855 // *[N]T and *[M]T31959 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);
3186531961
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 }
3187131966
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 }
3187931970
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 the31972 .success => |ty| return ty,
31882 // coerceInMemoryAllowed error reporting mechanism,31973 else => |result| {
31883 // however, for now we just fall through for the31974 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}
3188631979
31887 // [*c]T and any other pointer size31980fn resolvePeerTypesInner(
31888 // Whichever element type can coerce to the other one, is31981 sema: *Sema,
31889 // the one we will keep. If they're both OK then we keep the31982 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,
3189431987) !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 the32007 else => {},
31915 // coerceInMemoryAllowed error reporting mechanism,32008 }
31916 // however, for now we just fall through for the32009 }
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 },
3193332078
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 },
3193532089
31936 // *[N]T to ?![*]T32090 .optional => {
31937 // *[N]T to ?![]T32091 for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| {
31938 if (cand_info.size == .One and32092 const ty = ty_ptr.* orelse continue;
31939 cand_info.pointee_type.zigTypeTag(mod) == .Array and32093 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 },
3198032117
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 } };
3199332138
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 }
3203132176
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 }
3207332616
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 },
3208332664
32084 // At this point, we hit a compile error. We need to recover32665 .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 };
3209632684
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 },
3210332725
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 },
3210632739
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 },
3210932753
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 }
3211432757
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;
3211632760
32117 if (convert_to_slice) {32761 for (peer_tys, peer_vals, 0..) |opt_ty, *ptr_opt_val, i| {
32118 // turn *[N]T => []T32762 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);
3212532764
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 else32768 // 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}
3213433042
32135 if (seen_const) {33043fn maybeMergeErrorSets(sema: *Sema, block: *Block, src: LazySrcLoc, e0: Type, e1: Type) !Type {
32136 // turn []T => []const T33044 // 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 }
3216433048
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 }
3217333053
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);33057fn 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
33071const ArrayLike = struct {
33072 len: u64,
33073 /// `noreturn` indicates that this type is `struct{}` so can coerce to anything
33074 elem_ty: Type,
33075};
33076fn 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}
3218533104
32186pub fn resolveFnTypes(sema: *Sema, fn_ty: Type) CompileError!void {33105pub fn resolveFnTypes(sema: *Sema, fn_ty: Type) CompileError!void {
test/behavior/cast.zig+610
...@@ -1,6 +1,9 @@...@@ -1,6 +1,9 @@
1const builtin = @import("builtin");1const builtin = @import("builtin");
2const std = @import("std");2const std = @import("std");
3const assert = std.debug.assert;
3const expect = std.testing.expect;4const expect = std.testing.expect;
5const expectEqual = std.testing.expectEqual;
6const expectEqualSlices = std.testing.expectEqualSlices;
4const mem = std.mem;7const mem = std.mem;
5const maxInt = std.math.maxInt;8const maxInt = std.math.maxInt;
6const native_endian = builtin.target.cpu.arch.endian();9const native_endian = builtin.target.cpu.arch.endian();
...@@ -1609,3 +1612,610 @@ test "coercion from single-item pointer to @as to slice" {...@@ -1609,3 +1612,610 @@ test "coercion from single-item pointer to @as to slice" {
16091612
1610 try expect(t[0] == 1);1613 try expect(t[0] == 1);
1611}1614}
1615
1616test "peer type resolution: const sentinel slice and mutable non-sentinel slice" {
1617 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1618 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1619 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1620 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1621 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1622 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
1623
1624 const S = struct {
1625 fn doTheTest(comptime T: type, comptime s: T) !void {
1626 var a: [:s]const T = @intToPtr(*const [2:s]T, 0x1000);
1627 var b: []T = @intToPtr(*[3]T, 0x2000);
1628 comptime assert(@TypeOf(a, b) == []const T);
1629 comptime assert(@TypeOf(b, a) == []const T);
1630
1631 var t = true;
1632 const r1 = if (t) a else b;
1633 const r2 = if (t) b else a;
1634
1635 const R = @TypeOf(r1);
1636
1637 try expectEqual(@as(R, @intToPtr(*const [2:s]T, 0x1000)), r1);
1638 try expectEqual(@as(R, @intToPtr(*const [3]T, 0x2000)), r2);
1639 }
1640 };
1641
1642 try S.doTheTest(u8, 0);
1643 try S.doTheTest(?*anyopaque, null);
1644}
1645
1646test "peer type resolution: float and comptime-known fixed-width integer" {
1647 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1648 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1649 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1650 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1651 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1652 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
1653
1654 const i: u8 = 100;
1655 var f: f32 = 1.234;
1656 comptime assert(@TypeOf(i, f) == f32);
1657 comptime assert(@TypeOf(f, i) == f32);
1658
1659 var t = true;
1660 const r1 = if (t) i else f;
1661 const r2 = if (t) f else i;
1662
1663 const T = @TypeOf(r1);
1664
1665 try expectEqual(@as(T, 100.0), r1);
1666 try expectEqual(@as(T, 1.234), r2);
1667}
1668
1669test "peer type resolution: same array type with sentinel" {
1670 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1671 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1672 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1673 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1674 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1675 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
1676
1677 var a: [2:0]u32 = .{ 0, 1 };
1678 var b: [2:0]u32 = .{ 2, 3 };
1679 comptime assert(@TypeOf(a, b) == [2:0]u32);
1680 comptime assert(@TypeOf(b, a) == [2:0]u32);
1681
1682 var t = true;
1683 const r1 = if (t) a else b;
1684 const r2 = if (t) b else a;
1685
1686 const T = @TypeOf(r1);
1687
1688 try expectEqual(T{ 0, 1 }, r1);
1689 try expectEqual(T{ 2, 3 }, r2);
1690}
1691
1692test "peer type resolution: array with sentinel and array without sentinel" {
1693 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1694 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1695 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1696 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1697 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1698 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
1699
1700 var a: [2:0]u32 = .{ 0, 1 };
1701 var b: [2]u32 = .{ 2, 3 };
1702 comptime assert(@TypeOf(a, b) == [2]u32);
1703 comptime assert(@TypeOf(b, a) == [2]u32);
1704
1705 var t = true;
1706 const r1 = if (t) a else b;
1707 const r2 = if (t) b else a;
1708
1709 const T = @TypeOf(r1);
1710
1711 try expectEqual(T{ 0, 1 }, r1);
1712 try expectEqual(T{ 2, 3 }, r2);
1713}
1714
1715test "peer type resolution: array and vector with same child type" {
1716 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1717 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1718 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1719 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1720 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1721 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
1722
1723 var arr: [2]u32 = .{ 0, 1 };
1724 var vec: @Vector(2, u32) = .{ 2, 3 };
1725 comptime assert(@TypeOf(arr, vec) == @Vector(2, u32));
1726 comptime assert(@TypeOf(vec, arr) == @Vector(2, u32));
1727
1728 var t = true;
1729 const r1 = if (t) arr else vec;
1730 const r2 = if (t) vec else arr;
1731
1732 const T = @TypeOf(r1);
1733
1734 try expectEqual(T{ 0, 1 }, r1);
1735 try expectEqual(T{ 2, 3 }, r2);
1736}
1737
1738test "peer type resolution: array with smaller child type and vector with larger child type" {
1739 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1740 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1741 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1742 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1743 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1744 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
1745
1746 var arr: [2]u8 = .{ 0, 1 };
1747 var vec: @Vector(2, u64) = .{ 2, 3 };
1748 comptime assert(@TypeOf(arr, vec) == @Vector(2, u64));
1749 comptime assert(@TypeOf(vec, arr) == @Vector(2, u64));
1750
1751 var t = true;
1752 const r1 = if (t) arr else vec;
1753 const r2 = if (t) vec else arr;
1754
1755 const T = @TypeOf(r1);
1756
1757 try expectEqual(T{ 0, 1 }, r1);
1758 try expectEqual(T{ 2, 3 }, r2);
1759}
1760
1761test "peer type resolution: error union and optional of same type" {
1762 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1763 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1764 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1765 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1766 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1767 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
1768
1769 const E = error{Foo};
1770 var a: E!*u8 = error.Foo;
1771 var b: ?*u8 = null;
1772 comptime assert(@TypeOf(a, b) == E!?*u8);
1773 comptime assert(@TypeOf(b, a) == E!?*u8);
1774
1775 var t = true;
1776 const r1 = if (t) a else b;
1777 const r2 = if (t) b else a;
1778
1779 const T = @TypeOf(r1);
1780
1781 try expectEqual(@as(T, error.Foo), r1);
1782 try expectEqual(@as(T, null), r2);
1783}
1784
1785test "peer type resolution: C pointer and @TypeOf(null)" {
1786 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1787 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1788 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1789 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1790 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1791 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
1792
1793 var a: [*c]c_int = 0x1000;
1794 const b = null;
1795 comptime assert(@TypeOf(a, b) == [*c]c_int);
1796 comptime assert(@TypeOf(b, a) == [*c]c_int);
1797
1798 var t = true;
1799 const r1 = if (t) a else b;
1800 const r2 = if (t) b else a;
1801
1802 const T = @TypeOf(r1);
1803
1804 try expectEqual(@as(T, 0x1000), r1);
1805 try expectEqual(@as(T, null), r2);
1806}
1807
1808test "peer type resolution: three-way resolution combines error set and optional" {
1809 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1810 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1811 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1812 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1813 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1814 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
1815
1816 const E = error{Foo};
1817 var a: E = error.Foo;
1818 var b: *const [5:0]u8 = @intToPtr(*const [5:0]u8, 0x1000);
1819 var c: ?[*:0]u8 = null;
1820 comptime assert(@TypeOf(a, b, c) == E!?[*:0]const u8);
1821 comptime assert(@TypeOf(a, c, b) == E!?[*:0]const u8);
1822 comptime assert(@TypeOf(b, a, c) == E!?[*:0]const u8);
1823 comptime assert(@TypeOf(b, c, a) == E!?[*:0]const u8);
1824 comptime assert(@TypeOf(c, a, b) == E!?[*:0]const u8);
1825 comptime assert(@TypeOf(c, b, a) == E!?[*:0]const u8);
1826
1827 var x: u8 = 0;
1828 const r1 = switch (x) {
1829 0 => a,
1830 1 => b,
1831 else => c,
1832 };
1833 const r2 = switch (x) {
1834 0 => b,
1835 1 => a,
1836 else => c,
1837 };
1838 const r3 = switch (x) {
1839 0 => c,
1840 1 => a,
1841 else => b,
1842 };
1843
1844 const T = @TypeOf(r1);
1845
1846 try expectEqual(@as(T, error.Foo), r1);
1847 try expectEqual(@as(T, @intToPtr([*:0]u8, 0x1000)), r2);
1848 try expectEqual(@as(T, null), r3);
1849}
1850
1851test "peer type resolution: vector and optional vector" {
1852 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1853 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1854 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1855 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1856 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1857 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
1858
1859 var a: ?@Vector(3, u32) = .{ 0, 1, 2 };
1860 var b: @Vector(3, u32) = .{ 3, 4, 5 };
1861 comptime assert(@TypeOf(a, b) == ?@Vector(3, u32));
1862 comptime assert(@TypeOf(b, a) == ?@Vector(3, u32));
1863
1864 var t = true;
1865 const r1 = if (t) a else b;
1866 const r2 = if (t) b else a;
1867
1868 const T = @TypeOf(r1);
1869
1870 try expectEqual(@as(T, .{ 0, 1, 2 }), r1);
1871 try expectEqual(@as(T, .{ 3, 4, 5 }), r2);
1872}
1873
1874test "peer type resolution: optional fixed-width int and comptime_int" {
1875 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1876 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1877 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1878 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1879 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1880 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
1881
1882 var a: ?i32 = 42;
1883 const b: comptime_int = 50;
1884 comptime assert(@TypeOf(a, b) == ?i32);
1885 comptime assert(@TypeOf(b, a) == ?i32);
1886
1887 var t = true;
1888 const r1 = if (t) a else b;
1889 const r2 = if (t) b else a;
1890
1891 const T = @TypeOf(r1);
1892
1893 try expectEqual(@as(T, 42), r1);
1894 try expectEqual(@as(T, 50), r2);
1895}
1896
1897test "peer type resolution: array and tuple" {
1898 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1899 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1900 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1901 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1902 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1903 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
1904
1905 var arr: [3]i32 = .{ 1, 2, 3 };
1906 const tup = .{ 4, 5, 6 };
1907
1908 comptime assert(@TypeOf(arr, tup) == [3]i32);
1909 comptime assert(@TypeOf(tup, arr) == [3]i32);
1910
1911 var t = true;
1912 const r1 = if (t) arr else tup;
1913 const r2 = if (t) tup else arr;
1914
1915 const T = @TypeOf(r1);
1916
1917 try expectEqual(T{ 1, 2, 3 }, r1);
1918 try expectEqual(T{ 4, 5, 6 }, r2);
1919}
1920
1921test "peer type resolution: vector and tuple" {
1922 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1923 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1924 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1925 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1926 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1927 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
1928
1929 var vec: @Vector(3, i32) = .{ 1, 2, 3 };
1930 const tup = .{ 4, 5, 6 };
1931
1932 comptime assert(@TypeOf(vec, tup) == @Vector(3, i32));
1933 comptime assert(@TypeOf(tup, vec) == @Vector(3, i32));
1934
1935 var t = true;
1936 const r1 = if (t) vec else tup;
1937 const r2 = if (t) tup else vec;
1938
1939 const T = @TypeOf(r1);
1940
1941 try expectEqual(T{ 1, 2, 3 }, r1);
1942 try expectEqual(T{ 4, 5, 6 }, r2);
1943}
1944
1945test "peer type resolution: vector and array and tuple" {
1946 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1947 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1948 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1949 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1950 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1951 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
1952
1953 var vec: @Vector(2, i8) = .{ 10, 20 };
1954 var arr: [2]i8 = .{ 30, 40 };
1955 const tup = .{ 50, 60 };
1956
1957 comptime assert(@TypeOf(vec, arr, tup) == @Vector(2, i8));
1958 comptime assert(@TypeOf(vec, tup, arr) == @Vector(2, i8));
1959 comptime assert(@TypeOf(arr, vec, tup) == @Vector(2, i8));
1960 comptime assert(@TypeOf(arr, tup, vec) == @Vector(2, i8));
1961 comptime assert(@TypeOf(tup, vec, arr) == @Vector(2, i8));
1962 comptime assert(@TypeOf(tup, arr, vec) == @Vector(2, i8));
1963
1964 var x: u8 = 0;
1965 const r1 = switch (x) {
1966 0 => vec,
1967 1 => arr,
1968 else => tup,
1969 };
1970 const r2 = switch (x) {
1971 0 => arr,
1972 1 => vec,
1973 else => tup,
1974 };
1975 const r3 = switch (x) {
1976 0 => tup,
1977 1 => vec,
1978 else => arr,
1979 };
1980
1981 const T = @TypeOf(r1);
1982
1983 try expectEqual(T{ 10, 20 }, r1);
1984 try expectEqual(T{ 30, 40 }, r2);
1985 try expectEqual(T{ 50, 60 }, r3);
1986}
1987
1988test "peer type resolution: empty tuple pointer and slice" {
1989 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1990 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1991 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1992 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1993 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1994 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
1995
1996 var a: [:0]const u8 = "Hello";
1997 var b = &.{};
1998
1999 comptime assert(@TypeOf(a, b) == []const u8);
2000 comptime assert(@TypeOf(b, a) == []const u8);
2001
2002 var t = true;
2003 const r1 = if (t) a else b;
2004 const r2 = if (t) b else a;
2005
2006 try expectEqualSlices(u8, "Hello", r1);
2007 try expectEqualSlices(u8, "", r2);
2008}
2009
2010test "peer type resolution: tuple pointer and slice" {
2011 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
2012 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
2013 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
2014 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
2015 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2016 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
2017
2018 var a: [:0]const u8 = "Hello";
2019 var b = &.{ @as(u8, 'x'), @as(u8, 'y'), @as(u8, 'z') };
2020
2021 comptime assert(@TypeOf(a, b) == []const u8);
2022 comptime assert(@TypeOf(b, a) == []const u8);
2023
2024 var t = true;
2025 const r1 = if (t) a else b;
2026 const r2 = if (t) b else a;
2027
2028 try expectEqualSlices(u8, "Hello", r1);
2029 try expectEqualSlices(u8, "xyz", r2);
2030}
2031
2032test "peer type resolution: tuple pointer and optional slice" {
2033 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
2034 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
2035 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
2036 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
2037 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2038 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
2039
2040 var a: ?[:0]const u8 = null;
2041 var b = &.{ @as(u8, 'x'), @as(u8, 'y'), @as(u8, 'z') };
2042
2043 comptime assert(@TypeOf(a, b) == ?[]const u8);
2044 comptime assert(@TypeOf(b, a) == ?[]const u8);
2045
2046 var t = true;
2047 const r1 = if (t) a else b;
2048 const r2 = if (t) b else a;
2049
2050 try expectEqual(@as(?[]const u8, null), r1);
2051 try expectEqualSlices(u8, "xyz", r2 orelse "");
2052}
2053
2054test "peer type resolution: many compatible pointers" {
2055 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
2056 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
2057 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
2058 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
2059 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2060 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
2061
2062 var buf = "foo-3".*;
2063
2064 var vals = .{
2065 @as([*]const u8, "foo-0"),
2066 @as([*:0]const u8, "foo-1"),
2067 @as([*:0]const u8, "foo-2"),
2068 @as([*]u8, &buf),
2069 @as(*const [5]u8, "foo-4"),
2070 };
2071
2072 // Check every possible permutation of types in @TypeOf
2073 @setEvalBranchQuota(5000);
2074 comptime var perms = 0; // check the loop is hitting every permutation
2075 inline for (0..5) |i_0| {
2076 inline for (0..5) |i_1| {
2077 if (i_1 == i_0) continue;
2078 inline for (0..5) |i_2| {
2079 if (i_2 == i_0 or i_2 == i_1) continue;
2080 inline for (0..5) |i_3| {
2081 if (i_3 == i_0 or i_3 == i_1 or i_3 == i_2) continue;
2082 inline for (0..5) |i_4| {
2083 if (i_4 == i_0 or i_4 == i_1 or i_4 == i_2 or i_4 == i_3) continue;
2084 perms += 1;
2085 comptime assert(@TypeOf(
2086 vals[i_0],
2087 vals[i_1],
2088 vals[i_2],
2089 vals[i_3],
2090 vals[i_4],
2091 ) == [*]const u8);
2092 }
2093 }
2094 }
2095 }
2096 }
2097 comptime assert(perms == 5 * 4 * 3 * 2 * 1);
2098
2099 var x: u8 = 0;
2100 inline for (0..5) |i| {
2101 const r = switch (x) {
2102 0 => vals[i],
2103 1 => vals[0],
2104 2 => vals[1],
2105 3 => vals[2],
2106 4 => vals[3],
2107 else => vals[4],
2108 };
2109 const expected = switch (i) {
2110 0 => "foo-0",
2111 1 => "foo-1",
2112 2 => "foo-2",
2113 3 => "foo-3",
2114 4 => "foo-4",
2115 else => unreachable,
2116 };
2117 try expectEqualSlices(u8, expected, std.mem.span(@ptrCast([*:0]const u8, r)));
2118 }
2119}
2120
2121test "peer type resolution: tuples with comptime fields" {
2122 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
2123 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
2124 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
2125 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
2126 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2127 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
2128
2129 const a = .{ 1, 2 };
2130 const b = .{ @as(u32, 3), @as(i16, 4) };
2131
2132 // TODO: tuple type equality doesn't work properly yet
2133 const ti1 = @typeInfo(@TypeOf(a, b));
2134 const ti2 = @typeInfo(@TypeOf(b, a));
2135 inline for (.{ ti1, ti2 }) |ti| {
2136 const s = ti.Struct;
2137 comptime assert(s.is_tuple);
2138 comptime assert(s.fields.len == 2);
2139 comptime assert(s.fields[0].type == u32);
2140 comptime assert(s.fields[1].type == i16);
2141 }
2142
2143 var t = true;
2144 const r1 = if (t) a else b;
2145 const r2 = if (t) b else a;
2146
2147 try expectEqual(@as(u32, 1), r1[0]);
2148 try expectEqual(@as(i16, 2), r1[1]);
2149
2150 try expectEqual(@as(u32, 3), r2[0]);
2151 try expectEqual(@as(i16, 4), r2[1]);
2152}
2153
2154test "peer type resolution: C pointer and many pointer" {
2155 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
2156 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
2157 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
2158 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
2159 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2160 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
2161
2162 var buf = "hello".*;
2163
2164 var a: [*c]u8 = &buf;
2165 var b: [*:0]const u8 = "world";
2166
2167 comptime assert(@TypeOf(a, b) == [*c]const u8);
2168 comptime assert(@TypeOf(b, a) == [*c]const u8);
2169
2170 var t = true;
2171 const r1 = if (t) a else b;
2172 const r2 = if (t) b else a;
2173
2174 try expectEqual(r1, a);
2175 try expectEqual(r2, b);
2176}
2177
2178test "peer type resolution: pointer attributes are combined correctly" {
2179 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
2180 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
2181 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
2182 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
2183 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2184 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
2185
2186 var buf_a align(4) = "foo".*;
2187 var buf_b align(4) = "bar".*;
2188 var buf_c align(4) = "baz".*;
2189
2190 var a: [*:0]align(4) const u8 = &buf_a;
2191 var b: *align(2) volatile [3:0]u8 = &buf_b;
2192 var c: [*:0]align(4) u8 = &buf_c;
2193
2194 comptime assert(@TypeOf(a, b, c) == [*:0]align(2) const volatile u8);
2195 comptime assert(@TypeOf(a, c, b) == [*:0]align(2) const volatile u8);
2196 comptime assert(@TypeOf(b, a, c) == [*:0]align(2) const volatile u8);
2197 comptime assert(@TypeOf(b, c, a) == [*:0]align(2) const volatile u8);
2198 comptime assert(@TypeOf(c, a, b) == [*:0]align(2) const volatile u8);
2199 comptime assert(@TypeOf(c, b, a) == [*:0]align(2) const volatile u8);
2200
2201 var x: u8 = 0;
2202 const r1 = switch (x) {
2203 0 => a,
2204 1 => b,
2205 else => c,
2206 };
2207 const r2 = switch (x) {
2208 0 => b,
2209 1 => a,
2210 else => c,
2211 };
2212 const r3 = switch (x) {
2213 0 => c,
2214 1 => a,
2215 else => b,
2216 };
2217
2218 try expectEqualSlices(u8, std.mem.span(@volatileCast(r1)), "foo");
2219 try expectEqualSlices(u8, std.mem.span(@volatileCast(r2)), "bar");
2220 try expectEqualSlices(u8, std.mem.span(@volatileCast(r3)), "baz");
2221}
test/cases/compile_errors/compare_optional_to_non-optional_with_invalid_types.zig deleted-37
...@@ -1,37 +0,0 @@
1export fn inconsistentChildType() void {
2 var x: ?i32 = undefined;
3 const y: comptime_int = 10;
4 _ = (x == y);
5}
6export fn optionalToOptional() void {
7 var x: ?i32 = undefined;
8 var y: ?i32 = undefined;
9 _ = (x == y);
10}
11export fn optionalVector() void {
12 var x: ?@Vector(10, i32) = undefined;
13 var y: @Vector(10, i32) = undefined;
14 _ = (x == y);
15}
16export fn optionalVector2() void {
17 var x: ?@Vector(10, i32) = undefined;
18 var y: @Vector(11, i32) = undefined;
19 _ = (x == y);
20}
21export fn invalidChildType() void {
22 var x: ?[3]i32 = undefined;
23 var y: [3]i32 = undefined;
24 _ = (x == y);
25}
26
27// error
28// backend=llvm
29// target=native
30//
31// :4:12: error: incompatible types: '?i32' and 'comptime_int'
32// :4:10: note: type '?i32' here
33// :4:15: note: type 'comptime_int' here
34// :19:12: error: incompatible types: '?@Vector(10, i32)' and '@Vector(11, i32)'
35// :19:10: note: type '?@Vector(10, i32)' here
36// :19:15: note: type '@Vector(11, i32)' here
37// :24:12: error: operator == not allowed for type '?[3]i32'
test/cases/compile_errors/compare_optional_to_non_optional_with_incomparable_type.zig created+11
...@@ -0,0 +1,11 @@
1export fn entry() void {
2 var x: ?[3]i32 = undefined;
3 var y: [3]i32 = undefined;
4 _ = (x == y);
5}
6
7// error
8// backend=llvm
9// target=native
10//
11// :4:12: error: operator == not allowed for type '?[3]i32'
test/cases/compile_errors/invalid_peer_type_resolution.zig created+50
...@@ -0,0 +1,50 @@
1export fn optionalVector() void {
2 var x: ?@Vector(10, i32) = undefined;
3 var y: @Vector(11, i32) = undefined;
4 _ = @TypeOf(x, y);
5}
6export fn badTupleField() void {
7 var x = .{ @as(u8, 0), @as(u32, 1) };
8 var y = .{ @as(u8, 1), "hello" };
9 _ = @TypeOf(x, y);
10}
11export fn badNestedField() void {
12 const x = .{ .foo = "hi", .bar = .{ 0, 1 } };
13 const y = .{ .foo = "hello", .bar = .{ 2, "hi" } };
14 _ = @TypeOf(x, y);
15}
16export fn incompatiblePointers() void {
17 const x: []const u8 = "foo";
18 const y: [*:0]const u8 = "bar";
19 _ = @TypeOf(x, y);
20}
21export fn incompatiblePointers4() void {
22 const a: *const [5]u8 = "hello";
23 const b: *const [3:0]u8 = "foo";
24 const c: []const u8 = "baz"; // The conflict must be reported against this element!
25 const d: [*]const u8 = "bar";
26 _ = @TypeOf(a, b, c, d);
27}
28
29// error
30// backend=llvm
31// target=native
32//
33// :4:9: error: incompatible types: '?@Vector(10, i32)' and '@Vector(11, i32)'
34// :4:17: note: type '?@Vector(10, i32)' here
35// :4:20: note: type '@Vector(11, i32)' here
36// :9:9: error: struct field '1' has conflicting types
37// :9:9: note: incompatible types: 'u32' and '*const [5:0]u8'
38// :9:17: note: type 'u32' here
39// :9:20: note: type '*const [5:0]u8' here
40// :14:9: error: struct field 'bar' has conflicting types
41// :14:9: note: struct field '1' has conflicting types
42// :14:9: note: incompatible types: 'comptime_int' and '*const [2:0]u8'
43// :14:17: note: type 'comptime_int' here
44// :14:20: note: type '*const [2:0]u8' here
45// :19:9: error: incompatible types: '[]const u8' and '[*:0]const u8'
46// :19:17: note: type '[]const u8' here
47// :19:20: note: type '[*:0]const u8' here
48// :26:9: error: incompatible types: '[]const u8' and '[*]const u8'
49// :26:23: note: type '[]const u8' here
50// :26:26: note: type '[*]const u8' here