authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-06-14 07:18:28-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-06-14 07:18:28-07:00
log496320d9350971fca063d5297ab42ddd9c4dbe79
tree11157d630fecd58a2606e3ec819bd41d5cc8745e
parent666ae24816541e80e3cf9f5d4d73dcce7c4481de
parent8a92beb088c5eb890f0b662ca6e0c8d68b72fd6a
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #15726 from mlugg/feat/peer-type-resolution-but-better

Sema: rewrite peer type resolution

7 files changed, 2140 insertions(+), 636 deletions(-)

src/InternPool.zig+93
......@@ -4568,6 +4568,7 @@ pub fn sliceLen(ip: *const InternPool, i: Index) Index {
45684568/// * int <=> int
45694569/// * int <=> enum
45704570/// * enum_literal => enum
4571/// * float <=> float
45714572/// * ptr <=> ptr
45724573/// * opt ptr <=> ptr
45734574/// * opt ptr <=> opt ptr
......@@ -4579,6 +4580,7 @@ pub fn sliceLen(ip: *const InternPool, i: Index) Index {
45794580/// * error set => error union
45804581/// * payload => error union
45814582/// * fn <=> fn
4583/// * aggregate <=> aggregate (where children can also be coerced)
45824584pub fn getCoerced(ip: *InternPool, gpa: Allocator, val: Index, new_ty: Index) Allocator.Error!Index {
45834585 const old_ty = ip.typeOf(val);
45844586 if (old_ty == new_ty) return val;
......@@ -4623,6 +4625,23 @@ pub fn getCoerced(ip: *InternPool, gpa: Allocator, val: Index, new_ty: Index) Al
46234625 else => if (ip.isIntegerType(new_ty))
46244626 return getCoercedInts(ip, gpa, int, new_ty),
46254627 },
4628 .float => |float| switch (ip.indexToKey(new_ty)) {
4629 .simple_type => |simple| switch (simple) {
4630 .f16,
4631 .f32,
4632 .f64,
4633 .f80,
4634 .f128,
4635 .c_longdouble,
4636 .comptime_float,
4637 => return ip.get(gpa, .{ .float = .{
4638 .ty = new_ty,
4639 .storage = float.storage,
4640 } }),
4641 else => {},
4642 },
4643 else => {},
4644 },
46264645 .enum_tag => |enum_tag| if (ip.isIntegerType(new_ty))
46274646 return getCoercedInts(ip, gpa, ip.indexToKey(enum_tag.int).int, new_ty),
46284647 .enum_literal => |enum_literal| switch (ip.indexToKey(new_ty)) {
......@@ -4688,6 +4707,80 @@ pub fn getCoerced(ip: *InternPool, gpa: Allocator, val: Index, new_ty: Index) Al
46884707 .ty = new_ty,
46894708 .val = error_union.val,
46904709 } }),
4710 .aggregate => |aggregate| {
4711 const new_len = @intCast(usize, ip.aggregateTypeLen(new_ty));
4712 direct: {
4713 const old_ty_child = switch (ip.indexToKey(old_ty)) {
4714 inline .array_type, .vector_type => |seq_type| seq_type.child,
4715 .anon_struct_type, .struct_type => break :direct,
4716 else => unreachable,
4717 };
4718 const new_ty_child = switch (ip.indexToKey(new_ty)) {
4719 inline .array_type, .vector_type => |seq_type| seq_type.child,
4720 .anon_struct_type, .struct_type => break :direct,
4721 else => unreachable,
4722 };
4723 if (old_ty_child != new_ty_child) break :direct;
4724 // TODO: write something like getCoercedInts to avoid needing to dupe here
4725 switch (aggregate.storage) {
4726 .bytes => |bytes| {
4727 const bytes_copy = try gpa.dupe(u8, bytes[0..new_len]);
4728 defer gpa.free(bytes_copy);
4729 return ip.get(gpa, .{ .aggregate = .{
4730 .ty = new_ty,
4731 .storage = .{ .bytes = bytes_copy },
4732 } });
4733 },
4734 .elems => |elems| {
4735 const elems_copy = try gpa.dupe(InternPool.Index, elems[0..new_len]);
4736 defer gpa.free(elems_copy);
4737 return ip.get(gpa, .{ .aggregate = .{
4738 .ty = new_ty,
4739 .storage = .{ .elems = elems_copy },
4740 } });
4741 },
4742 .repeated_elem => |elem| {
4743 return ip.get(gpa, .{ .aggregate = .{
4744 .ty = new_ty,
4745 .storage = .{ .repeated_elem = elem },
4746 } });
4747 },
4748 }
4749 }
4750 // Direct approach failed - we must recursively coerce elems
4751 const agg_elems = try gpa.alloc(InternPool.Index, new_len);
4752 defer gpa.free(agg_elems);
4753 // First, fill the vector with the uncoerced elements. We do this to avoid key
4754 // lifetime issues, since it'll allow us to avoid referencing `aggregate` after we
4755 // begin interning elems.
4756 switch (aggregate.storage) {
4757 .bytes => {
4758 // We have to intern each value here, so unfortunately we can't easily avoid
4759 // the repeated indexToKey calls.
4760 for (agg_elems, 0..) |*elem, i| {
4761 const x = ip.indexToKey(val).aggregate.storage.bytes[i];
4762 elem.* = try ip.get(gpa, .{ .int = .{
4763 .ty = .u8_type,
4764 .storage = .{ .u64 = x },
4765 } });
4766 }
4767 },
4768 .elems => |elems| @memcpy(agg_elems, elems[0..new_len]),
4769 .repeated_elem => |elem| @memset(agg_elems, elem),
4770 }
4771 // Now, coerce each element to its new type.
4772 for (agg_elems, 0..) |*elem, i| {
4773 const new_elem_ty = switch (ip.indexToKey(new_ty)) {
4774 inline .array_type, .vector_type => |seq_type| seq_type.child,
4775 .anon_struct_type => |anon_struct_type| anon_struct_type.types[i],
4776 .struct_type => |struct_type| ip.structPtr(struct_type.index.unwrap().?)
4777 .fields.values()[i].ty.toIntern(),
4778 else => unreachable,
4779 };
4780 elem.* = try ip.getCoerced(gpa, elem.*, new_elem_ty);
4781 }
4782 return ip.get(gpa, .{ .aggregate = .{ .ty = new_ty, .storage = .{ .elems = agg_elems } } });
4783 },
46914784 else => {},
46924785 },
46934786 }
src/Module.zig+1-1
......@@ -6978,7 +6978,7 @@ pub fn intBitsForValue(mod: *Module, val: Value, sign: bool) u16 {
69786978 assert(sign);
69796979 // Protect against overflow in the following negation.
69806980 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;
69826982 },
69836983 .u64 => |x| {
69846984 return Type.smallestUnsignedBits(x) + @boolToInt(sign);
src/Sema.zig+1375-598
......@@ -23069,7 +23069,7 @@ fn analyzeMinMax(
2306923069 if (std.debug.runtime_safety) {
2307023070 assert(try sema.intFitsInType(val, refined_ty, null));
2307123071 }
23072 cur_minmax = try sema.coerceInMemory(block, val, orig_ty, refined_ty, src);
23072 cur_minmax = try sema.coerceInMemory(val, refined_ty);
2307323073 }
2307423074
2307523075 break :refined refined_ty;
......@@ -26610,7 +26610,7 @@ fn coerceExtra(
2661026610 var in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src);
2661126611 if (in_memory_result == .ok) {
2661226612 if (maybe_inst_val) |val| {
26613 return sema.coerceInMemory(block, val, inst_ty, dest_ty, dest_ty_src);
26613 return sema.coerceInMemory(val, dest_ty);
2661426614 }
2661526615 try sema.requireRuntimeBlock(block, inst_src, null);
2661626616 return block.addBitCast(dest_ty, inst);
......@@ -27278,82 +27278,12 @@ fn coerceExtra(
2727827278 return sema.failWithOwnedErrorMsg(msg);
2727927279}
2728027280
27281fn coerceValueInMemory(
27282 sema: *Sema,
27283 block: *Block,
27284 val: Value,
27285 src_ty: Type,
27286 dst_ty: Type,
27287 dst_ty_src: LazySrcLoc,
27288) CompileError!Value {
27289 const mod = sema.mod;
27290 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
27291 .aggregate => |aggregate| {
27292 const dst_ty_key = mod.intern_pool.indexToKey(dst_ty.toIntern());
27293 const dest_len = try sema.usizeCast(
27294 block,
27295 dst_ty_src,
27296 mod.intern_pool.aggregateTypeLen(dst_ty.toIntern()),
27297 );
27298 direct: {
27299 const src_ty_child = switch (mod.intern_pool.indexToKey(src_ty.toIntern())) {
27300 inline .array_type, .vector_type => |seq_type| seq_type.child,
27301 .anon_struct_type, .struct_type => break :direct,
27302 else => unreachable,
27303 };
27304 const dst_ty_child = switch (dst_ty_key) {
27305 inline .array_type, .vector_type => |seq_type| seq_type.child,
27306 .anon_struct_type, .struct_type => break :direct,
27307 else => unreachable,
27308 };
27309 if (src_ty_child != dst_ty_child) break :direct;
27310 // TODO: write something like getCoercedInts to avoid needing to dupe
27311 return (try mod.intern(.{ .aggregate = .{
27312 .ty = dst_ty.toIntern(),
27313 .storage = switch (aggregate.storage) {
27314 .bytes => |bytes| .{ .bytes = try sema.arena.dupe(u8, bytes[0..dest_len]) },
27315 .elems => |elems| .{ .elems = try sema.arena.dupe(InternPool.Index, elems[0..dest_len]) },
27316 .repeated_elem => |elem| .{ .repeated_elem = elem },
27317 },
27318 } })).toValue();
27319 }
27320 const dest_elems = try sema.arena.alloc(InternPool.Index, dest_len);
27321 for (dest_elems, 0..) |*dest_elem, i| {
27322 const elem_ty = switch (dst_ty_key) {
27323 inline .array_type, .vector_type => |seq_type| seq_type.child,
27324 .anon_struct_type => |anon_struct_type| anon_struct_type.types[i],
27325 .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?
27326 .fields.values()[i].ty.toIntern(),
27327 else => unreachable,
27328 };
27329 dest_elem.* = try mod.intern_pool.getCoerced(mod.gpa, switch (aggregate.storage) {
27330 .bytes => |bytes| (try mod.intValue(Type.u8, bytes[i])).toIntern(),
27331 .elems => |elems| elems[i],
27332 .repeated_elem => |elem| elem,
27333 }, elem_ty);
27334 }
27335 return (try mod.intern(.{ .aggregate = .{
27336 .ty = dst_ty.toIntern(),
27337 .storage = .{ .elems = dest_elems },
27338 } })).toValue();
27339 },
27340 .float => |float| (try mod.intern(.{ .float = .{
27341 .ty = dst_ty.toIntern(),
27342 .storage = float.storage,
27343 } })).toValue(),
27344 else => try mod.getCoerced(val, dst_ty),
27345 };
27346}
27347
2734827281fn coerceInMemory(
2734927282 sema: *Sema,
27350 block: *Block,
2735127283 val: Value,
27352 src_ty: Type,
2735327284 dst_ty: Type,
27354 dst_ty_src: LazySrcLoc,
2735527285) CompileError!Air.Inst.Ref {
27356 return sema.addConstant(dst_ty, try sema.coerceValueInMemory(block, val, src_ty, dst_ty, dst_ty_src));
27286 return sema.addConstant(dst_ty, try sema.mod.getCoerced(val, dst_ty));
2735727287}
2735827288
2735927289const InMemoryCoercionResult = union(enum) {
......@@ -27891,6 +27821,22 @@ fn coerceInMemoryAllowed(
2789127821 return .ok;
2789227822 }
2789327823
27824 // Tuples (with in-memory-coercible fields)
27825 if (dest_ty.isTuple(mod) and src_ty.isTuple(mod)) tuple: {
27826 if (dest_ty.containerLayout(mod) != src_ty.containerLayout(mod)) break :tuple;
27827 if (dest_ty.structFieldCount(mod) != src_ty.structFieldCount(mod)) break :tuple;
27828 const field_count = dest_ty.structFieldCount(mod);
27829 for (0..field_count) |field_idx| {
27830 if (dest_ty.structFieldIsComptime(field_idx, mod) != src_ty.structFieldIsComptime(field_idx, mod)) break :tuple;
27831 if (dest_ty.structFieldAlign(field_idx, mod) != src_ty.structFieldAlign(field_idx, mod)) break :tuple;
27832 const dest_field_ty = dest_ty.structFieldType(field_idx, mod);
27833 const src_field_ty = src_ty.structFieldType(field_idx, mod);
27834 const field = try sema.coerceInMemoryAllowed(block, dest_field_ty, src_field_ty, dest_is_mut, target, dest_src, src_src);
27835 if (field != .ok) break :tuple;
27836 }
27837 return .ok;
27838 }
27839
2789427840 return InMemoryCoercionResult{ .no_match = .{
2789527841 .actual = dest_ty,
2789627842 .wanted = src_ty,
......@@ -27959,9 +27905,8 @@ fn coerceInMemoryAllowedErrorSets(
2795927905
2796027906 switch (src_ty.toIntern()) {
2796127907 .anyerror_type => switch (ip.indexToKey(dest_ty.toIntern())) {
27962 .inferred_error_set_type => unreachable, // Caught by dest_ty.isAnyError(mod) above.
2796327908 .simple_type => unreachable, // filtered out above
27964 .error_set_type => return .from_anyerror,
27909 .error_set_type, .inferred_error_set_type => return .from_anyerror,
2796527910 else => unreachable,
2796627911 },
2796727912
......@@ -28008,8 +27953,6 @@ fn coerceInMemoryAllowedErrorSets(
2800827953 else => unreachable,
2800927954 },
2801027955 }
28011
28012 unreachable;
2801327956}
2801427957
2801527958fn coerceInMemoryAllowedFns(
......@@ -29800,7 +29743,7 @@ fn coerceArrayLike(
2980029743 if (in_memory_result == .ok) {
2980129744 if (try sema.resolveMaybeUndefVal(inst)) |inst_val| {
2980229745 // These types share the same comptime value representation.
29803 return sema.coerceInMemory(block, inst_val, inst_ty, dest_ty, dest_ty_src);
29746 return sema.coerceInMemory(inst_val, dest_ty);
2980429747 }
2980529748 try sema.requireRuntimeBlock(block, inst_src, null);
2980629749 return block.addBitCast(dest_ty, inst);
......@@ -31586,601 +31529,1435 @@ fn unionToTag(
3158631529 return block.addTyOp(.get_union_tag, enum_ty, un);
3158731530}
3158831531
31589fn resolvePeerTypes(
31590 sema: *Sema,
31591 block: *Block,
31592 src: LazySrcLoc,
31593 instructions: []const Air.Inst.Ref,
31594 candidate_srcs: Module.PeerTypeCandidateSrc,
31595) !Type {
31596 const mod = sema.mod;
31597 switch (instructions.len) {
31598 0 => return Type.noreturn,
31599 1 => return sema.typeOf(instructions[0]),
31600 else => {},
31601 }
31602
31603 const target = mod.getTarget();
31604
31605 var chosen = instructions[0];
31606 // If this is non-null then it does the following thing, depending on the chosen zigTypeTag(mod).
31607 // * ErrorSet: this is an override
31608 // * ErrorUnion: this is an override of the error set only
31609 // * other: at the end we make an ErrorUnion with the other thing and this
31610 var err_set_ty: ?Type = null;
31611 var any_are_null = false;
31612 var seen_const = false;
31613 var convert_to_slice = false;
31614 var chosen_i: usize = 0;
31615 for (instructions[1..], 0..) |candidate, candidate_i| {
31616 const candidate_ty = sema.typeOf(candidate);
31617 const chosen_ty = sema.typeOf(chosen);
31618
31619 const candidate_ty_tag = try candidate_ty.zigTypeTagOrPoison(mod);
31620 const chosen_ty_tag = try chosen_ty.zigTypeTagOrPoison(mod);
31621
31622 // If the candidate can coerce into our chosen type, we're done.
31623 // If the chosen type can coerce into the candidate, use that.
31624 if ((try sema.coerceInMemoryAllowed(block, chosen_ty, candidate_ty, false, target, src, src)) == .ok) {
31625 continue;
31626 }
31627 if ((try sema.coerceInMemoryAllowed(block, candidate_ty, chosen_ty, false, target, src, src)) == .ok) {
31628 chosen = candidate;
31629 chosen_i = candidate_i + 1;
31630 continue;
31631 }
31632
31633 switch (candidate_ty_tag) {
31634 .NoReturn, .Undefined => continue,
31532const PeerResolveStrategy = enum {
31533 /// The type is not known.
31534 /// If refined no further, this is equivalent to `exact`.
31535 unknown,
31536 /// The type may be an error set or error union.
31537 /// If refined no further, it is an error set.
31538 error_set,
31539 /// The type must be some error union.
31540 error_union,
31541 /// The type may be @TypeOf(null), an optional or a C pointer.
31542 /// If refined no further, it is @TypeOf(null).
31543 nullable,
31544 /// The type must be some optional or a C pointer.
31545 /// If refined no further, it is an optional.
31546 optional,
31547 /// The type must be either an array or a vector.
31548 /// If refined no further, it is an array.
31549 array,
31550 /// The type must be a vector.
31551 vector,
31552 /// The type must be a C pointer.
31553 c_ptr,
31554 /// The type must be a pointer (C or not).
31555 /// If refined no further, it is a non-C pointer.
31556 ptr,
31557 /// The type must be a function or a pointer to a function.
31558 /// If refined no further, it is a function.
31559 func,
31560 /// The type must be an enum literal, or some specific enum or union. Which one is decided
31561 /// afterwards based on the types in question.
31562 enum_or_union,
31563 /// The type must be some integer or float type.
31564 /// If refined no further, it is `comptime_int`.
31565 comptime_int,
31566 /// The type must be some float type.
31567 /// If refined no further, it is `comptime_float`.
31568 comptime_float,
31569 /// The type must be some float or fixed-width integer type.
31570 /// If refined no further, it is some fixed-width integer type.
31571 fixed_int,
31572 /// The type must be some fixed-width float type.
31573 fixed_float,
31574 /// The type must be a struct literal or tuple type.
31575 coercible_struct,
31576 /// The peers must all be of the same type.
31577 exact,
31578
31579 /// Given two strategies, find a strategy that satisfies both, if one exists. If no such
31580 /// strategy exists, any strategy may be returned; an error will be emitted when the caller
31581 /// attempts to use the strategy to resolve the type.
31582 /// Strategy `a` comes from the peer in `reason_peer`, while strategy `b` comes from the peer at
31583 /// index `b_peer_idx`. `reason_peer` is updated to reflect the reason for the new strategy.
31584 fn merge(a: PeerResolveStrategy, b: PeerResolveStrategy, reason_peer: *usize, b_peer_idx: usize) PeerResolveStrategy {
31585 // Our merging should be order-independent. Thus, even though the union order is arbitrary,
31586 // by sorting the tags and switching first on the smaller, we have half as many cases to
31587 // worry about (since we avoid the duplicates).
31588 const s0_is_a = @enumToInt(a) <= @enumToInt(b);
31589 const s0 = if (s0_is_a) a else b;
31590 const s1 = if (s0_is_a) b else a;
31591
31592 const ReasonMethod = enum {
31593 all_s0,
31594 all_s1,
31595 either,
31596 };
3163531597
31636 .Null => {
31637 any_are_null = true;
31638 continue;
31598 const res: struct { ReasonMethod, PeerResolveStrategy } = switch (s0) {
31599 .unknown => .{ .all_s1, s1 },
31600 .error_set => switch (s1) {
31601 .error_set => .{ .either, .error_set },
31602 else => .{ .all_s0, .error_union },
3163931603 },
31640
31641 .Int => switch (chosen_ty_tag) {
31642 .ComptimeInt => {
31643 chosen = candidate;
31644 chosen_i = candidate_i + 1;
31645 continue;
31646 },
31647 .Int => {
31648 const chosen_info = chosen_ty.intInfo(mod);
31649 const candidate_info = candidate_ty.intInfo(mod);
31650
31651 if (chosen_info.bits < candidate_info.bits) {
31652 chosen = candidate;
31653 chosen_i = candidate_i + 1;
31654 }
31655 continue;
31656 },
31657 .Pointer => if (chosen_ty.ptrSize(mod) == .C) continue,
31658 else => {},
31604 .error_union => switch (s1) {
31605 .error_union => .{ .either, .error_union },
31606 else => .{ .all_s0, .error_union },
3165931607 },
31660 .ComptimeInt => switch (chosen_ty_tag) {
31661 .Int, .Float, .ComptimeFloat => continue,
31662 .Pointer => if (chosen_ty.ptrSize(mod) == .C) continue,
31663 else => {},
31608 .nullable => switch (s1) {
31609 .nullable => .{ .either, .nullable },
31610 .c_ptr => .{ .all_s1, .c_ptr },
31611 else => .{ .all_s0, .optional },
3166431612 },
31665 .Float => switch (chosen_ty_tag) {
31666 .Float => {
31667 if (chosen_ty.floatBits(target) < candidate_ty.floatBits(target)) {
31668 chosen = candidate;
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 => {},
31613 .optional => switch (s1) {
31614 .optional => .{ .either, .optional },
31615 .c_ptr => .{ .all_s1, .c_ptr },
31616 else => .{ .all_s0, .optional },
3167931617 },
31680 .ComptimeFloat => switch (chosen_ty_tag) {
31681 .Float => continue,
31682 .ComptimeInt => {
31683 chosen = candidate;
31684 chosen_i = candidate_i + 1;
31685 continue;
31686 },
31687 else => {},
31618 .array => switch (s1) {
31619 .array => .{ .either, .array },
31620 .vector => .{ .all_s1, .vector },
31621 else => .{ .all_s0, .array },
3168831622 },
31689 .Enum => switch (chosen_ty_tag) {
31690 .EnumLiteral => {
31691 chosen = candidate;
31692 chosen_i = candidate_i + 1;
31693 continue;
31694 },
31695 .Union => continue,
31696 else => {},
31623 .vector => switch (s1) {
31624 .vector => .{ .either, .vector },
31625 else => .{ .all_s0, .vector },
3169731626 },
31698 .EnumLiteral => switch (chosen_ty_tag) {
31699 .Enum, .Union => continue,
31700 else => {},
31627 .c_ptr => switch (s1) {
31628 .c_ptr => .{ .either, .c_ptr },
31629 else => .{ .all_s0, .c_ptr },
3170131630 },
31702 .Union => switch (chosen_ty_tag) {
31703 .Enum, .EnumLiteral => {
31704 chosen = candidate;
31705 chosen_i = candidate_i + 1;
31706 continue;
31707 },
31708 else => {},
31631 .ptr => switch (s1) {
31632 .ptr => .{ .either, .ptr },
31633 else => .{ .all_s0, .ptr },
3170931634 },
31710 .ErrorSet => switch (chosen_ty_tag) {
31711 .ErrorSet => {
31712 // If chosen is superset of candidate, keep it.
31713 // If candidate is superset of chosen, switch it.
31714 // If neither is a superset, merge errors.
31715 const chosen_set_ty = err_set_ty orelse chosen_ty;
31635 .func => switch (s1) {
31636 .func => .{ .either, .func },
31637 else => .{ .all_s1, s1 }, // doesn't override anything later
31638 },
31639 .enum_or_union => switch (s1) {
31640 .enum_or_union => .{ .either, .enum_or_union },
31641 else => .{ .all_s0, .enum_or_union },
31642 },
31643 .comptime_int => switch (s1) {
31644 .comptime_int => .{ .either, .comptime_int },
31645 else => .{ .all_s1, s1 }, // doesn't override anything later
31646 },
31647 .comptime_float => switch (s1) {
31648 .comptime_float => .{ .either, .comptime_float },
31649 else => .{ .all_s1, s1 }, // doesn't override anything later
31650 },
31651 .fixed_int => switch (s1) {
31652 .fixed_int => .{ .either, .fixed_int },
31653 else => .{ .all_s1, s1 }, // doesn't override anything later
31654 },
31655 .fixed_float => switch (s1) {
31656 .fixed_float => .{ .either, .fixed_float },
31657 else => .{ .all_s1, s1 }, // doesn't override anything later
31658 },
31659 .coercible_struct => switch (s1) {
31660 .exact => .{ .all_s1, .exact },
31661 else => .{ .all_s0, .coercible_struct },
31662 },
31663 .exact => .{ .all_s0, .exact },
31664 };
3171631665
31717 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_ty, src, src)) {
31718 continue;
31719 }
31720 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_ty, chosen_set_ty, src, src)) {
31721 err_set_ty = null;
31722 chosen = candidate;
31723 chosen_i = candidate_i + 1;
31724 continue;
31725 }
31666 switch (res[0]) {
31667 .all_s0 => {
31668 if (!s0_is_a) {
31669 reason_peer.* = b_peer_idx;
31670 }
31671 },
31672 .all_s1 => {
31673 if (s0_is_a) {
31674 reason_peer.* = b_peer_idx;
31675 }
31676 },
31677 .either => {
31678 // Prefer the earliest peer
31679 reason_peer.* = @min(reason_peer.*, b_peer_idx);
31680 },
31681 }
3172631682
31727 err_set_ty = try sema.errorSetMerge(chosen_set_ty, candidate_ty);
31728 continue;
31729 },
31730 .ErrorUnion => {
31731 const chosen_set_ty = err_set_ty orelse chosen_ty.errorUnionSet(mod);
31683 return res[1];
31684 }
3173231685
31733 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_ty, src, src)) {
31734 continue;
31735 }
31736 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_ty, chosen_set_ty, src, src)) {
31737 err_set_ty = candidate_ty;
31738 continue;
31739 }
31686 fn select(ty: Type, mod: *Module) PeerResolveStrategy {
31687 return switch (ty.zigTypeTag(mod)) {
31688 .Type, .Void, .Bool, .Opaque, .Frame, .AnyFrame => .exact,
31689 .NoReturn, .Undefined => .unknown,
31690 .Null => .nullable,
31691 .ComptimeInt => .comptime_int,
31692 .Int => .fixed_int,
31693 .ComptimeFloat => .comptime_float,
31694 .Float => .fixed_float,
31695 .Pointer => if (ty.ptrInfo(mod).size == .C) .c_ptr else .ptr,
31696 .Array => .array,
31697 .Vector => .vector,
31698 .Optional => .optional,
31699 .ErrorSet => .error_set,
31700 .ErrorUnion => .error_union,
31701 .EnumLiteral, .Enum, .Union => .enum_or_union,
31702 .Struct => if (ty.isTupleOrAnonStruct(mod)) .coercible_struct else .exact,
31703 .Fn => .func,
31704 };
31705 }
31706};
3174031707
31741 err_set_ty = try sema.errorSetMerge(chosen_set_ty, candidate_ty);
31742 continue;
31743 },
31744 else => {
31745 if (err_set_ty) |chosen_set_ty| {
31746 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_ty, src, src)) {
31747 continue;
31748 }
31749 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_ty, chosen_set_ty, src, src)) {
31750 err_set_ty = candidate_ty;
31751 continue;
31752 }
31708const PeerResolveResult = union(enum) {
31709 /// The peer type resolution was successful, and resulted in the given type.
31710 success: Type,
31711 /// There was some generic conflict between two peers.
31712 conflict: struct {
31713 peer_idx_a: usize,
31714 peer_idx_b: usize,
31715 },
31716 /// There was an error when resolving the type of a struct or tuple field.
31717 field_error: struct {
31718 /// The name of the field which caused the failure.
31719 field_name: []const u8,
31720 /// The type of this field in each peer.
31721 field_types: []Type,
31722 /// The error from resolving the field type. Guaranteed not to be `success`.
31723 sub_result: *PeerResolveResult,
31724 },
3175331725
31754 err_set_ty = try sema.errorSetMerge(chosen_set_ty, candidate_ty);
31755 continue;
31756 } else {
31757 err_set_ty = candidate_ty;
31758 continue;
31759 }
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);
31726 fn report(
31727 result: PeerResolveResult,
31728 sema: *Sema,
31729 block: *Block,
31730 src: LazySrcLoc,
31731 instructions: []const Air.Inst.Ref,
31732 candidate_srcs: Module.PeerTypeCandidateSrc,
31733 ) !*Module.ErrorMsg {
31734 const mod = sema.mod;
31735 const decl_ptr = mod.declPtr(block.src_decl);
31736
31737 var opt_msg: ?*Module.ErrorMsg = null;
31738 errdefer if (opt_msg) |msg| msg.destroy(sema.gpa);
3176631739
31767 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_set_ty, src, src)) {
31768 err_set_ty = chosen_set_ty;
31769 } else if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_set_ty, chosen_set_ty, src, src)) {
31770 err_set_ty = null;
31740 // If we mention fields we'll want to include field types, so put peer types in a buffer
31741 var peer_tys = try sema.arena.alloc(Type, instructions.len);
31742 for (peer_tys, instructions) |*ty, inst| {
31743 ty.* = sema.typeOf(inst);
31744 }
31745
31746 var cur = result;
31747 while (true) {
31748 var conflict_idx: [2]usize = undefined;
31749
31750 switch (cur) {
31751 .success => unreachable,
31752 .conflict => |conflict| {
31753 // Fall through to two-peer conflict handling below
31754 conflict_idx = .{
31755 conflict.peer_idx_a,
31756 conflict.peer_idx_b,
31757 };
31758 },
31759 .field_error => |field_error| {
31760 const fmt = "struct field '{s}' has conflicting types";
31761 const args = .{field_error.field_name};
31762 if (opt_msg) |msg| {
31763 try sema.errNote(block, src, msg, fmt, args);
3177131764 } else {
31772 err_set_ty = try sema.errorSetMerge(chosen_set_ty, candidate_set_ty);
31765 opt_msg = try sema.errMsg(block, src, fmt, args);
3177331766 }
31774 chosen = candidate;
31775 chosen_i = candidate_i + 1;
31767
31768 // Continue on to child error
31769 cur = field_error.sub_result.*;
31770 peer_tys = field_error.field_types;
3177631771 continue;
3177731772 },
31773 }
3177831774
31779 .ErrorUnion => {
31780 const chosen_payload_ty = chosen_ty.errorUnionPayload(mod);
31781 const candidate_payload_ty = candidate_ty.errorUnionPayload(mod);
31775 // This is the path for reporting a generic conflict between two peers.
3178231776
31783 const coerce_chosen = (try sema.coerceInMemoryAllowed(block, chosen_payload_ty, candidate_payload_ty, false, target, src, src)) == .ok;
31784 const coerce_candidate = (try sema.coerceInMemoryAllowed(block, candidate_payload_ty, chosen_payload_ty, false, target, src, src)) == .ok;
31777 if (conflict_idx[1] < conflict_idx[0]) {
31778 // b comes first in source, so it's better if it comes first in the error
31779 std.mem.swap(usize, &conflict_idx[0], &conflict_idx[1]);
31780 }
3178531781
31786 if (coerce_chosen or coerce_candidate) {
31787 // If we can coerce to the candidate, we switch to that
31788 // type. This is the same logic as the bare (non-union)
31789 // coercion check we do at the top of this func.
31790 if (coerce_candidate) {
31791 chosen = candidate;
31792 chosen_i = candidate_i + 1;
31793 }
31782 const conflict_tys: [2]Type = .{
31783 peer_tys[conflict_idx[0]],
31784 peer_tys[conflict_idx[1]],
31785 };
31786 const conflict_srcs: [2]?LazySrcLoc = .{
31787 candidate_srcs.resolve(mod, decl_ptr, conflict_idx[0]),
31788 candidate_srcs.resolve(mod, decl_ptr, conflict_idx[1]),
31789 };
3179431790
31795 const chosen_set_ty = err_set_ty orelse chosen_ty.errorUnionSet(mod);
31796 const candidate_set_ty = candidate_ty.errorUnionSet(mod);
31791 const fmt = "incompatible types: '{}' and '{}'";
31792 const args = .{
31793 conflict_tys[0].fmt(mod),
31794 conflict_tys[1].fmt(mod),
31795 };
31796 const msg = if (opt_msg) |msg| msg: {
31797 try sema.errNote(block, src, msg, fmt, args);
31798 break :msg msg;
31799 } else msg: {
31800 const msg = try sema.errMsg(block, src, fmt, args);
31801 opt_msg = msg;
31802 break :msg msg;
31803 };
3179731804
31798 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_set_ty, src, src)) {
31799 err_set_ty = chosen_set_ty;
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 },
31805 if (conflict_srcs[0]) |src_loc| try sema.errNote(block, src_loc, msg, "type '{}' here", .{conflict_tys[0].fmt(mod)});
31806 if (conflict_srcs[1]) |src_loc| try sema.errNote(block, src_loc, msg, "type '{}' here", .{conflict_tys[1].fmt(mod)});
3180831807
31809 else => {
31810 if (err_set_ty) |chosen_set_ty| {
31811 const candidate_set_ty = candidate_ty.errorUnionSet(mod);
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);
31808 // No child error
31809 break;
31810 }
3183131811
31832 seen_const = seen_const or !chosen_info.mutable or !cand_info.mutable;
31812 return opt_msg.?;
31813 }
31814};
3183331815
31834 // *[N]T to [*]T
31835 // *[N]T to []T
31836 if ((cand_info.size == .Many or cand_info.size == .Slice) and
31837 chosen_info.size == .One and
31838 chosen_info.pointee_type.zigTypeTag(mod) == .Array)
31839 {
31840 // In case we see i.e.: `*[1]T`, `*[2]T`, `[*]T`
31841 convert_to_slice = false;
31842 chosen = candidate;
31843 chosen_i = candidate_i + 1;
31844 continue;
31845 }
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 }
31816fn resolvePeerTypes(
31817 sema: *Sema,
31818 block: *Block,
31819 src: LazySrcLoc,
31820 instructions: []const Air.Inst.Ref,
31821 candidate_srcs: Module.PeerTypeCandidateSrc,
31822) !Type {
31823 switch (instructions.len) {
31824 0 => return Type.noreturn,
31825 1 => return sema.typeOf(instructions[0]),
31826 else => {},
31827 }
3185431828
31855 // *[N]T and *[M]T
31856 // Verify both are single-pointers to arrays.
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);
31829 var peer_tys = try sema.arena.alloc(?Type, instructions.len);
31830 var peer_vals = try sema.arena.alloc(?Value, instructions.len);
3186531831
31866 const chosen_ok = .ok == try sema.coerceInMemoryAllowed(block, chosen_elem_ty, cand_elem_ty, chosen_info.mutable, target, src, src);
31867 if (chosen_ok) {
31868 convert_to_slice = true;
31869 continue;
31870 }
31832 for (instructions, peer_tys, peer_vals) |inst, *ty, *val| {
31833 ty.* = sema.typeOf(inst);
31834 val.* = try sema.resolveMaybeUndefVal(inst);
31835 }
3187131836
31872 const cand_ok = .ok == try sema.coerceInMemoryAllowed(block, cand_elem_ty, chosen_elem_ty, cand_info.mutable, target, src, src);
31873 if (cand_ok) {
31874 convert_to_slice = true;
31875 chosen = candidate;
31876 chosen_i = candidate_i + 1;
31877 continue;
31878 }
31837 switch (try sema.resolvePeerTypesInner(block, src, peer_tys, peer_vals)) {
31838 .success => |ty| return ty,
31839 else => |result| {
31840 const msg = try result.report(sema, block, src, instructions, candidate_srcs);
31841 return sema.failWithOwnedErrorMsg(msg);
31842 },
31843 }
31844}
3187931845
31880 // They're both bad. Report error.
31881 // In the future we probably want to use the
31882 // coerceInMemoryAllowed error reporting mechanism,
31883 // however, for now we just fall through for the
31884 // "incompatible types" error below.
31885 }
31846fn resolvePeerTypesInner(
31847 sema: *Sema,
31848 block: *Block,
31849 src: LazySrcLoc,
31850 peer_tys: []?Type,
31851 peer_vals: []?Value,
31852) !PeerResolveResult {
31853 const mod = sema.mod;
3188631854
31887 // [*c]T and any other pointer size
31888 // Whichever element type can coerce to the other one, is
31889 // the one we will keep. If they're both OK then we keep the
31890 // C pointer since it matches both single and many pointers.
31891 if (cand_info.size == .C or chosen_info.size == .C) {
31892 const cand_ok = .ok == try sema.coerceInMemoryAllowed(block, cand_info.pointee_type, chosen_info.pointee_type, cand_info.mutable, target, src, src);
31893 const chosen_ok = .ok == try sema.coerceInMemoryAllowed(block, chosen_info.pointee_type, cand_info.pointee_type, chosen_info.mutable, target, src, src);
31894
31895 if (cand_ok) {
31896 if (chosen_ok) {
31897 if (chosen_info.size == .C) {
31898 continue;
31899 } else {
31900 chosen = candidate;
31901 chosen_i = candidate_i + 1;
31902 continue;
31903 }
31904 } else {
31905 chosen = candidate;
31906 chosen_i = candidate_i + 1;
31907 continue;
31908 }
31909 } else {
31910 if (chosen_ok) {
31911 continue;
31912 } else {
31913 // They're both bad. Report error.
31914 // In the future we probably want to use the
31915 // coerceInMemoryAllowed error reporting mechanism,
31916 // however, for now we just fall through for the
31917 // "incompatible types" error below.
31918 }
31919 }
31920 }
31855 var strat_reason: usize = 0;
31856 var s: PeerResolveStrategy = .unknown;
31857 for (peer_tys, 0..) |opt_ty, i| {
31858 const ty = opt_ty orelse continue;
31859 s = s.merge(PeerResolveStrategy.select(ty, mod), &strat_reason, i);
31860 }
31861
31862 if (s == .unknown) {
31863 // The whole thing was noreturn or undefined - try to do an exact match
31864 s = .exact;
31865 } else {
31866 // There was something other than noreturn and undefined, so we can ignore those peers
31867 for (peer_tys) |*ty_ptr| {
31868 const ty = ty_ptr.* orelse continue;
31869 switch (ty.zigTypeTag(mod)) {
31870 .NoReturn, .Undefined => ty_ptr.* = null,
31871 else => {},
31872 }
31873 }
31874 }
31875
31876 const target = mod.getTarget();
31877
31878 switch (s) {
31879 .unknown => unreachable,
31880
31881 .error_set => {
31882 var final_set: ?Type = null;
31883 for (peer_tys, 0..) |opt_ty, i| {
31884 const ty = opt_ty orelse continue;
31885 if (ty.zigTypeTag(mod) != .ErrorSet) return .{ .conflict = .{
31886 .peer_idx_a = strat_reason,
31887 .peer_idx_b = i,
31888 } };
31889 if (final_set) |cur_set| {
31890 final_set = try sema.maybeMergeErrorSets(block, src, cur_set, ty);
31891 } else {
31892 final_set = ty;
31893 }
31894 }
31895 return .{ .success = final_set.? };
31896 },
31897
31898 .error_union => {
31899 var final_set: ?Type = null;
31900 for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| {
31901 const ty = ty_ptr.* orelse continue;
31902 const set_ty = switch (ty.zigTypeTag(mod)) {
31903 .ErrorSet => blk: {
31904 ty_ptr.* = null; // no payload to decide on
31905 val_ptr.* = null;
31906 break :blk ty;
3192131907 },
31922 .Int, .ComptimeInt => {
31923 if (cand_info.size == .C) {
31924 chosen = candidate;
31925 chosen_i = candidate_i + 1;
31926 continue;
31927 }
31908 .ErrorUnion => blk: {
31909 const set_ty = ty.errorUnionSet(mod);
31910 ty_ptr.* = ty.errorUnionPayload(mod);
31911 if (val_ptr.*) |eu_val| switch (mod.intern_pool.indexToKey(eu_val.toIntern())) {
31912 .error_union => |eu| switch (eu.val) {
31913 .payload => |payload_ip| val_ptr.* = payload_ip.toValue(),
31914 .err_name => val_ptr.* = null,
31915 },
31916 .undef => val_ptr.* = (try sema.mod.intern(.{ .undef = ty_ptr.*.?.toIntern() })).toValue(),
31917 else => unreachable,
31918 };
31919 break :blk set_ty;
3192831920 },
31929 .Optional => {
31930 const chosen_ptr_ty = chosen_ty.optionalChild(mod);
31931 if (chosen_ptr_ty.zigTypeTag(mod) == .Pointer) {
31932 const chosen_info = chosen_ptr_ty.ptrInfo(mod);
31921 else => continue, // whole type is the payload
31922 };
31923 if (final_set) |cur_set| {
31924 final_set = try sema.maybeMergeErrorSets(block, src, cur_set, set_ty);
31925 } else {
31926 final_set = set_ty;
31927 }
31928 }
31929 assert(final_set != null);
31930 const final_payload = switch (try sema.resolvePeerTypesInner(
31931 block,
31932 src,
31933 peer_tys,
31934 peer_vals,
31935 )) {
31936 .success => |ty| ty,
31937 else => |result| return result,
31938 };
31939 return .{ .success = try mod.errorUnionType(final_set.?, final_payload) };
31940 },
3193331941
31934 seen_const = seen_const or !chosen_info.mutable or !cand_info.mutable;
31942 .nullable => {
31943 for (peer_tys, 0..) |opt_ty, i| {
31944 const ty = opt_ty orelse continue;
31945 if (!ty.eql(Type.null, mod)) return .{ .conflict = .{
31946 .peer_idx_a = strat_reason,
31947 .peer_idx_b = i,
31948 } };
31949 }
31950 return .{ .success = Type.null };
31951 },
3193531952
31936 // *[N]T to ?![*]T
31937 // *[N]T to ?![]T
31938 if (cand_info.size == .One and
31939 cand_info.pointee_type.zigTypeTag(mod) == .Array and
31940 (chosen_info.size == .Many or chosen_info.size == .Slice))
31941 {
31942 continue;
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 }
31953 .optional => {
31954 for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| {
31955 const ty = ty_ptr.* orelse continue;
31956 switch (ty.zigTypeTag(mod)) {
31957 .Null => {
31958 ty_ptr.* = null;
31959 val_ptr.* = null;
3196231960 },
31963 .Fn => {
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)) {
31965 chosen = candidate;
31966 chosen_i = candidate_i + 1;
31967 continue;
31968 }
31961 .Optional => {
31962 ty_ptr.* = ty.optionalChild(mod);
31963 if (val_ptr.*) |opt_val| val_ptr.* = if (!opt_val.isUndef(mod)) opt_val.optionalValue(mod) else null;
3196931964 },
3197031965 else => {},
3197131966 }
31972 },
31973 .Optional => {
31974 const opt_child_ty = candidate_ty.optionalChild(mod);
31975 if ((try sema.coerceInMemoryAllowed(block, chosen_ty, opt_child_ty, false, target, src, src)) == .ok) {
31976 seen_const = seen_const or opt_child_ty.isConstPtr(mod);
31977 any_are_null = true;
31978 continue;
31979 }
31967 }
31968 const child_ty = switch (try sema.resolvePeerTypesInner(
31969 block,
31970 src,
31971 peer_tys,
31972 peer_vals,
31973 )) {
31974 .success => |ty| ty,
31975 else => |result| return result,
31976 };
31977 return .{ .success = try mod.optionalType(child_ty.toIntern()) };
31978 },
3198031979
31981 seen_const = seen_const or chosen_ty.isConstPtr(mod);
31982 any_are_null = false;
31983 chosen = candidate;
31984 chosen_i = candidate_i + 1;
31985 continue;
31986 },
31987 .Vector => switch (chosen_ty_tag) {
31988 .Vector => {
31989 const chosen_len = chosen_ty.vectorLen(mod);
31990 const candidate_len = candidate_ty.vectorLen(mod);
31991 if (chosen_len != candidate_len)
31992 continue;
31980 .array => {
31981 // Index of the first non-null peer
31982 var opt_first_idx: ?usize = null;
31983 // Index of the first array or vector peer (i.e. not a tuple)
31984 var opt_first_arr_idx: ?usize = null;
31985 // Set to non-null once we see any peer, even a tuple
31986 var len: u64 = undefined;
31987 var sentinel: ?Value = undefined;
31988 // Only set once we see a non-tuple peer
31989 var elem_ty: Type = undefined;
31990
31991 for (peer_tys, 0..) |*ty_ptr, i| {
31992 const ty = ty_ptr.* orelse continue;
31993
31994 if (!ty.isArrayOrVector(mod)) {
31995 // We allow tuples of the correct length. We won't validate their elem type, since the elements can be coerced.
31996 const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{
31997 .peer_idx_a = strat_reason,
31998 .peer_idx_b = i,
31999 } };
32000
32001 if (opt_first_idx) |first_idx| {
32002 if (arr_like.len != len) return .{ .conflict = .{
32003 .peer_idx_a = first_idx,
32004 .peer_idx_b = i,
32005 } };
32006 } else {
32007 opt_first_idx = i;
32008 len = arr_like.len;
32009 }
32010
32011 sentinel = null;
32012
32013 continue;
32014 }
32015
32016 const first_arr_idx = opt_first_arr_idx orelse {
32017 if (opt_first_idx == null) {
32018 opt_first_idx = i;
32019 len = ty.arrayLen(mod);
32020 sentinel = ty.sentinel(mod);
32021 }
32022 opt_first_arr_idx = i;
32023 elem_ty = ty.childType(mod);
32024 continue;
32025 };
32026
32027 if (ty.arrayLen(mod) != len) return .{ .conflict = .{
32028 .peer_idx_a = first_arr_idx,
32029 .peer_idx_b = i,
32030 } };
32031
32032 if (!ty.childType(mod).eql(elem_ty, mod)) {
32033 return .{ .conflict = .{
32034 .peer_idx_a = first_arr_idx,
32035 .peer_idx_b = i,
32036 } };
32037 }
32038
32039 if (sentinel) |cur_sent| {
32040 if (ty.sentinel(mod)) |peer_sent| {
32041 if (!peer_sent.eql(cur_sent, elem_ty, mod)) sentinel = null;
32042 } else {
32043 sentinel = null;
32044 }
32045 }
32046 }
32047
32048 // There should always be at least one array or vector peer
32049 assert(opt_first_arr_idx != null);
32050
32051 return .{ .success = try mod.arrayType(.{
32052 .len = len,
32053 .child = elem_ty.toIntern(),
32054 .sentinel = if (sentinel) |sent_val| sent_val.toIntern() else .none,
32055 }) };
32056 },
32057
32058 .vector => {
32059 var len: ?u64 = null;
32060 var first_idx: usize = undefined;
32061 for (peer_tys, peer_vals, 0..) |*ty_ptr, *val_ptr, i| {
32062 const ty = ty_ptr.* orelse continue;
32063
32064 if (!ty.isArrayOrVector(mod)) {
32065 // Allow tuples of the correct length
32066 const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{
32067 .peer_idx_a = strat_reason,
32068 .peer_idx_b = i,
32069 } };
32070
32071 if (len) |expect_len| {
32072 if (arr_like.len != expect_len) return .{ .conflict = .{
32073 .peer_idx_a = first_idx,
32074 .peer_idx_b = i,
32075 } };
32076 } else {
32077 len = arr_like.len;
32078 first_idx = i;
32079 }
32080
32081 // Tuples won't participate in the child type resolution. We'll resolve without
32082 // them, and if the tuples have a bad type, we'll get a coercion error later.
32083 ty_ptr.* = null;
32084 val_ptr.* = null;
32085
32086 continue;
32087 }
32088
32089 if (len) |expect_len| {
32090 if (ty.arrayLen(mod) != expect_len) return .{ .conflict = .{
32091 .peer_idx_a = first_idx,
32092 .peer_idx_b = i,
32093 } };
32094 } else {
32095 len = ty.arrayLen(mod);
32096 first_idx = i;
32097 }
32098
32099 ty_ptr.* = ty.childType(mod);
32100 val_ptr.* = null; // multiple child vals, so we can't easily use them in PTR
32101 }
3199332102
31994 const chosen_child_ty = chosen_ty.childType(mod);
31995 const candidate_child_ty = candidate_ty.childType(mod);
31996 if (chosen_child_ty.zigTypeTag(mod) == .Int and candidate_child_ty.zigTypeTag(mod) == .Int) {
31997 const chosen_info = chosen_child_ty.intInfo(mod);
31998 const candidate_info = candidate_child_ty.intInfo(mod);
31999 if (chosen_info.bits < candidate_info.bits) {
32000 chosen = candidate;
32001 chosen_i = candidate_i + 1;
32103 const child_ty = switch (try sema.resolvePeerTypesInner(
32104 block,
32105 src,
32106 peer_tys,
32107 peer_vals,
32108 )) {
32109 .success => |ty| ty,
32110 else => |result| return result,
32111 };
32112
32113 return .{ .success = try mod.vectorType(.{
32114 .len = @intCast(u32, len.?),
32115 .child = child_ty.toIntern(),
32116 }) };
32117 },
32118
32119 .c_ptr => {
32120 var opt_ptr_info: ?Type.Payload.Pointer.Data = null;
32121 var first_idx: usize = undefined;
32122 for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| {
32123 const ty = opt_ty orelse continue;
32124 switch (ty.zigTypeTag(mod)) {
32125 .ComptimeInt => continue, // comptime-known integers can always coerce to C pointers
32126 .Int => {
32127 if (opt_val != null) {
32128 // Always allow the coercion for comptime-known ints
32129 continue;
32130 } else {
32131 // Runtime-known, so check if the type is no bigger than a usize
32132 const ptr_bits = target.ptrBitWidth();
32133 const bits = ty.intInfo(mod).bits;
32134 if (bits <= ptr_bits) continue;
3200232135 }
32003 continue;
32136 },
32137 .Null => continue,
32138 else => {},
32139 }
32140
32141 if (!ty.isPtrAtRuntime(mod)) return .{ .conflict = .{
32142 .peer_idx_a = strat_reason,
32143 .peer_idx_b = i,
32144 } };
32145
32146 // Goes through optionals
32147 const peer_info = ty.ptrInfo(mod);
32148
32149 var ptr_info = opt_ptr_info orelse {
32150 opt_ptr_info = peer_info;
32151 opt_ptr_info.?.size = .C;
32152 first_idx = i;
32153 continue;
32154 };
32155
32156 // Try peer -> cur, then cur -> peer
32157 ptr_info.pointee_type = (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.pointee_type, peer_info.pointee_type)) orelse {
32158 return .{ .conflict = .{
32159 .peer_idx_a = first_idx,
32160 .peer_idx_b = i,
32161 } };
32162 };
32163
32164 if (ptr_info.sentinel != null and peer_info.sentinel != null) {
32165 const peer_sent = try mod.getCoerced(ptr_info.sentinel.?, ptr_info.pointee_type);
32166 const ptr_sent = try mod.getCoerced(peer_info.sentinel.?, ptr_info.pointee_type);
32167 if (ptr_sent.eql(peer_sent, ptr_info.pointee_type, mod)) {
32168 ptr_info.sentinel = ptr_sent;
32169 } else {
32170 ptr_info.sentinel = null;
3200432171 }
32005 if (chosen_child_ty.zigTypeTag(mod) == .Float and candidate_child_ty.zigTypeTag(mod) == .Float) {
32006 if (chosen_ty.floatBits(target) < candidate_ty.floatBits(target)) {
32007 chosen = candidate;
32008 chosen_i = candidate_i + 1;
32172 } else {
32173 ptr_info.sentinel = null;
32174 }
32175
32176 // Note that the align can be always non-zero; Type.ptr will canonicalize it
32177 ptr_info.@"align" = @min(ptr_info.alignment(mod), peer_info.alignment(mod));
32178 if (ptr_info.@"addrspace" != peer_info.@"addrspace") {
32179 return .{ .conflict = .{
32180 .peer_idx_a = first_idx,
32181 .peer_idx_b = i,
32182 } };
32183 }
32184
32185 if (ptr_info.bit_offset != peer_info.bit_offset or
32186 ptr_info.host_size != peer_info.host_size)
32187 {
32188 return .{ .conflict = .{
32189 .peer_idx_a = first_idx,
32190 .peer_idx_b = i,
32191 } };
32192 }
32193
32194 ptr_info.mutable = ptr_info.mutable and peer_info.mutable;
32195 ptr_info.@"volatile" = ptr_info.@"volatile" or peer_info.@"volatile";
32196
32197 opt_ptr_info = ptr_info;
32198 }
32199 return .{ .success = try Type.ptr(sema.arena, mod, opt_ptr_info.?) };
32200 },
32201
32202 .ptr => {
32203 // If we've resolved to a `[]T` but then see a `[*]T`, we can resolve to a `[*]T` only
32204 // if there were no actual slices. Else, we want the slice index to report a conflict.
32205 var opt_slice_idx: ?usize = null;
32206
32207 var opt_ptr_info: ?Type.Payload.Pointer.Data = null;
32208 var first_idx: usize = undefined;
32209 var other_idx: usize = undefined; // We sometimes need a second peer index to report a generic error
32210
32211 for (peer_tys, 0..) |opt_ty, i| {
32212 const ty = opt_ty orelse continue;
32213 const peer_info: Type.Payload.Pointer.Data = switch (ty.zigTypeTag(mod)) {
32214 .Pointer => ty.ptrInfo(mod),
32215 .Fn => .{
32216 .pointee_type = ty,
32217 .@"addrspace" = target_util.defaultAddressSpace(target, .global_constant),
32218 },
32219 else => return .{ .conflict = .{
32220 .peer_idx_a = strat_reason,
32221 .peer_idx_b = i,
32222 } },
32223 };
32224
32225 switch (peer_info.size) {
32226 .One, .Many => {},
32227 .Slice => opt_slice_idx = i,
32228 .C => return .{ .conflict = .{
32229 .peer_idx_a = strat_reason,
32230 .peer_idx_b = i,
32231 } },
32232 }
32233
32234 var ptr_info = opt_ptr_info orelse {
32235 opt_ptr_info = peer_info;
32236 first_idx = i;
32237 continue;
32238 };
32239
32240 other_idx = i;
32241
32242 // We want to return this in a lot of cases, so alias it here for convenience
32243 const generic_err: PeerResolveResult = .{ .conflict = .{
32244 .peer_idx_a = first_idx,
32245 .peer_idx_b = i,
32246 } };
32247
32248 // Note that the align can be always non-zero; Type.ptr will canonicalize it
32249 ptr_info.@"align" = @min(ptr_info.alignment(mod), peer_info.alignment(mod));
32250
32251 if (ptr_info.@"addrspace" != peer_info.@"addrspace") {
32252 return generic_err;
32253 }
32254
32255 if (ptr_info.bit_offset != peer_info.bit_offset or
32256 ptr_info.host_size != peer_info.host_size)
32257 {
32258 return generic_err;
32259 }
32260
32261 ptr_info.mutable = ptr_info.mutable and peer_info.mutable;
32262 ptr_info.@"volatile" = ptr_info.@"volatile" or peer_info.@"volatile";
32263
32264 const peer_sentinel: ?Value = switch (peer_info.size) {
32265 .One => switch (peer_info.pointee_type.zigTypeTag(mod)) {
32266 .Array => peer_info.pointee_type.sentinel(mod),
32267 else => null,
32268 },
32269 .Many, .Slice => peer_info.sentinel,
32270 .C => unreachable,
32271 };
32272
32273 const cur_sentinel: ?Value = switch (ptr_info.size) {
32274 .One => switch (ptr_info.pointee_type.zigTypeTag(mod)) {
32275 .Array => ptr_info.pointee_type.sentinel(mod),
32276 else => null,
32277 },
32278 .Many, .Slice => ptr_info.sentinel,
32279 .C => unreachable,
32280 };
32281
32282 // We abstract array handling slightly so that tuple pointers can work like array pointers
32283 const peer_pointee_array = sema.typeIsArrayLike(peer_info.pointee_type);
32284 const cur_pointee_array = sema.typeIsArrayLike(ptr_info.pointee_type);
32285
32286 // This switch is just responsible for deciding the size and pointee (not including
32287 // single-pointer array sentinel).
32288 good: {
32289 switch (peer_info.size) {
32290 .One => switch (ptr_info.size) {
32291 .One => {
32292 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.pointee_type, peer_info.pointee_type)) |pointee| {
32293 ptr_info.pointee_type = pointee;
32294 break :good;
32295 }
32296
32297 const cur_arr = cur_pointee_array orelse return generic_err;
32298 const peer_arr = peer_pointee_array orelse return generic_err;
32299
32300 if (try sema.resolvePairInMemoryCoercible(block, src, cur_arr.elem_ty, peer_arr.elem_ty)) |elem_ty| {
32301 // *[n:x]T + *[n:y]T = *[n]T
32302 if (cur_arr.len == peer_arr.len) {
32303 ptr_info.pointee_type = try mod.arrayType(.{
32304 .len = cur_arr.len,
32305 .child = elem_ty.toIntern(),
32306 });
32307 break :good;
32308 }
32309 // *[a]T + *[b]T = []T
32310 ptr_info.size = .Slice;
32311 ptr_info.pointee_type = elem_ty;
32312 break :good;
32313 }
32314
32315 if (peer_arr.elem_ty.toIntern() == .noreturn_type) {
32316 // *struct{} + *[a]T = []T
32317 ptr_info.size = .Slice;
32318 ptr_info.pointee_type = cur_arr.elem_ty;
32319 break :good;
32320 }
32321
32322 if (cur_arr.elem_ty.toIntern() == .noreturn_type) {
32323 // *[a]T + *struct{} = []T
32324 ptr_info.size = .Slice;
32325 ptr_info.pointee_type = peer_arr.elem_ty;
32326 break :good;
32327 }
32328
32329 return generic_err;
32330 },
32331 .Many => {
32332 // Only works for *[n]T + [*]T -> [*]T
32333 const arr = peer_pointee_array orelse return generic_err;
32334 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.pointee_type, arr.elem_ty)) |pointee| {
32335 ptr_info.pointee_type = pointee;
32336 break :good;
32337 }
32338 if (arr.elem_ty.toIntern() == .noreturn_type) {
32339 // *struct{} + [*]T -> [*]T
32340 break :good;
32341 }
32342 return generic_err;
32343 },
32344 .Slice => {
32345 // Only works for *[n]T + []T -> []T
32346 const arr = peer_pointee_array orelse return generic_err;
32347 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.pointee_type, arr.elem_ty)) |pointee| {
32348 ptr_info.pointee_type = pointee;
32349 break :good;
32350 }
32351 if (arr.elem_ty.toIntern() == .noreturn_type) {
32352 // *struct{} + []T -> []T
32353 break :good;
32354 }
32355 return generic_err;
32356 },
32357 .C => unreachable,
32358 },
32359 .Many => switch (ptr_info.size) {
32360 .One => {
32361 // Only works for [*]T + *[n]T -> [*]T
32362 const arr = cur_pointee_array orelse return generic_err;
32363 if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, peer_info.pointee_type)) |pointee| {
32364 ptr_info.size = .Many;
32365 ptr_info.pointee_type = pointee;
32366 break :good;
32367 }
32368 if (arr.elem_ty.toIntern() == .noreturn_type) {
32369 // [*]T + *struct{} -> [*]T
32370 ptr_info.size = .Many;
32371 ptr_info.pointee_type = peer_info.pointee_type;
32372 break :good;
32373 }
32374 return generic_err;
32375 },
32376 .Many => {
32377 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.pointee_type, peer_info.pointee_type)) |pointee| {
32378 ptr_info.pointee_type = pointee;
32379 break :good;
32380 }
32381 return generic_err;
32382 },
32383 .Slice => {
32384 // Only works if no peers are actually slices
32385 if (opt_slice_idx) |slice_idx| {
32386 return .{ .conflict = .{
32387 .peer_idx_a = slice_idx,
32388 .peer_idx_b = i,
32389 } };
32390 }
32391 // Okay, then works for [*]T + "[]T" -> [*]T
32392 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.pointee_type, peer_info.pointee_type)) |pointee| {
32393 ptr_info.size = .Many;
32394 ptr_info.pointee_type = pointee;
32395 break :good;
32396 }
32397 return generic_err;
32398 },
32399 .C => unreachable,
32400 },
32401 .Slice => switch (ptr_info.size) {
32402 .One => {
32403 // Only works for []T + *[n]T -> []T
32404 const arr = cur_pointee_array orelse return generic_err;
32405 if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, peer_info.pointee_type)) |pointee| {
32406 ptr_info.size = .Slice;
32407 ptr_info.pointee_type = pointee;
32408 break :good;
32409 }
32410 if (arr.elem_ty.toIntern() == .noreturn_type) {
32411 // []T + *struct{} -> []T
32412 ptr_info.size = .Slice;
32413 ptr_info.pointee_type = peer_info.pointee_type;
32414 break :good;
32415 }
32416 return generic_err;
32417 },
32418 .Many => {
32419 // Impossible! (current peer is an actual slice)
32420 return generic_err;
32421 },
32422 .Slice => {
32423 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.pointee_type, peer_info.pointee_type)) |pointee| {
32424 ptr_info.pointee_type = pointee;
32425 break :good;
32426 }
32427 return generic_err;
32428 },
32429 .C => unreachable,
32430 },
32431 .C => unreachable,
32432 }
32433 }
32434
32435 const sentinel_ty = if (ptr_info.size == .One and ptr_info.pointee_type.zigTypeTag(mod) == .Array) blk: {
32436 break :blk ptr_info.pointee_type.childType(mod);
32437 } else ptr_info.pointee_type;
32438
32439 // TODO: once InternPool is in, we need to cast the sentinels to sentinel_ty
32440
32441 sentinel: {
32442 no_sentinel: {
32443 if (peer_sentinel == null) break :no_sentinel;
32444 if (cur_sentinel == null) break :no_sentinel;
32445 const peer_sent_coerced = try mod.getCoerced(peer_sentinel.?, sentinel_ty);
32446 const cur_sent_coerced = try mod.getCoerced(cur_sentinel.?, sentinel_ty);
32447 if (!peer_sent_coerced.eql(cur_sent_coerced, sentinel_ty, mod)) break :no_sentinel;
32448 // Sentinels match
32449 if (ptr_info.size == .One) {
32450 assert(ptr_info.pointee_type.zigTypeTag(mod) == .Array);
32451 ptr_info.pointee_type = try mod.arrayType(.{
32452 .len = ptr_info.pointee_type.arrayLen(mod),
32453 .child = ptr_info.pointee_type.childType(mod).toIntern(),
32454 .sentinel = cur_sent_coerced.toIntern(),
32455 });
32456 } else {
32457 ptr_info.sentinel = cur_sent_coerced;
3200932458 }
32010 continue;
32459 break :sentinel;
3201132460 }
32012 },
32013 .Array => {
32014 chosen = candidate;
32015 chosen_i = candidate_i + 1;
32461 // Clear existing sentinel
32462 ptr_info.sentinel = null;
32463 if (ptr_info.pointee_type.zigTypeTag(mod) == .Array) {
32464 ptr_info.pointee_type = try mod.arrayType(.{
32465 .len = ptr_info.pointee_type.arrayLen(mod),
32466 .child = ptr_info.pointee_type.childType(mod).toIntern(),
32467 .sentinel = .none,
32468 });
32469 }
32470 }
32471
32472 opt_ptr_info = ptr_info;
32473 }
32474
32475 // Before we succeed, check the pointee type. If we tried to apply PTR to (for instance)
32476 // &.{} and &.{}, we'll currently have a pointer type of `*[0]noreturn` - we wanted to
32477 // coerce the empty struct to a specific type, but no peer provided one. We need to
32478 // detect this case and emit an error.
32479 const pointee = opt_ptr_info.?.pointee_type;
32480 if (pointee.toIntern() == .noreturn_type or
32481 (pointee.zigTypeTag(mod) == .Array and pointee.childType(mod).toIntern() == .noreturn_type))
32482 {
32483 return .{ .conflict = .{
32484 .peer_idx_a = first_idx,
32485 .peer_idx_b = other_idx,
32486 } };
32487 }
32488
32489 return .{ .success = try Type.ptr(sema.arena, mod, opt_ptr_info.?) };
32490 },
32491
32492 .func => {
32493 var opt_cur_ty: ?Type = null;
32494 var first_idx: usize = undefined;
32495 for (peer_tys, 0..) |opt_ty, i| {
32496 const ty = opt_ty orelse continue;
32497 const cur_ty = opt_cur_ty orelse {
32498 opt_cur_ty = ty;
32499 first_idx = i;
3201632500 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)) {
32501 };
32502 if (ty.zigTypeTag(mod) != .Fn) return .{ .conflict = .{
32503 .peer_idx_a = strat_reason,
32504 .peer_idx_b = i,
32505 } };
32506 // ty -> cur_ty
32507 if (.ok == try sema.coerceInMemoryAllowedFns(block, cur_ty, ty, target, src, src)) {
3202632508 continue;
3202732509 }
32028 },
32029 else => {},
32030 }
32510 // cur_ty -> ty
32511 if (.ok == try sema.coerceInMemoryAllowedFns(block, ty, cur_ty, target, src, src)) {
32512 opt_cur_ty = ty;
32513 continue;
32514 }
32515 return .{ .conflict = .{
32516 .peer_idx_a = first_idx,
32517 .peer_idx_b = i,
32518 } };
32519 }
32520 return .{ .success = opt_cur_ty.? };
32521 },
3203132522
32032 switch (chosen_ty_tag) {
32033 .NoReturn, .Undefined => {
32034 chosen = candidate;
32035 chosen_i = candidate_i + 1;
32036 continue;
32037 },
32038 .Null => {
32039 any_are_null = true;
32040 chosen = candidate;
32041 chosen_i = candidate_i + 1;
32042 continue;
32043 },
32044 .Optional => {
32045 const opt_child_ty = chosen_ty.optionalChild(mod);
32046 if ((try sema.coerceInMemoryAllowed(block, opt_child_ty, candidate_ty, false, target, src, src)) == .ok) {
32523 .enum_or_union => {
32524 var opt_cur_ty: ?Type = null;
32525 // The peer index which gave the current type
32526 var cur_ty_idx: usize = undefined;
32527
32528 for (peer_tys, 0..) |opt_ty, i| {
32529 const ty = opt_ty orelse continue;
32530 switch (ty.zigTypeTag(mod)) {
32531 .EnumLiteral, .Enum, .Union => {},
32532 else => return .{ .conflict = .{
32533 .peer_idx_a = strat_reason,
32534 .peer_idx_b = i,
32535 } },
32536 }
32537 const cur_ty = opt_cur_ty orelse {
32538 opt_cur_ty = ty;
32539 cur_ty_idx = i;
3204732540 continue;
32541 };
32542
32543 // We want to return this in a lot of cases, so alias it here for convenience
32544 const generic_err: PeerResolveResult = .{ .conflict = .{
32545 .peer_idx_a = cur_ty_idx,
32546 .peer_idx_b = i,
32547 } };
32548
32549 switch (cur_ty.zigTypeTag(mod)) {
32550 .EnumLiteral => {
32551 opt_cur_ty = ty;
32552 cur_ty_idx = i;
32553 },
32554 .Enum => switch (ty.zigTypeTag(mod)) {
32555 .EnumLiteral => {},
32556 .Enum => {
32557 if (!ty.eql(cur_ty, mod)) return generic_err;
32558 },
32559 .Union => {
32560 const tag_ty = ty.unionTagTypeHypothetical(mod);
32561 if (!tag_ty.eql(cur_ty, mod)) return generic_err;
32562 opt_cur_ty = ty;
32563 cur_ty_idx = i;
32564 },
32565 else => unreachable,
32566 },
32567 .Union => switch (ty.zigTypeTag(mod)) {
32568 .EnumLiteral => {},
32569 .Enum => {
32570 const cur_tag_ty = cur_ty.unionTagTypeHypothetical(mod);
32571 if (!ty.eql(cur_tag_ty, mod)) return generic_err;
32572 },
32573 .Union => {
32574 if (!ty.eql(cur_ty, mod)) return generic_err;
32575 },
32576 else => unreachable,
32577 },
32578 else => unreachable,
32579 }
32580 }
32581 return .{ .success = opt_cur_ty.? };
32582 },
32583
32584 .comptime_int => {
32585 for (peer_tys, 0..) |opt_ty, i| {
32586 const ty = opt_ty orelse continue;
32587 switch (ty.zigTypeTag(mod)) {
32588 .ComptimeInt => {},
32589 else => return .{ .conflict = .{
32590 .peer_idx_a = strat_reason,
32591 .peer_idx_b = i,
32592 } },
32593 }
32594 }
32595 return .{ .success = Type.comptime_int };
32596 },
32597
32598 .comptime_float => {
32599 for (peer_tys, 0..) |opt_ty, i| {
32600 const ty = opt_ty orelse continue;
32601 switch (ty.zigTypeTag(mod)) {
32602 .ComptimeInt, .ComptimeFloat => {},
32603 else => return .{ .conflict = .{
32604 .peer_idx_a = strat_reason,
32605 .peer_idx_b = i,
32606 } },
32607 }
32608 }
32609 return .{ .success = Type.comptime_float };
32610 },
32611
32612 .fixed_int => {
32613 var idx_unsigned: ?usize = null;
32614 var idx_signed: ?usize = null;
32615
32616 // TODO: this is for compatibility with legacy behavior. See beneath the loop.
32617 var any_comptime_known = false;
32618
32619 for (peer_tys, peer_vals, 0..) |opt_ty, *ptr_opt_val, i| {
32620 const ty = opt_ty orelse continue;
32621 const opt_val = ptr_opt_val.*;
32622
32623 const peer_tag = ty.zigTypeTag(mod);
32624 switch (peer_tag) {
32625 .ComptimeInt => {
32626 // If the value is undefined, we can't refine to a fixed-width int
32627 if (opt_val == null or opt_val.?.isUndef(mod)) return .{ .conflict = .{
32628 .peer_idx_a = strat_reason,
32629 .peer_idx_b = i,
32630 } };
32631 any_comptime_known = true;
32632 ptr_opt_val.* = try sema.resolveLazyValue(opt_val.?);
32633 continue;
32634 },
32635 .Int => {},
32636 else => return .{ .conflict = .{
32637 .peer_idx_a = strat_reason,
32638 .peer_idx_b = i,
32639 } },
3204832640 }
32049 if ((try sema.coerceInMemoryAllowed(block, candidate_ty, opt_child_ty, false, target, src, src)) == .ok) {
32050 any_are_null = true;
32051 chosen = candidate;
32052 chosen_i = candidate_i + 1;
32641
32642 if (opt_val != null) any_comptime_known = true;
32643
32644 const info = ty.intInfo(mod);
32645
32646 const idx_ptr = switch (info.signedness) {
32647 .unsigned => &idx_unsigned,
32648 .signed => &idx_signed,
32649 };
32650
32651 const largest_idx = idx_ptr.* orelse {
32652 idx_ptr.* = i;
3205332653 continue;
32654 };
32655
32656 const cur_info = peer_tys[largest_idx].?.intInfo(mod);
32657 if (info.bits > cur_info.bits) {
32658 idx_ptr.* = i;
3205432659 }
32055 },
32056 .ErrorUnion => {
32057 const payload_ty = chosen_ty.errorUnionPayload(mod);
32058 if ((try sema.coerceInMemoryAllowed(block, payload_ty, candidate_ty, false, target, src, src)) == .ok) {
32660 }
32661
32662 if (idx_signed == null) {
32663 return .{ .success = peer_tys[idx_unsigned.?].? };
32664 }
32665
32666 if (idx_unsigned == null) {
32667 return .{ .success = peer_tys[idx_signed.?].? };
32668 }
32669
32670 const unsigned_info = peer_tys[idx_unsigned.?].?.intInfo(mod);
32671 const signed_info = peer_tys[idx_signed.?].?.intInfo(mod);
32672 if (signed_info.bits > unsigned_info.bits) {
32673 return .{ .success = peer_tys[idx_signed.?].? };
32674 }
32675
32676 // TODO: this is for compatibility with legacy behavior. Before this version of PTR was
32677 // implemented, the algorithm very often returned false positives, with the expectation
32678 // that you'd just hit a coercion error later. One of these was that for integers, the
32679 // largest type would always be returned, even if it couldn't fit everything. This had
32680 // an unintentional consequence to semantics, which is that if values were known at
32681 // comptime, they would be coerced down to the smallest type where possible. This
32682 // behavior is unintuitive and order-dependent, so in my opinion should be eliminated,
32683 // but for now we'll retain compatibility.
32684 if (any_comptime_known) {
32685 if (unsigned_info.bits > signed_info.bits) {
32686 return .{ .success = peer_tys[idx_unsigned.?].? };
32687 }
32688 const idx = @min(idx_unsigned.?, idx_signed.?);
32689 return .{ .success = peer_tys[idx].? };
32690 }
32691
32692 return .{ .conflict = .{
32693 .peer_idx_a = idx_unsigned.?,
32694 .peer_idx_b = idx_signed.?,
32695 } };
32696 },
32697
32698 .fixed_float => {
32699 var opt_cur_ty: ?Type = null;
32700
32701 for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| {
32702 const ty = opt_ty orelse continue;
32703 switch (ty.zigTypeTag(mod)) {
32704 .ComptimeFloat, .ComptimeInt => {},
32705 .Int => {
32706 if (opt_val == null) return .{ .conflict = .{
32707 .peer_idx_a = strat_reason,
32708 .peer_idx_b = i,
32709 } };
32710 },
32711 .Float => {
32712 if (opt_cur_ty) |cur_ty| {
32713 if (cur_ty.eql(ty, mod)) continue;
32714 // Recreate the type so we eliminate any c_longdouble
32715 const bits = @max(cur_ty.floatBits(target), ty.floatBits(target));
32716 opt_cur_ty = switch (bits) {
32717 16 => Type.f16,
32718 32 => Type.f32,
32719 64 => Type.f64,
32720 80 => Type.f80,
32721 128 => Type.f128,
32722 else => unreachable,
32723 };
32724 } else {
32725 opt_cur_ty = ty;
32726 }
32727 },
32728 else => return .{ .conflict = .{
32729 .peer_idx_a = strat_reason,
32730 .peer_idx_b = i,
32731 } },
32732 }
32733 }
32734
32735 // Note that fixed_float is only chosen if there is at least one fixed-width float peer,
32736 // so opt_cur_ty must be non-null.
32737 return .{ .success = opt_cur_ty.? };
32738 },
32739
32740 .coercible_struct => {
32741 // First, check that every peer has the same approximate structure (field count and names)
32742
32743 var opt_first_idx: ?usize = null;
32744 var is_tuple: bool = undefined;
32745 var field_count: usize = undefined;
32746 // Only defined for non-tuples.
32747 var field_names: []InternPool.NullTerminatedString = undefined;
32748
32749 for (peer_tys, 0..) |opt_ty, i| {
32750 const ty = opt_ty orelse continue;
32751
32752 if (!ty.isTupleOrAnonStruct(mod)) {
32753 return .{ .conflict = .{
32754 .peer_idx_a = strat_reason,
32755 .peer_idx_b = i,
32756 } };
32757 }
32758
32759 const first_idx = opt_first_idx orelse {
32760 opt_first_idx = i;
32761 is_tuple = ty.isTuple(mod);
32762 field_count = ty.structFieldCount(mod);
32763 if (!is_tuple) {
32764 const names = mod.intern_pool.indexToKey(ty.toIntern()).anon_struct_type.names;
32765 field_names = try sema.arena.dupe(InternPool.NullTerminatedString, names);
32766 }
3205932767 continue;
32768 };
32769
32770 if (ty.isTuple(mod) != is_tuple or ty.structFieldCount(mod) != field_count) {
32771 return .{ .conflict = .{
32772 .peer_idx_a = first_idx,
32773 .peer_idx_b = i,
32774 } };
3206032775 }
32061 },
32062 .ErrorSet => {
32063 chosen = candidate;
32064 chosen_i = candidate_i + 1;
32065 if (err_set_ty) |chosen_set_ty| {
32066 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, chosen_ty, src, src)) {
32067 continue;
32776
32777 if (!is_tuple) {
32778 for (field_names, 0..) |expected, field_idx| {
32779 const actual = ty.structFieldName(field_idx, mod);
32780 if (actual == expected) continue;
32781 return .{ .conflict = .{
32782 .peer_idx_a = first_idx,
32783 .peer_idx_b = i,
32784 } };
3206832785 }
32069 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_ty, chosen_set_ty, src, src)) {
32070 err_set_ty = chosen_ty;
32786 }
32787 }
32788
32789 assert(opt_first_idx != null);
32790
32791 // Now, we'll recursively resolve the field types
32792 const field_types = try sema.arena.alloc(InternPool.Index, field_count);
32793 // Values for `comptime` fields - `.none` used for non-comptime fields
32794 const field_vals = try sema.arena.alloc(InternPool.Index, field_count);
32795 const sub_peer_tys = try sema.arena.alloc(?Type, peer_tys.len);
32796 const sub_peer_vals = try sema.arena.alloc(?Value, peer_vals.len);
32797
32798 for (field_types, field_vals, 0..) |*field_ty, *field_val, field_idx| {
32799 // Fill buffers with types and values of the field
32800 for (peer_tys, peer_vals, sub_peer_tys, sub_peer_vals) |opt_ty, opt_val, *peer_field_ty, *peer_field_val| {
32801 const ty = opt_ty orelse {
32802 peer_field_ty.* = null;
32803 peer_field_val.* = null;
3207132804 continue;
32072 }
32805 };
32806 peer_field_ty.* = ty.structFieldType(field_idx, mod);
32807 peer_field_val.* = if (opt_val) |val| try val.fieldValue(mod, field_idx) else null;
32808 }
32809
32810 // Resolve field type recursively
32811 field_ty.* = switch (try sema.resolvePeerTypesInner(block, src, sub_peer_tys, sub_peer_vals)) {
32812 .success => |ty| ty.toIntern(),
32813 else => |result| {
32814 const result_buf = try sema.arena.create(PeerResolveResult);
32815 result_buf.* = result;
32816 const field_name = if (is_tuple) name: {
32817 break :name try std.fmt.allocPrint(sema.arena, "{d}", .{field_idx});
32818 } else try sema.arena.dupe(u8, mod.intern_pool.stringToSlice(field_names[field_idx]));
32819
32820 // The error info needs the field types, but we can't reuse sub_peer_tys
32821 // since the recursive call may have clobbered it.
32822 const peer_field_tys = try sema.arena.alloc(Type, peer_tys.len);
32823 for (peer_tys, peer_field_tys) |opt_ty, *peer_field_ty| {
32824 // Already-resolved types won't be referenced by the error so it's fine
32825 // to leave them undefined.
32826 const ty = opt_ty orelse continue;
32827 peer_field_ty.* = ty.structFieldType(field_idx, mod);
32828 }
3207332829
32074 err_set_ty = try sema.errorSetMerge(chosen_set_ty, chosen_ty);
32075 continue;
32076 } else {
32077 err_set_ty = chosen_ty;
32078 continue;
32830 return .{ .field_error = .{
32831 .field_name = field_name,
32832 .field_types = peer_field_tys,
32833 .sub_result = result_buf,
32834 } };
32835 },
32836 };
32837
32838 // Decide if this is a comptime field. If it is comptime in all peers, and the
32839 // coerced comptime values are all the same, we say it is comptime, else not.
32840
32841 var comptime_val: ?Value = null;
32842 for (peer_tys) |opt_ty| {
32843 const struct_ty = opt_ty orelse continue;
32844 const uncoerced_field_val = try struct_ty.structFieldValueComptime(mod, field_idx) orelse {
32845 comptime_val = null;
32846 break;
32847 };
32848 const uncoerced_field_ty = struct_ty.structFieldType(field_idx, mod);
32849 const uncoerced_field = try sema.addConstant(uncoerced_field_ty, uncoerced_field_val);
32850 const coerced_inst = sema.coerceExtra(block, field_ty.toType(), uncoerced_field, src, .{ .report_err = false }) catch |err| switch (err) {
32851 // 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
32852 error.NotCoercible => {
32853 comptime_val = null;
32854 break;
32855 },
32856 else => |e| return e,
32857 };
32858 const coerced_val = (try sema.resolveMaybeUndefVal(coerced_inst)) orelse continue;
32859 const existing = comptime_val orelse {
32860 comptime_val = coerced_val;
32861 continue;
32862 };
32863 if (!coerced_val.eql(existing, field_ty.toType(), mod)) {
32864 comptime_val = null;
32865 break;
32866 }
3207932867 }
32080 },
32081 else => {},
32082 }
3208332868
32084 // At this point, we hit a compile error. We need to recover
32085 // the source locations.
32086 const chosen_src = candidate_srcs.resolve(
32087 mod,
32088 mod.declPtr(block.src_decl),
32089 chosen_i,
32090 );
32091 const candidate_src = candidate_srcs.resolve(
32092 mod,
32093 mod.declPtr(block.src_decl),
32094 candidate_i + 1,
32095 );
32869 field_val.* = if (comptime_val) |v| v.toIntern() else .none;
32870 }
3209632871
32097 const msg = msg: {
32098 const msg = try sema.errMsg(block, src, "incompatible types: '{}' and '{}'", .{
32099 chosen_ty.fmt(mod),
32100 candidate_ty.fmt(mod),
32101 });
32102 errdefer msg.destroy(sema.gpa);
32872 const final_ty = try mod.intern(.{ .anon_struct_type = .{
32873 .types = field_types,
32874 .names = if (is_tuple) &.{} else field_names,
32875 .values = field_vals,
32876 } });
3210332877
32104 if (chosen_src) |src_loc|
32105 try sema.errNote(block, src_loc, msg, "type '{}' here", .{chosen_ty.fmt(mod)});
32878 return .{ .success = final_ty.toType() };
32879 },
3210632880
32107 if (candidate_src) |src_loc|
32108 try sema.errNote(block, src_loc, msg, "type '{}' here", .{candidate_ty.fmt(mod)});
32881 .exact => {
32882 var expect_ty: ?Type = null;
32883 var first_idx: usize = undefined;
32884 for (peer_tys, 0..) |opt_ty, i| {
32885 const ty = opt_ty orelse continue;
32886 if (expect_ty) |expect| {
32887 if (!ty.eql(expect, mod)) return .{ .conflict = .{
32888 .peer_idx_a = first_idx,
32889 .peer_idx_b = i,
32890 } };
32891 } else {
32892 expect_ty = ty;
32893 first_idx = i;
32894 }
32895 }
32896 return .{ .success = expect_ty.? };
32897 },
32898 }
32899}
3210932900
32110 break :msg msg;
32111 };
32112 return sema.failWithOwnedErrorMsg(msg);
32901fn maybeMergeErrorSets(sema: *Sema, block: *Block, src: LazySrcLoc, e0: Type, e1: Type) !Type {
32902 // e0 -> e1
32903 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e1, e0, src, src)) {
32904 return e1;
3211332905 }
3211432906
32115 const chosen_ty = sema.typeOf(chosen);
32907 // e1 -> e0
32908 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e0, e1, src, src)) {
32909 return e0;
32910 }
3211632911
32117 if (convert_to_slice) {
32118 // turn *[N]T => []T
32119 const chosen_child_ty = chosen_ty.childType(mod);
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);
32912 return sema.errorSetMerge(e0, e1);
32913}
3212532914
32126 const new_ptr_ty = try Type.ptr(sema.arena, mod, info);
32127 const opt_ptr_ty = if (any_are_null)
32128 try Type.optional(sema.arena, new_ptr_ty, mod)
32129 else
32130 new_ptr_ty;
32131 const set_ty = err_set_ty orelse return opt_ptr_ty;
32132 return try mod.errorUnionType(set_ty, opt_ptr_ty);
32915fn resolvePairInMemoryCoercible(sema: *Sema, block: *Block, src: LazySrcLoc, ty_a: Type, ty_b: Type) !?Type {
32916 // ty_b -> ty_a
32917 if (.ok == try sema.coerceInMemoryAllowed(block, ty_a, ty_b, true, sema.mod.getTarget(), src, src)) {
32918 return ty_a;
3213332919 }
3213432920
32135 if (seen_const) {
32136 // turn []T => []const T
32137 switch (chosen_ty.zigTypeTag(mod)) {
32138 .ErrorUnion => {
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 }
32921 // ty_a -> ty_b
32922 if (.ok == try sema.coerceInMemoryAllowed(block, ty_b, ty_a, true, sema.mod.getTarget(), src, src)) {
32923 return ty_b;
3216332924 }
3216432925
32165 if (any_are_null) {
32166 const opt_ty = switch (chosen_ty.zigTypeTag(mod)) {
32167 .Null, .Optional => chosen_ty,
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 }
32926 return null;
32927}
3217332928
32174 if (err_set_ty) |ty| switch (chosen_ty.zigTypeTag(mod)) {
32175 .ErrorSet => return ty,
32176 .ErrorUnion => {
32177 const payload_ty = chosen_ty.errorUnionPayload(mod);
32178 return try mod.errorUnionType(ty, payload_ty);
32929const ArrayLike = struct {
32930 len: u64,
32931 /// `noreturn` indicates that this type is `struct{}` so can coerce to anything
32932 elem_ty: Type,
32933};
32934fn typeIsArrayLike(sema: *Sema, ty: Type) ?ArrayLike {
32935 const mod = sema.mod;
32936 return switch (ty.zigTypeTag(mod)) {
32937 .Array => .{
32938 .len = ty.arrayLen(mod),
32939 .elem_ty = ty.childType(mod),
32940 },
32941 .Struct => {
32942 const field_count = ty.structFieldCount(mod);
32943 if (field_count == 0) return .{
32944 .len = 0,
32945 .elem_ty = Type.noreturn,
32946 };
32947 if (!ty.isTuple(mod)) return null;
32948 const elem_ty = ty.structFieldType(0, mod);
32949 for (1..field_count) |i| {
32950 if (!ty.structFieldType(i, mod).eql(elem_ty, mod)) {
32951 return null;
32952 }
32953 }
32954 return .{
32955 .len = field_count,
32956 .elem_ty = elem_ty,
32957 };
3217932958 },
32180 else => return try mod.errorUnionType(ty, chosen_ty),
32959 else => null,
3218132960 };
32182
32183 return chosen_ty;
3218432961}
3218532962
3218632963pub fn resolveFnTypes(sema: *Sema, fn_ty: Type) CompileError!void {
......@@ -34596,7 +35373,7 @@ fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value
3459635373 // Move mutable decl values to the InternPool and assert other decls are already in
3459735374 // the InternPool.
3459835375 const uncoerced_val = if (deref.is_mutable) try tv.val.intern(tv.ty, mod) else tv.val.toIntern();
34599 const coerced_val = try sema.coerceValueInMemory(block, uncoerced_val.toValue(), tv.ty, load_ty, src);
35376 const coerced_val = try mod.getCoerced(uncoerced_val.toValue(), load_ty);
3460035377 return .{ .val = coerced_val };
3460135378 }
3460235379 }
test/behavior/cast.zig+610
......@@ -1,6 +1,9 @@
11const builtin = @import("builtin");
22const std = @import("std");
3const assert = std.debug.assert;
34const expect = std.testing.expect;
5const expectEqual = std.testing.expectEqual;
6const expectEqualSlices = std.testing.expectEqualSlices;
47const mem = std.mem;
58const maxInt = std.math.maxInt;
69const native_endian = builtin.target.cpu.arch.endian();
......@@ -1609,3 +1612,610 @@ test "coercion from single-item pointer to @as to slice" {
16091612
16101613 try expect(t[0] == 1);
16111614}
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