| ... | @@ -23069,7 +23069,7 @@ fn analyzeMinMax( | ... | @@ -23069,7 +23069,7 @@ fn analyzeMinMax( |
| 23069 | if (std.debug.runtime_safety) { | 23069 | if (std.debug.runtime_safety) { |
| 23070 | assert(try sema.intFitsInType(val, refined_ty, null)); | 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 | break :refined refined_ty; | 23075 | break :refined refined_ty; |
| ... | @@ -26610,7 +26610,7 @@ fn coerceExtra( | ... | @@ -26610,7 +26610,7 @@ fn coerceExtra( |
| 26610 | var in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src); | 26610 | var in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src); |
| 26611 | if (in_memory_result == .ok) { | 26611 | if (in_memory_result == .ok) { |
| 26612 | if (maybe_inst_val) |val| { | 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 | try sema.requireRuntimeBlock(block, inst_src, null); | 26615 | try sema.requireRuntimeBlock(block, inst_src, null); |
| 26616 | return block.addBitCast(dest_ty, inst); | 26616 | return block.addBitCast(dest_ty, inst); |
| ... | @@ -27278,82 +27278,12 @@ fn coerceExtra( | ... | @@ -27278,82 +27278,12 @@ fn coerceExtra( |
| 27278 | return sema.failWithOwnedErrorMsg(msg); | 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 | fn coerceInMemory( | 27281 | fn coerceInMemory( |
| 27349 | sema: *Sema, | 27282 | sema: *Sema, |
| 27350 | block: *Block, | | |
| 27351 | val: Value, | 27283 | val: Value, |
| 27352 | src_ty: Type, | | |
| 27353 | dst_ty: Type, | 27284 | dst_ty: Type, |
| 27354 | dst_ty_src: LazySrcLoc, | | |
| 27355 | ) CompileError!Air.Inst.Ref { | 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 | const InMemoryCoercionResult = union(enum) { | 27289 | const InMemoryCoercionResult = union(enum) { |
| ... | @@ -27891,6 +27821,22 @@ fn coerceInMemoryAllowed( | ... | @@ -27891,6 +27821,22 @@ fn coerceInMemoryAllowed( |
| 27891 | return .ok; | 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 | return InMemoryCoercionResult{ .no_match = .{ | 27840 | return InMemoryCoercionResult{ .no_match = .{ |
| 27895 | .actual = dest_ty, | 27841 | .actual = dest_ty, |
| 27896 | .wanted = src_ty, | 27842 | .wanted = src_ty, |
| ... | @@ -27959,9 +27905,8 @@ fn coerceInMemoryAllowedErrorSets( | ... | @@ -27959,9 +27905,8 @@ fn coerceInMemoryAllowedErrorSets( |
| 27959 | | 27905 | |
| 27960 | switch (src_ty.toIntern()) { | 27906 | switch (src_ty.toIntern()) { |
| 27961 | .anyerror_type => switch (ip.indexToKey(dest_ty.toIntern())) { | 27907 | .anyerror_type => switch (ip.indexToKey(dest_ty.toIntern())) { |
| 27962 | .inferred_error_set_type => unreachable, // Caught by dest_ty.isAnyError(mod) above. | | |
| 27963 | .simple_type => unreachable, // filtered out above | 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 | else => unreachable, | 27910 | else => unreachable, |
| 27966 | }, | 27911 | }, |
| 27967 | | 27912 | |
| ... | @@ -28008,8 +27953,6 @@ fn coerceInMemoryAllowedErrorSets( | ... | @@ -28008,8 +27953,6 @@ fn coerceInMemoryAllowedErrorSets( |
| 28008 | else => unreachable, | 27953 | else => unreachable, |
| 28009 | }, | 27954 | }, |
| 28010 | } | 27955 | } |
| 28011 | | | |
| 28012 | unreachable; | | |
| 28013 | } | 27956 | } |
| 28014 | | 27957 | |
| 28015 | fn coerceInMemoryAllowedFns( | 27958 | fn coerceInMemoryAllowedFns( |
| ... | @@ -29800,7 +29743,7 @@ fn coerceArrayLike( | ... | @@ -29800,7 +29743,7 @@ fn coerceArrayLike( |
| 29800 | if (in_memory_result == .ok) { | 29743 | if (in_memory_result == .ok) { |
| 29801 | if (try sema.resolveMaybeUndefVal(inst)) |inst_val| { | 29744 | if (try sema.resolveMaybeUndefVal(inst)) |inst_val| { |
| 29802 | // These types share the same comptime value representation. | 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 | try sema.requireRuntimeBlock(block, inst_src, null); | 29748 | try sema.requireRuntimeBlock(block, inst_src, null); |
| 29806 | return block.addBitCast(dest_ty, inst); | 29749 | return block.addBitCast(dest_ty, inst); |
| ... | @@ -31586,601 +31529,1435 @@ fn unionToTag( | ... | @@ -31586,601 +31529,1435 @@ fn unionToTag( |
| 31586 | return block.addTyOp(.get_union_tag, enum_ty, un); | 31529 | return block.addTyOp(.get_union_tag, enum_ty, un); |
| 31587 | } | 31530 | } |
| 31588 | | 31531 | |
| 31589 | fn resolvePeerTypes( | 31532 | const PeerResolveStrategy = enum { |
| 31590 | sema: *Sema, | 31533 | /// The type is not known. |
| 31591 | block: *Block, | 31534 | /// If refined no further, this is equivalent to `exact`. |
| 31592 | src: LazySrcLoc, | 31535 | unknown, |
| 31593 | instructions: []const Air.Inst.Ref, | 31536 | /// The type may be an error set or error union. |
| 31594 | candidate_srcs: Module.PeerTypeCandidateSrc, | 31537 | /// If refined no further, it is an error set. |
| 31595 | ) !Type { | 31538 | error_set, |
| 31596 | const mod = sema.mod; | 31539 | /// The type must be some error union. |
| 31597 | switch (instructions.len) { | 31540 | error_union, |
| 31598 | 0 => return Type.noreturn, | 31541 | /// The type may be @TypeOf(null), an optional or a C pointer. |
| 31599 | 1 => return sema.typeOf(instructions[0]), | 31542 | /// If refined no further, it is @TypeOf(null). |
| 31600 | else => {}, | 31543 | nullable, |
| 31601 | } | 31544 | /// The type must be some optional or a C pointer. |
| 31602 | | 31545 | /// If refined no further, it is an optional. |
| 31603 | const target = mod.getTarget(); | 31546 | optional, |
| 31604 | | 31547 | /// The type must be either an array or a vector. |
| 31605 | var chosen = instructions[0]; | 31548 | /// If refined no further, it is an array. |
| 31606 | // If this is non-null then it does the following thing, depending on the chosen zigTypeTag(mod). | 31549 | array, |
| 31607 | // * ErrorSet: this is an override | 31550 | /// The type must be a vector. |
| 31608 | // * ErrorUnion: this is an override of the error set only | 31551 | vector, |
| 31609 | // * other: at the end we make an ErrorUnion with the other thing and this | 31552 | /// The type must be a C pointer. |
| 31610 | var err_set_ty: ?Type = null; | 31553 | c_ptr, |
| 31611 | var any_are_null = false; | 31554 | /// The type must be a pointer (C or not). |
| 31612 | var seen_const = false; | 31555 | /// If refined no further, it is a non-C pointer. |
| 31613 | var convert_to_slice = false; | 31556 | ptr, |
| 31614 | var chosen_i: usize = 0; | 31557 | /// The type must be a function or a pointer to a function. |
| 31615 | for (instructions[1..], 0..) |candidate, candidate_i| { | 31558 | /// If refined no further, it is a function. |
| 31616 | const candidate_ty = sema.typeOf(candidate); | 31559 | func, |
| 31617 | const chosen_ty = sema.typeOf(chosen); | 31560 | /// The type must be an enum literal, or some specific enum or union. Which one is decided |
| 31618 | | 31561 | /// afterwards based on the types in question. |
| 31619 | const candidate_ty_tag = try candidate_ty.zigTypeTagOrPoison(mod); | 31562 | enum_or_union, |
| 31620 | const chosen_ty_tag = try chosen_ty.zigTypeTagOrPoison(mod); | 31563 | /// The type must be some integer or float type. |
| 31621 | | 31564 | /// If refined no further, it is `comptime_int`. |
| 31622 | // If the candidate can coerce into our chosen type, we're done. | 31565 | comptime_int, |
| 31623 | // If the chosen type can coerce into the candidate, use that. | 31566 | /// The type must be some float type. |
| 31624 | if ((try sema.coerceInMemoryAllowed(block, chosen_ty, candidate_ty, false, target, src, src)) == .ok) { | 31567 | /// If refined no further, it is `comptime_float`. |
| 31625 | continue; | 31568 | comptime_float, |
| 31626 | } | 31569 | /// The type must be some float or fixed-width integer type. |
| 31627 | if ((try sema.coerceInMemoryAllowed(block, candidate_ty, chosen_ty, false, target, src, src)) == .ok) { | 31570 | /// If refined no further, it is some fixed-width integer type. |
| 31628 | chosen = candidate; | 31571 | fixed_int, |
| 31629 | chosen_i = candidate_i + 1; | 31572 | /// The type must be some fixed-width float type. |
| 31630 | continue; | 31573 | fixed_float, |
| 31631 | } | 31574 | /// The type must be a struct literal or tuple type. |
| 31632 | | 31575 | coercible_struct, |
| 31633 | switch (candidate_ty_tag) { | 31576 | /// The peers must all be of the same type. |
| 31634 | .NoReturn, .Undefined => continue, | 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 => { | 31598 | const res: struct { ReasonMethod, PeerResolveStrategy } = switch (s0) { |
| 31637 | any_are_null = true; | 31599 | .unknown => .{ .all_s1, s1 }, |
| 31638 | continue; | 31600 | .error_set => switch (s1) { |
| | 31601 | .error_set => .{ .either, .error_set }, |
| | 31602 | else => .{ .all_s0, .error_union }, |
| 31639 | }, | 31603 | }, |
| 31640 | | 31604 | .error_union => switch (s1) { |
| 31641 | .Int => switch (chosen_ty_tag) { | 31605 | .error_union => .{ .either, .error_union }, |
| 31642 | .ComptimeInt => { | 31606 | else => .{ .all_s0, .error_union }, |
| 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 => {}, | | |
| 31659 | }, | 31607 | }, |
| 31660 | .ComptimeInt => switch (chosen_ty_tag) { | 31608 | .nullable => switch (s1) { |
| 31661 | .Int, .Float, .ComptimeFloat => continue, | 31609 | .nullable => .{ .either, .nullable }, |
| 31662 | .Pointer => if (chosen_ty.ptrSize(mod) == .C) continue, | 31610 | .c_ptr => .{ .all_s1, .c_ptr }, |
| 31663 | else => {}, | 31611 | else => .{ .all_s0, .optional }, |
| 31664 | }, | 31612 | }, |
| 31665 | .Float => switch (chosen_ty_tag) { | 31613 | .optional => switch (s1) { |
| 31666 | .Float => { | 31614 | .optional => .{ .either, .optional }, |
| 31667 | if (chosen_ty.floatBits(target) < candidate_ty.floatBits(target)) { | 31615 | .c_ptr => .{ .all_s1, .c_ptr }, |
| 31668 | chosen = candidate; | 31616 | else => .{ .all_s0, .optional }, |
| 31669 | chosen_i = candidate_i + 1; | | |
| 31670 | } | | |
| 31671 | continue; | | |
| 31672 | }, | | |
| 31673 | .ComptimeFloat, .ComptimeInt => { | | |
| 31674 | chosen = candidate; | | |
| 31675 | chosen_i = candidate_i + 1; | | |
| 31676 | continue; | | |
| 31677 | }, | | |
| 31678 | else => {}, | | |
| 31679 | }, | 31617 | }, |
| 31680 | .ComptimeFloat => switch (chosen_ty_tag) { | 31618 | .array => switch (s1) { |
| 31681 | .Float => continue, | 31619 | .array => .{ .either, .array }, |
| 31682 | .ComptimeInt => { | 31620 | .vector => .{ .all_s1, .vector }, |
| 31683 | chosen = candidate; | 31621 | else => .{ .all_s0, .array }, |
| 31684 | chosen_i = candidate_i + 1; | | |
| 31685 | continue; | | |
| 31686 | }, | | |
| 31687 | else => {}, | | |
| 31688 | }, | 31622 | }, |
| 31689 | .Enum => switch (chosen_ty_tag) { | 31623 | .vector => switch (s1) { |
| 31690 | .EnumLiteral => { | 31624 | .vector => .{ .either, .vector }, |
| 31691 | chosen = candidate; | 31625 | else => .{ .all_s0, .vector }, |
| 31692 | chosen_i = candidate_i + 1; | | |
| 31693 | continue; | | |
| 31694 | }, | | |
| 31695 | .Union => continue, | | |
| 31696 | else => {}, | | |
| 31697 | }, | 31626 | }, |
| 31698 | .EnumLiteral => switch (chosen_ty_tag) { | 31627 | .c_ptr => switch (s1) { |
| 31699 | .Enum, .Union => continue, | 31628 | .c_ptr => .{ .either, .c_ptr }, |
| 31700 | else => {}, | 31629 | else => .{ .all_s0, .c_ptr }, |
| 31701 | }, | 31630 | }, |
| 31702 | .Union => switch (chosen_ty_tag) { | 31631 | .ptr => switch (s1) { |
| 31703 | .Enum, .EnumLiteral => { | 31632 | .ptr => .{ .either, .ptr }, |
| 31704 | chosen = candidate; | 31633 | else => .{ .all_s0, .ptr }, |
| 31705 | chosen_i = candidate_i + 1; | | |
| 31706 | continue; | | |
| 31707 | }, | | |
| 31708 | else => {}, | | |
| 31709 | }, | 31634 | }, |
| 31710 | .ErrorSet => switch (chosen_ty_tag) { | 31635 | .func => switch (s1) { |
| 31711 | .ErrorSet => { | 31636 | .func => .{ .either, .func }, |
| 31712 | // If chosen is superset of candidate, keep it. | 31637 | else => .{ .all_s1, s1 }, // doesn't override anything later |
| 31713 | // If candidate is superset of chosen, switch it. | 31638 | }, |
| 31714 | // If neither is a superset, merge errors. | 31639 | .enum_or_union => switch (s1) { |
| 31715 | const chosen_set_ty = err_set_ty orelse chosen_ty; | 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)) { | 31666 | switch (res[0]) { |
| 31718 | continue; | 31667 | .all_s0 => { |
| 31719 | } | 31668 | if (!s0_is_a) { |
| 31720 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_ty, chosen_set_ty, src, src)) { | 31669 | reason_peer.* = b_peer_idx; |
| 31721 | err_set_ty = null; | 31670 | } |
| 31722 | chosen = candidate; | 31671 | }, |
| 31723 | chosen_i = candidate_i + 1; | 31672 | .all_s1 => { |
| 31724 | continue; | 31673 | if (s0_is_a) { |
| 31725 | } | 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); | 31683 | return res[1]; |
| 31728 | continue; | 31684 | } |
| 31729 | }, | | |
| 31730 | .ErrorUnion => { | | |
| 31731 | const chosen_set_ty = err_set_ty orelse chosen_ty.errorUnionSet(mod); | | |
| 31732 | | 31685 | |
| 31733 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_ty, src, src)) { | 31686 | fn select(ty: Type, mod: *Module) PeerResolveStrategy { |
| 31734 | continue; | 31687 | return switch (ty.zigTypeTag(mod)) { |
| 31735 | } | 31688 | .Type, .Void, .Bool, .Opaque, .Frame, .AnyFrame => .exact, |
| 31736 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_ty, chosen_set_ty, src, src)) { | 31689 | .NoReturn, .Undefined => .unknown, |
| 31737 | err_set_ty = candidate_ty; | 31690 | .Null => .nullable, |
| 31738 | continue; | 31691 | .ComptimeInt => .comptime_int, |
| 31739 | } | 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); | 31708 | const PeerResolveResult = union(enum) { |
| 31742 | continue; | 31709 | /// The peer type resolution was successful, and resulted in the given type. |
| 31743 | }, | 31710 | success: Type, |
| 31744 | else => { | 31711 | /// There was some generic conflict between two peers. |
| 31745 | if (err_set_ty) |chosen_set_ty| { | 31712 | conflict: struct { |
| 31746 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_ty, src, src)) { | 31713 | peer_idx_a: usize, |
| 31747 | continue; | 31714 | peer_idx_b: usize, |
| 31748 | } | 31715 | }, |
| 31749 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_ty, chosen_set_ty, src, src)) { | 31716 | /// There was an error when resolving the type of a struct or tuple field. |
| 31750 | err_set_ty = candidate_ty; | 31717 | field_error: struct { |
| 31751 | continue; | 31718 | /// The name of the field which caused the failure. |
| 31752 | } | 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); | 31726 | fn report( |
| 31755 | continue; | 31727 | result: PeerResolveResult, |
| 31756 | } else { | 31728 | sema: *Sema, |
| 31757 | err_set_ty = candidate_ty; | 31729 | block: *Block, |
| 31758 | continue; | 31730 | src: LazySrcLoc, |
| 31759 | } | 31731 | instructions: []const Air.Inst.Ref, |
| 31760 | }, | 31732 | candidate_srcs: Module.PeerTypeCandidateSrc, |
| 31761 | }, | 31733 | ) !*Module.ErrorMsg { |
| 31762 | .ErrorUnion => switch (chosen_ty_tag) { | 31734 | const mod = sema.mod; |
| 31763 | .ErrorSet => { | 31735 | const decl_ptr = mod.declPtr(block.src_decl); |
| 31764 | const chosen_set_ty = err_set_ty orelse chosen_ty; | 31736 | |
| 31765 | const candidate_set_ty = candidate_ty.errorUnionSet(mod); | 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)) { | 31740 | // If we mention fields we'll want to include field types, so put peer types in a buffer |
| 31768 | err_set_ty = chosen_set_ty; | 31741 | var peer_tys = try sema.arena.alloc(Type, instructions.len); |
| 31769 | } else if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_set_ty, chosen_set_ty, src, src)) { | 31742 | for (peer_tys, instructions) |*ty, inst| { |
| 31770 | err_set_ty = null; | 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 | } else { | 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; | 31767 | |
| 31775 | chosen_i = candidate_i + 1; | 31768 | // Continue on to child error |
| | 31769 | cur = field_error.sub_result.*; |
| | 31770 | peer_tys = field_error.field_types; |
| 31776 | continue; | 31771 | continue; |
| 31777 | }, | 31772 | }, |
| | 31773 | } |
| 31778 | | 31774 | |
| 31779 | .ErrorUnion => { | 31775 | // This is the path for reporting a generic conflict between two peers. |
| 31780 | const chosen_payload_ty = chosen_ty.errorUnionPayload(mod); | | |
| 31781 | const candidate_payload_ty = candidate_ty.errorUnionPayload(mod); | | |
| 31782 | | 31776 | |
| 31783 | const coerce_chosen = (try sema.coerceInMemoryAllowed(block, chosen_payload_ty, candidate_payload_ty, false, target, src, src)) == .ok; | 31777 | if (conflict_idx[1] < conflict_idx[0]) { |
| 31784 | const coerce_candidate = (try sema.coerceInMemoryAllowed(block, candidate_payload_ty, chosen_payload_ty, false, target, src, src)) == .ok; | 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) { | 31782 | const conflict_tys: [2]Type = .{ |
| 31787 | // If we can coerce to the candidate, we switch to that | 31783 | peer_tys[conflict_idx[0]], |
| 31788 | // type. This is the same logic as the bare (non-union) | 31784 | peer_tys[conflict_idx[1]], |
| 31789 | // coercion check we do at the top of this func. | 31785 | }; |
| 31790 | if (coerce_candidate) { | 31786 | const conflict_srcs: [2]?LazySrcLoc = .{ |
| 31791 | chosen = candidate; | 31787 | candidate_srcs.resolve(mod, decl_ptr, conflict_idx[0]), |
| 31792 | chosen_i = candidate_i + 1; | 31788 | candidate_srcs.resolve(mod, decl_ptr, conflict_idx[1]), |
| 31793 | } | 31789 | }; |
| 31794 | | 31790 | |
| 31795 | const chosen_set_ty = err_set_ty orelse chosen_ty.errorUnionSet(mod); | 31791 | const fmt = "incompatible types: '{}' and '{}'"; |
| 31796 | const candidate_set_ty = candidate_ty.errorUnionSet(mod); | 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)) { | 31805 | if (conflict_srcs[0]) |src_loc| try sema.errNote(block, src_loc, msg, "type '{}' here", .{conflict_tys[0].fmt(mod)}); |
| 31799 | err_set_ty = chosen_set_ty; | 31806 | if (conflict_srcs[1]) |src_loc| try sema.errNote(block, src_loc, msg, "type '{}' here", .{conflict_tys[1].fmt(mod)}); |
| 31800 | } else if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_set_ty, chosen_set_ty, src, src)) { | | |
| 31801 | err_set_ty = candidate_set_ty; | | |
| 31802 | } else { | | |
| 31803 | err_set_ty = try sema.errorSetMerge(chosen_set_ty, candidate_set_ty); | | |
| 31804 | } | | |
| 31805 | continue; | | |
| 31806 | } | | |
| 31807 | }, | | |
| 31808 | | 31807 | |
| 31809 | else => { | 31808 | // No child error |
| 31810 | if (err_set_ty) |chosen_set_ty| { | 31809 | break; |
| 31811 | const candidate_set_ty = candidate_ty.errorUnionSet(mod); | 31810 | } |
| 31812 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_set_ty, src, src)) { | | |
| 31813 | err_set_ty = chosen_set_ty; | | |
| 31814 | } else if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_set_ty, chosen_set_ty, src, src)) { | | |
| 31815 | err_set_ty = null; | | |
| 31816 | } else { | | |
| 31817 | err_set_ty = try sema.errorSetMerge(chosen_set_ty, candidate_set_ty); | | |
| 31818 | } | | |
| 31819 | } | | |
| 31820 | seen_const = seen_const or chosen_ty.isConstPtr(mod); | | |
| 31821 | chosen = candidate; | | |
| 31822 | chosen_i = candidate_i + 1; | | |
| 31823 | continue; | | |
| 31824 | }, | | |
| 31825 | }, | | |
| 31826 | .Pointer => { | | |
| 31827 | const cand_info = candidate_ty.ptrInfo(mod); | | |
| 31828 | switch (chosen_ty_tag) { | | |
| 31829 | .Pointer => { | | |
| 31830 | const chosen_info = chosen_ty.ptrInfo(mod); | | |
| 31831 | | 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 | 31816 | fn resolvePeerTypes( |
| 31835 | // *[N]T to []T | 31817 | sema: *Sema, |
| 31836 | if ((cand_info.size == .Many or cand_info.size == .Slice) and | 31818 | block: *Block, |
| 31837 | chosen_info.size == .One and | 31819 | src: LazySrcLoc, |
| 31838 | chosen_info.pointee_type.zigTypeTag(mod) == .Array) | 31820 | instructions: []const Air.Inst.Ref, |
| 31839 | { | 31821 | candidate_srcs: Module.PeerTypeCandidateSrc, |
| 31840 | // In case we see i.e.: `*[1]T`, `*[2]T`, `[*]T` | 31822 | ) !Type { |
| 31841 | convert_to_slice = false; | 31823 | switch (instructions.len) { |
| 31842 | chosen = candidate; | 31824 | 0 => return Type.noreturn, |
| 31843 | chosen_i = candidate_i + 1; | 31825 | 1 => return sema.typeOf(instructions[0]), |
| 31844 | continue; | 31826 | else => {}, |
| 31845 | } | 31827 | } |
| 31846 | if (cand_info.size == .One and | | |
| 31847 | cand_info.pointee_type.zigTypeTag(mod) == .Array and | | |
| 31848 | (chosen_info.size == .Many or chosen_info.size == .Slice)) | | |
| 31849 | { | | |
| 31850 | // In case we see i.e.: `*[1]T`, `*[2]T`, `[*]T` | | |
| 31851 | convert_to_slice = false; | | |
| 31852 | continue; | | |
| 31853 | } | | |
| 31854 | | 31828 | |
| 31855 | // *[N]T and *[M]T | 31829 | var peer_tys = try sema.arena.alloc(?Type, instructions.len); |
| 31856 | // Verify both are single-pointers to arrays. | 31830 | var peer_vals = try sema.arena.alloc(?Value, instructions.len); |
| 31857 | // Keep the one whose element type can be coerced into. | | |
| 31858 | if (chosen_info.size == .One and | | |
| 31859 | cand_info.size == .One and | | |
| 31860 | chosen_info.pointee_type.zigTypeTag(mod) == .Array and | | |
| 31861 | cand_info.pointee_type.zigTypeTag(mod) == .Array) | | |
| 31862 | { | | |
| 31863 | const chosen_elem_ty = chosen_info.pointee_type.childType(mod); | | |
| 31864 | const cand_elem_ty = cand_info.pointee_type.childType(mod); | | |
| 31865 | | 31831 | |
| 31866 | const chosen_ok = .ok == try sema.coerceInMemoryAllowed(block, chosen_elem_ty, cand_elem_ty, chosen_info.mutable, target, src, src); | 31832 | for (instructions, peer_tys, peer_vals) |inst, *ty, *val| { |
| 31867 | if (chosen_ok) { | 31833 | ty.* = sema.typeOf(inst); |
| 31868 | convert_to_slice = true; | 31834 | val.* = try sema.resolveMaybeUndefVal(inst); |
| 31869 | continue; | 31835 | } |
| 31870 | } | | |
| 31871 | | 31836 | |
| 31872 | const cand_ok = .ok == try sema.coerceInMemoryAllowed(block, cand_elem_ty, chosen_elem_ty, cand_info.mutable, target, src, src); | 31837 | switch (try sema.resolvePeerTypesInner(block, src, peer_tys, peer_vals)) { |
| 31873 | if (cand_ok) { | 31838 | .success => |ty| return ty, |
| 31874 | convert_to_slice = true; | 31839 | else => |result| { |
| 31875 | chosen = candidate; | 31840 | const msg = try result.report(sema, block, src, instructions, candidate_srcs); |
| 31876 | chosen_i = candidate_i + 1; | 31841 | return sema.failWithOwnedErrorMsg(msg); |
| 31877 | continue; | 31842 | }, |
| 31878 | } | 31843 | } |
| | 31844 | } |
| 31879 | | 31845 | |
| 31880 | // They're both bad. Report error. | 31846 | fn resolvePeerTypesInner( |
| 31881 | // In the future we probably want to use the | 31847 | sema: *Sema, |
| 31882 | // coerceInMemoryAllowed error reporting mechanism, | 31848 | block: *Block, |
| 31883 | // however, for now we just fall through for the | 31849 | src: LazySrcLoc, |
| 31884 | // "incompatible types" error below. | 31850 | peer_tys: []?Type, |
| 31885 | } | 31851 | peer_vals: []?Value, |
| | 31852 | ) !PeerResolveResult { |
| | 31853 | const mod = sema.mod; |
| 31886 | | 31854 | |
| 31887 | // [*c]T and any other pointer size | 31855 | var strat_reason: usize = 0; |
| 31888 | // Whichever element type can coerce to the other one, is | 31856 | var s: PeerResolveStrategy = .unknown; |
| 31889 | // the one we will keep. If they're both OK then we keep the | 31857 | for (peer_tys, 0..) |opt_ty, i| { |
| 31890 | // C pointer since it matches both single and many pointers. | 31858 | const ty = opt_ty orelse continue; |
| 31891 | if (cand_info.size == .C or chosen_info.size == .C) { | 31859 | s = s.merge(PeerResolveStrategy.select(ty, mod), &strat_reason, i); |
| 31892 | const cand_ok = .ok == try sema.coerceInMemoryAllowed(block, cand_info.pointee_type, chosen_info.pointee_type, cand_info.mutable, target, src, src); | 31860 | } |
| 31893 | const chosen_ok = .ok == try sema.coerceInMemoryAllowed(block, chosen_info.pointee_type, cand_info.pointee_type, chosen_info.mutable, target, src, src); | 31861 | |
| 31894 | | 31862 | if (s == .unknown) { |
| 31895 | if (cand_ok) { | 31863 | // The whole thing was noreturn or undefined - try to do an exact match |
| 31896 | if (chosen_ok) { | 31864 | s = .exact; |
| 31897 | if (chosen_info.size == .C) { | 31865 | } else { |
| 31898 | continue; | 31866 | // There was something other than noreturn and undefined, so we can ignore those peers |
| 31899 | } else { | 31867 | for (peer_tys) |*ty_ptr| { |
| 31900 | chosen = candidate; | 31868 | const ty = ty_ptr.* orelse continue; |
| 31901 | chosen_i = candidate_i + 1; | 31869 | switch (ty.zigTypeTag(mod)) { |
| 31902 | continue; | 31870 | .NoReturn, .Undefined => ty_ptr.* = null, |
| 31903 | } | 31871 | else => {}, |
| 31904 | } else { | 31872 | } |
| 31905 | chosen = candidate; | 31873 | } |
| 31906 | chosen_i = candidate_i + 1; | 31874 | } |
| 31907 | continue; | 31875 | |
| 31908 | } | 31876 | const target = mod.getTarget(); |
| 31909 | } else { | 31877 | |
| 31910 | if (chosen_ok) { | 31878 | switch (s) { |
| 31911 | continue; | 31879 | .unknown => unreachable, |
| 31912 | } else { | 31880 | |
| 31913 | // They're both bad. Report error. | 31881 | .error_set => { |
| 31914 | // In the future we probably want to use the | 31882 | var final_set: ?Type = null; |
| 31915 | // coerceInMemoryAllowed error reporting mechanism, | 31883 | for (peer_tys, 0..) |opt_ty, i| { |
| 31916 | // however, for now we just fall through for the | 31884 | const ty = opt_ty orelse continue; |
| 31917 | // "incompatible types" error below. | 31885 | if (ty.zigTypeTag(mod) != .ErrorSet) return .{ .conflict = .{ |
| 31918 | } | 31886 | .peer_idx_a = strat_reason, |
| 31919 | } | 31887 | .peer_idx_b = i, |
| 31920 | } | 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 => { | 31908 | .ErrorUnion => blk: { |
| 31923 | if (cand_info.size == .C) { | 31909 | const set_ty = ty.errorUnionSet(mod); |
| 31924 | chosen = candidate; | 31910 | ty_ptr.* = ty.errorUnionPayload(mod); |
| 31925 | chosen_i = candidate_i + 1; | 31911 | if (val_ptr.*) |eu_val| switch (mod.intern_pool.indexToKey(eu_val.toIntern())) { |
| 31926 | continue; | 31912 | .error_union => |eu| switch (eu.val) { |
| 31927 | } | 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 => { | 31921 | else => continue, // whole type is the payload |
| 31930 | const chosen_ptr_ty = chosen_ty.optionalChild(mod); | 31922 | }; |
| 31931 | if (chosen_ptr_ty.zigTypeTag(mod) == .Pointer) { | 31923 | if (final_set) |cur_set| { |
| 31932 | const chosen_info = chosen_ptr_ty.ptrInfo(mod); | 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 | 31953 | .optional => { |
| 31937 | // *[N]T to ?![]T | 31954 | for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| { |
| 31938 | if (cand_info.size == .One and | 31955 | const ty = ty_ptr.* orelse continue; |
| 31939 | cand_info.pointee_type.zigTypeTag(mod) == .Array and | 31956 | switch (ty.zigTypeTag(mod)) { |
| 31940 | (chosen_info.size == .Many or chosen_info.size == .Slice)) | 31957 | .Null => { |
| 31941 | { | 31958 | ty_ptr.* = null; |
| 31942 | continue; | 31959 | val_ptr.* = null; |
| 31943 | } | | |
| 31944 | } | | |
| 31945 | }, | | |
| 31946 | .ErrorUnion => { | | |
| 31947 | const chosen_ptr_ty = chosen_ty.errorUnionPayload(mod); | | |
| 31948 | if (chosen_ptr_ty.zigTypeTag(mod) == .Pointer) { | | |
| 31949 | const chosen_info = chosen_ptr_ty.ptrInfo(mod); | | |
| 31950 | | | |
| 31951 | seen_const = seen_const or !chosen_info.mutable or !cand_info.mutable; | | |
| 31952 | | | |
| 31953 | // *[N]T to E![*]T | | |
| 31954 | // *[N]T to E![]T | | |
| 31955 | if (cand_info.size == .One and | | |
| 31956 | cand_info.pointee_type.zigTypeTag(mod) == .Array and | | |
| 31957 | (chosen_info.size == .Many or chosen_info.size == .Slice)) | | |
| 31958 | { | | |
| 31959 | continue; | | |
| 31960 | } | | |
| 31961 | } | | |
| 31962 | }, | 31960 | }, |
| 31963 | .Fn => { | 31961 | .Optional => { |
| 31964 | if (!cand_info.mutable and cand_info.pointee_type.zigTypeTag(mod) == .Fn and .ok == try sema.coerceInMemoryAllowedFns(block, chosen_ty, cand_info.pointee_type, target, src, src)) { | 31962 | ty_ptr.* = ty.optionalChild(mod); |
| 31965 | chosen = candidate; | 31963 | if (val_ptr.*) |opt_val| val_ptr.* = if (!opt_val.isUndef(mod)) opt_val.optionalValue(mod) else null; |
| 31966 | chosen_i = candidate_i + 1; | | |
| 31967 | continue; | | |
| 31968 | } | | |
| 31969 | }, | 31964 | }, |
| 31970 | else => {}, | 31965 | else => {}, |
| 31971 | } | 31966 | } |
| 31972 | }, | 31967 | } |
| 31973 | .Optional => { | 31968 | const child_ty = switch (try sema.resolvePeerTypesInner( |
| 31974 | const opt_child_ty = candidate_ty.optionalChild(mod); | 31969 | block, |
| 31975 | if ((try sema.coerceInMemoryAllowed(block, chosen_ty, opt_child_ty, false, target, src, src)) == .ok) { | 31970 | src, |
| 31976 | seen_const = seen_const or opt_child_ty.isConstPtr(mod); | 31971 | peer_tys, |
| 31977 | any_are_null = true; | 31972 | peer_vals, |
| 31978 | continue; | 31973 | )) { |
| 31979 | } | 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); | 31980 | .array => { |
| 31982 | any_are_null = false; | 31981 | // Index of the first non-null peer |
| 31983 | chosen = candidate; | 31982 | var opt_first_idx: ?usize = null; |
| 31984 | chosen_i = candidate_i + 1; | 31983 | // Index of the first array or vector peer (i.e. not a tuple) |
| 31985 | continue; | 31984 | var opt_first_arr_idx: ?usize = null; |
| 31986 | }, | 31985 | // Set to non-null once we see any peer, even a tuple |
| 31987 | .Vector => switch (chosen_ty_tag) { | 31986 | var len: u64 = undefined; |
| 31988 | .Vector => { | 31987 | var sentinel: ?Value = undefined; |
| 31989 | const chosen_len = chosen_ty.vectorLen(mod); | 31988 | // Only set once we see a non-tuple peer |
| 31990 | const candidate_len = candidate_ty.vectorLen(mod); | 31989 | var elem_ty: Type = undefined; |
| 31991 | if (chosen_len != candidate_len) | 31990 | |
| 31992 | continue; | 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); | 32103 | const child_ty = switch (try sema.resolvePeerTypesInner( |
| 31995 | const candidate_child_ty = candidate_ty.childType(mod); | 32104 | block, |
| 31996 | if (chosen_child_ty.zigTypeTag(mod) == .Int and candidate_child_ty.zigTypeTag(mod) == .Int) { | 32105 | src, |
| 31997 | const chosen_info = chosen_child_ty.intInfo(mod); | 32106 | peer_tys, |
| 31998 | const candidate_info = candidate_child_ty.intInfo(mod); | 32107 | peer_vals, |
| 31999 | if (chosen_info.bits < candidate_info.bits) { | 32108 | )) { |
| 32000 | chosen = candidate; | 32109 | .success => |ty| ty, |
| 32001 | chosen_i = candidate_i + 1; | 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) { | 32172 | } else { |
| 32006 | if (chosen_ty.floatBits(target) < candidate_ty.floatBits(target)) { | 32173 | ptr_info.sentinel = null; |
| 32007 | chosen = candidate; | 32174 | } |
| 32008 | chosen_i = candidate_i + 1; | 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 | }, | 32461 | // Clear existing sentinel |
| 32013 | .Array => { | 32462 | ptr_info.sentinel = null; |
| 32014 | chosen = candidate; | 32463 | if (ptr_info.pointee_type.zigTypeTag(mod) == .Array) { |
| 32015 | chosen_i = candidate_i + 1; | 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 | continue; | 32500 | continue; |
| 32017 | }, | 32501 | }; |
| 32018 | else => {}, | 32502 | if (ty.zigTypeTag(mod) != .Fn) return .{ .conflict = .{ |
| 32019 | }, | 32503 | .peer_idx_a = strat_reason, |
| 32020 | .Array => switch (chosen_ty_tag) { | 32504 | .peer_idx_b = i, |
| 32021 | .Vector => continue, | 32505 | } }; |
| 32022 | else => {}, | 32506 | // ty -> cur_ty |
| 32023 | }, | 32507 | if (.ok == try sema.coerceInMemoryAllowedFns(block, cur_ty, ty, target, src, src)) { |
| 32024 | .Fn => if (chosen_ty.isSinglePointer(mod) and chosen_ty.isConstPtr(mod) and chosen_ty.childType(mod).zigTypeTag(mod) == .Fn) { | | |
| 32025 | if (.ok == try sema.coerceInMemoryAllowedFns(block, chosen_ty.childType(mod), candidate_ty, target, src, src)) { | | |
| 32026 | continue; | 32508 | continue; |
| 32027 | } | 32509 | } |
| 32028 | }, | 32510 | // cur_ty -> ty |
| 32029 | else => {}, | 32511 | if (.ok == try sema.coerceInMemoryAllowedFns(block, ty, cur_ty, target, src, src)) { |
| 32030 | } | 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) { | 32523 | .enum_or_union => { |
| 32033 | .NoReturn, .Undefined => { | 32524 | var opt_cur_ty: ?Type = null; |
| 32034 | chosen = candidate; | 32525 | // The peer index which gave the current type |
| 32035 | chosen_i = candidate_i + 1; | 32526 | var cur_ty_idx: usize = undefined; |
| 32036 | continue; | 32527 | |
| 32037 | }, | 32528 | for (peer_tys, 0..) |opt_ty, i| { |
| 32038 | .Null => { | 32529 | const ty = opt_ty orelse continue; |
| 32039 | any_are_null = true; | 32530 | switch (ty.zigTypeTag(mod)) { |
| 32040 | chosen = candidate; | 32531 | .EnumLiteral, .Enum, .Union => {}, |
| 32041 | chosen_i = candidate_i + 1; | 32532 | else => return .{ .conflict = .{ |
| 32042 | continue; | 32533 | .peer_idx_a = strat_reason, |
| 32043 | }, | 32534 | .peer_idx_b = i, |
| 32044 | .Optional => { | 32535 | } }, |
| 32045 | const opt_child_ty = chosen_ty.optionalChild(mod); | 32536 | } |
| 32046 | if ((try sema.coerceInMemoryAllowed(block, opt_child_ty, candidate_ty, false, target, src, src)) == .ok) { | 32537 | const cur_ty = opt_cur_ty orelse { |
| | 32538 | opt_cur_ty = ty; |
| | 32539 | cur_ty_idx = i; |
| 32047 | continue; | 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) { | 32641 | |
| 32050 | any_are_null = true; | 32642 | if (opt_val != null) any_comptime_known = true; |
| 32051 | chosen = candidate; | 32643 | |
| 32052 | chosen_i = candidate_i + 1; | 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 | continue; | 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 | }, | 32660 | } |
| 32056 | .ErrorUnion => { | 32661 | |
| 32057 | const payload_ty = chosen_ty.errorUnionPayload(mod); | 32662 | if (idx_signed == null) { |
| 32058 | if ((try sema.coerceInMemoryAllowed(block, payload_ty, candidate_ty, false, target, src, src)) == .ok) { | 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 | continue; | 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 | }, | 32776 | |
| 32062 | .ErrorSet => { | 32777 | if (!is_tuple) { |
| 32063 | chosen = candidate; | 32778 | for (field_names, 0..) |expected, field_idx| { |
| 32064 | chosen_i = candidate_i + 1; | 32779 | const actual = ty.structFieldName(field_idx, mod); |
| 32065 | if (err_set_ty) |chosen_set_ty| { | 32780 | if (actual == expected) continue; |
| 32066 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, chosen_ty, src, src)) { | 32781 | return .{ .conflict = .{ |
| 32067 | continue; | 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)) { | 32786 | } |
| 32070 | err_set_ty = chosen_ty; | 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 | continue; | 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); | 32830 | return .{ .field_error = .{ |
| 32075 | continue; | 32831 | .field_name = field_name, |
| 32076 | } else { | 32832 | .field_types = peer_field_tys, |
| 32077 | err_set_ty = chosen_ty; | 32833 | .sub_result = result_buf, |
| 32078 | continue; | 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 | 32869 | field_val.* = if (comptime_val) |v| v.toIntern() else .none; |
| 32085 | // the source locations. | 32870 | } |
| 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 | ); | | |
| 32096 | | 32871 | |
| 32097 | const msg = msg: { | 32872 | const final_ty = try mod.intern(.{ .anon_struct_type = .{ |
| 32098 | const msg = try sema.errMsg(block, src, "incompatible types: '{}' and '{}'", .{ | 32873 | .types = field_types, |
| 32099 | chosen_ty.fmt(mod), | 32874 | .names = if (is_tuple) &.{} else field_names, |
| 32100 | candidate_ty.fmt(mod), | 32875 | .values = field_vals, |
| 32101 | }); | 32876 | } }); |
| 32102 | errdefer msg.destroy(sema.gpa); | | |
| 32103 | | 32877 | |
| 32104 | if (chosen_src) |src_loc| | 32878 | return .{ .success = final_ty.toType() }; |
| 32105 | try sema.errNote(block, src_loc, msg, "type '{}' here", .{chosen_ty.fmt(mod)}); | 32879 | }, |
| 32106 | | 32880 | |
| 32107 | if (candidate_src) |src_loc| | 32881 | .exact => { |
| 32108 | try sema.errNote(block, src_loc, msg, "type '{}' here", .{candidate_ty.fmt(mod)}); | 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; | 32901 | fn maybeMergeErrorSets(sema: *Sema, block: *Block, src: LazySrcLoc, e0: Type, e1: Type) !Type { |
| 32111 | }; | 32902 | // e0 -> e1 |
| 32112 | return sema.failWithOwnedErrorMsg(msg); | 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) { | 32912 | return sema.errorSetMerge(e0, e1); |
| 32118 | // turn *[N]T => []T | 32913 | } |
| 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); | | |
| 32125 | | 32914 | |
| 32126 | const new_ptr_ty = try Type.ptr(sema.arena, mod, info); | 32915 | fn resolvePairInMemoryCoercible(sema: *Sema, block: *Block, src: LazySrcLoc, ty_a: Type, ty_b: Type) !?Type { |
| 32127 | const opt_ptr_ty = if (any_are_null) | 32916 | // ty_b -> ty_a |
| 32128 | try Type.optional(sema.arena, new_ptr_ty, mod) | 32917 | if (.ok == try sema.coerceInMemoryAllowed(block, ty_a, ty_b, true, sema.mod.getTarget(), src, src)) { |
| 32129 | else | 32918 | return ty_a; |
| 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); | | |
| 32133 | } | 32919 | } |
| 32134 | | 32920 | |
| 32135 | if (seen_const) { | 32921 | // ty_a -> ty_b |
| 32136 | // turn []T => []const T | 32922 | if (.ok == try sema.coerceInMemoryAllowed(block, ty_b, ty_a, true, sema.mod.getTarget(), src, src)) { |
| 32137 | switch (chosen_ty.zigTypeTag(mod)) { | 32923 | return ty_b; |
| 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 | } | | |
| 32163 | } | 32924 | } |
| 32164 | | 32925 | |
| 32165 | if (any_are_null) { | 32926 | return null; |
| 32166 | const opt_ty = switch (chosen_ty.zigTypeTag(mod)) { | 32927 | } |
| 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 | } | | |
| 32173 | | 32928 | |
| 32174 | if (err_set_ty) |ty| switch (chosen_ty.zigTypeTag(mod)) { | 32929 | const ArrayLike = struct { |
| 32175 | .ErrorSet => return ty, | 32930 | len: u64, |
| 32176 | .ErrorUnion => { | 32931 | /// `noreturn` indicates that this type is `struct{}` so can coerce to anything |
| 32177 | const payload_ty = chosen_ty.errorUnionPayload(mod); | 32932 | elem_ty: Type, |
| 32178 | return try mod.errorUnionType(ty, payload_ty); | 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 | pub fn resolveFnTypes(sema: *Sema, fn_ty: Type) CompileError!void { | 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,7 +35373,7 @@ fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value |
| 34596 | // Move mutable decl values to the InternPool and assert other decls are already in | 35373 | // Move mutable decl values to the InternPool and assert other decls are already in |
| 34597 | // the InternPool. | 35374 | // the InternPool. |
| 34598 | const uncoerced_val = if (deref.is_mutable) try tv.val.intern(tv.ty, mod) else tv.val.toIntern(); | 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 | return .{ .val = coerced_val }; | 35377 | return .{ .val = coerced_val }; |
| 34601 | } | 35378 | } |
| 34602 | } | 35379 | } |