| ... | ... | @@ -23069,7 +23069,7 @@ fn analyzeMinMax( |
| 23069 | 23069 | if (std.debug.runtime_safety) { |
| 23070 | 23070 | assert(try sema.intFitsInType(val, refined_ty, null)); |
| 23071 | 23071 | } |
| 23072 | | cur_minmax = try sema.coerceInMemory(block, val, orig_ty, refined_ty, src); |
| 23072 | cur_minmax = try sema.coerceInMemory(val, refined_ty); |
| 23073 | 23073 | } |
| 23074 | 23074 | |
| 23075 | 23075 | break :refined refined_ty; |
| ... | ... | @@ -26610,7 +26610,7 @@ fn coerceExtra( |
| 26610 | 26610 | var in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src); |
| 26611 | 26611 | if (in_memory_result == .ok) { |
| 26612 | 26612 | 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); |
| 26614 | 26614 | } |
| 26615 | 26615 | try sema.requireRuntimeBlock(block, inst_src, null); |
| 26616 | 26616 | return block.addBitCast(dest_ty, inst); |
| ... | ... | @@ -27278,82 +27278,12 @@ fn coerceExtra( |
| 27278 | 27278 | return sema.failWithOwnedErrorMsg(msg); |
| 27279 | 27279 | } |
| 27280 | 27280 | |
| 27281 | | fn 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 | | |
| 27348 | 27281 | fn coerceInMemory( |
| 27349 | 27282 | sema: *Sema, |
| 27350 | | block: *Block, |
| 27351 | 27283 | val: Value, |
| 27352 | | src_ty: Type, |
| 27353 | 27284 | dst_ty: Type, |
| 27354 | | dst_ty_src: LazySrcLoc, |
| 27355 | 27285 | ) 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)); |
| 27357 | 27287 | } |
| 27358 | 27288 | |
| 27359 | 27289 | const InMemoryCoercionResult = union(enum) { |
| ... | ... | @@ -27891,6 +27821,22 @@ fn coerceInMemoryAllowed( |
| 27891 | 27821 | return .ok; |
| 27892 | 27822 | } |
| 27893 | 27823 | |
| 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 | |
| 27894 | 27840 | return InMemoryCoercionResult{ .no_match = .{ |
| 27895 | 27841 | .actual = dest_ty, |
| 27896 | 27842 | .wanted = src_ty, |
| ... | ... | @@ -27959,9 +27905,8 @@ fn coerceInMemoryAllowedErrorSets( |
| 27959 | 27905 | |
| 27960 | 27906 | switch (src_ty.toIntern()) { |
| 27961 | 27907 | .anyerror_type => switch (ip.indexToKey(dest_ty.toIntern())) { |
| 27962 | | .inferred_error_set_type => unreachable, // Caught by dest_ty.isAnyError(mod) above. |
| 27963 | 27908 | .simple_type => unreachable, // filtered out above |
| 27964 | | .error_set_type => return .from_anyerror, |
| 27909 | .error_set_type, .inferred_error_set_type => return .from_anyerror, |
| 27965 | 27910 | else => unreachable, |
| 27966 | 27911 | }, |
| 27967 | 27912 | |
| ... | ... | @@ -28008,8 +27953,6 @@ fn coerceInMemoryAllowedErrorSets( |
| 28008 | 27953 | else => unreachable, |
| 28009 | 27954 | }, |
| 28010 | 27955 | } |
| 28011 | | |
| 28012 | | unreachable; |
| 28013 | 27956 | } |
| 28014 | 27957 | |
| 28015 | 27958 | fn coerceInMemoryAllowedFns( |
| ... | ... | @@ -29800,7 +29743,7 @@ fn coerceArrayLike( |
| 29800 | 29743 | if (in_memory_result == .ok) { |
| 29801 | 29744 | if (try sema.resolveMaybeUndefVal(inst)) |inst_val| { |
| 29802 | 29745 | // 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); |
| 29804 | 29747 | } |
| 29805 | 29748 | try sema.requireRuntimeBlock(block, inst_src, null); |
| 29806 | 29749 | return block.addBitCast(dest_ty, inst); |
| ... | ... | @@ -31586,601 +31529,1435 @@ fn unionToTag( |
| 31586 | 31529 | return block.addTyOp(.get_union_tag, enum_ty, un); |
| 31587 | 31530 | } |
| 31588 | 31531 | |
| 31589 | | fn 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, |
| 31532 | const 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 | }; |
| 31635 | 31597 | |
| 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 }, |
| 31639 | 31603 | }, |
| 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 }, |
| 31659 | 31607 | }, |
| 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 }, |
| 31664 | 31612 | }, |
| 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 }, |
| 31679 | 31617 | }, |
| 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 }, |
| 31688 | 31622 | }, |
| 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 }, |
| 31697 | 31626 | }, |
| 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 }, |
| 31701 | 31630 | }, |
| 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 }, |
| 31709 | 31634 | }, |
| 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 | }; |
| 31716 | 31665 | |
| 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 | } |
| 31726 | 31682 | |
| 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 | } |
| 31732 | 31685 | |
| 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 | }; |
| 31740 | 31707 | |
| 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 | | } |
| 31708 | const 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 | }, |
| 31753 | 31725 | |
| 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); |
| 31766 | 31739 | |
| 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); |
| 31771 | 31764 | } else { |
| 31772 | | err_set_ty = try sema.errorSetMerge(chosen_set_ty, candidate_set_ty); |
| 31765 | opt_msg = try sema.errMsg(block, src, fmt, args); |
| 31773 | 31766 | } |
| 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; |
| 31776 | 31771 | continue; |
| 31777 | 31772 | }, |
| 31773 | } |
| 31778 | 31774 | |
| 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. |
| 31782 | 31776 | |
| 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 | } |
| 31785 | 31781 | |
| 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 | }; |
| 31794 | 31790 | |
| 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 | }; |
| 31797 | 31804 | |
| 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)}); |
| 31808 | 31807 | |
| 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 | } |
| 31831 | 31811 | |
| 31832 | | seen_const = seen_const or !chosen_info.mutable or !cand_info.mutable; |
| 31812 | return opt_msg.?; |
| 31813 | } |
| 31814 | }; |
| 31833 | 31815 | |
| 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 | | } |
| 31816 | fn 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 | } |
| 31854 | 31828 | |
| 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); |
| 31865 | 31831 | |
| 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 | } |
| 31871 | 31836 | |
| 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 | } |
| 31879 | 31845 | |
| 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 | | } |
| 31846 | fn 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; |
| 31886 | 31854 | |
| 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; |
| 31921 | 31907 | }, |
| 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; |
| 31928 | 31920 | }, |
| 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 | }, |
| 31933 | 31941 | |
| 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 | }, |
| 31935 | 31952 | |
| 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; |
| 31962 | 31960 | }, |
| 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; |
| 31969 | 31964 | }, |
| 31970 | 31965 | else => {}, |
| 31971 | 31966 | } |
| 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 | }, |
| 31980 | 31979 | |
| 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 | } |
| 31993 | 32102 | |
| 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; |
| 32002 | 32135 | } |
| 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; |
| 32004 | 32171 | } |
| 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; |
| 32009 | 32458 | } |
| 32010 | | continue; |
| 32459 | break :sentinel; |
| 32011 | 32460 | } |
| 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; |
| 32016 | 32500 | 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)) { |
| 32026 | 32508 | continue; |
| 32027 | 32509 | } |
| 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 | }, |
| 32031 | 32522 | |
| 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; |
| 32047 | 32540 | 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 | } }, |
| 32048 | 32640 | } |
| 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; |
| 32053 | 32653 | continue; |
| 32654 | }; |
| 32655 | |
| 32656 | const cur_info = peer_tys[largest_idx].?.intInfo(mod); |
| 32657 | if (info.bits > cur_info.bits) { |
| 32658 | idx_ptr.* = i; |
| 32054 | 32659 | } |
| 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 | } |
| 32059 | 32767 | 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 | } }; |
| 32060 | 32775 | } |
| 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 | } }; |
| 32068 | 32785 | } |
| 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; |
| 32071 | 32804 | 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 | } |
| 32073 | 32829 | |
| 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 | } |
| 32079 | 32867 | } |
| 32080 | | }, |
| 32081 | | else => {}, |
| 32082 | | } |
| 32083 | 32868 | |
| 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 | } |
| 32096 | 32871 | |
| 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 | } }); |
| 32103 | 32877 | |
| 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 | }, |
| 32106 | 32880 | |
| 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 | } |
| 32109 | 32900 | |
| 32110 | | break :msg msg; |
| 32111 | | }; |
| 32112 | | return sema.failWithOwnedErrorMsg(msg); |
| 32901 | fn 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; |
| 32113 | 32905 | } |
| 32114 | 32906 | |
| 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 | } |
| 32116 | 32911 | |
| 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 | } |
| 32125 | 32914 | |
| 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); |
| 32915 | fn 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; |
| 32133 | 32919 | } |
| 32134 | 32920 | |
| 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; |
| 32163 | 32924 | } |
| 32164 | 32925 | |
| 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 | } |
| 32173 | 32928 | |
| 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); |
| 32929 | const ArrayLike = struct { |
| 32930 | len: u64, |
| 32931 | /// `noreturn` indicates that this type is `struct{}` so can coerce to anything |
| 32932 | elem_ty: Type, |
| 32933 | }; |
| 32934 | fn 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 | }; |
| 32179 | 32958 | }, |
| 32180 | | else => return try mod.errorUnionType(ty, chosen_ty), |
| 32959 | else => null, |
| 32181 | 32960 | }; |
| 32182 | | |
| 32183 | | return chosen_ty; |
| 32184 | 32961 | } |
| 32185 | 32962 | |
| 32186 | 32963 | pub fn resolveFnTypes(sema: *Sema, fn_ty: Type) CompileError!void { |
| ... | ... | @@ -34596,7 +35373,7 @@ fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value |
| 34596 | 35373 | // Move mutable decl values to the InternPool and assert other decls are already in |
| 34597 | 35374 | // the InternPool. |
| 34598 | 35375 | 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); |
| 34600 | 35377 | return .{ .val = coerced_val }; |
| 34601 | 35378 | } |
| 34602 | 35379 | } |