1414#include " toolchain/check/inst.h"
1515#include " toolchain/check/name_lookup.h"
1616#include " toolchain/check/type.h"
17+ #include " toolchain/check/type_completion.h"
1718#include " toolchain/sem_ir/builtin_function_kind.h"
1819#include " toolchain/sem_ir/ids.h"
1920#include " toolchain/sem_ir/typed_insts.h"
@@ -283,7 +284,8 @@ auto GetCoreInterface(Context& context, SemIR::InterfaceId interface_id)
283284
284285 for (auto [core_identifier, core_interface] :
285286 {std::pair{CoreIdentifier::Copy, CoreInterface::Copy},
286- std::pair{CoreIdentifier::Destroy, CoreInterface::Destroy}}) {
287+ std::pair{CoreIdentifier::Destroy, CoreInterface::Destroy},
288+ std::pair{CoreIdentifier::IntFitsIn, CoreInterface::IntFitsIn}}) {
287289 if (interface.name_id ==
288290 context.core_identifiers ().AddNameId (core_identifier)) {
289291 return core_interface;
@@ -348,16 +350,11 @@ static auto TypeCanDestroy(Context& context,
348350 }
349351}
350352
351- auto LookupCustomWitness (Context& context, SemIR::LocId loc_id,
352- CoreInterface core_interface ,
353- SemIR::ConstantId query_self_const_id,
354- SemIR::SpecificInterfaceId query_specific_interface_id)
353+ static auto MakeDestroyWitness (
354+ Context& context, SemIR::LocId loc_id ,
355+ SemIR::ConstantId query_self_const_id,
356+ SemIR::SpecificInterfaceId query_specific_interface_id)
355357 -> std::optional<SemIR::InstId> {
356- // TODO: Handle more interfaces, particularly copy, move, and conversion.
357- if (core_interface != CoreInterface::Destroy) {
358- return std::nullopt ;
359- }
360-
361358 auto query_specific_interface =
362359 context.specific_interfaces ().Get (query_specific_interface_id);
363360
@@ -383,4 +380,93 @@ auto LookupCustomWitness(Context& context, SemIR::LocId loc_id,
383380 query_specific_interface_id, {op_id});
384381}
385382
383+ static auto MakeIntFitsInWitness (
384+ Context& context, SemIR::LocId loc_id,
385+ SemIR::ConstantId query_self_const_id,
386+ SemIR::SpecificInterfaceId query_specific_interface_id)
387+ -> std::optional<SemIR::InstId> {
388+ auto query_specific_interface =
389+ context.specific_interfaces ().Get (query_specific_interface_id);
390+
391+ auto args_id = query_specific_interface.specific_id ;
392+ if (!args_id.has_value ()) {
393+ return std::nullopt ;
394+ }
395+ auto args_block_id = context.specifics ().Get (args_id).args_id ;
396+ auto args_block = context.inst_blocks ().Get (args_block_id);
397+ if (args_block.size () != 1 ) {
398+ return std::nullopt ;
399+ }
400+
401+ auto dest_const_id = context.constant_values ().Get (args_block[0 ]);
402+ if (!dest_const_id.is_constant ()) {
403+ return std::nullopt ;
404+ }
405+
406+ auto src_type_id = GetFacetAsType (context, query_self_const_id);
407+ auto dest_type_id = GetFacetAsType (context, dest_const_id);
408+
409+ auto context_fn = [](DiagnosticContextBuilder& /* builder*/ ) -> void {};
410+ if (!RequireCompleteType (context, src_type_id, loc_id, context_fn) ||
411+ !RequireCompleteType (context, dest_type_id, loc_id, context_fn)) {
412+ return std::nullopt ;
413+ }
414+
415+ auto src_info = context.types ().TryGetIntTypeInfo (src_type_id);
416+ auto dest_info = context.types ().TryGetIntTypeInfo (dest_type_id);
417+
418+ if (!src_info || !dest_info) {
419+ return std::nullopt ;
420+ }
421+
422+ // If the bit width is unknown (e.g., due to symbolic evaluation), we cannot
423+ // determine whether it fits yet.
424+ if (src_info->bit_width == IntId::None ||
425+ dest_info->bit_width == IntId::None) {
426+ return std::nullopt ;
427+ }
428+
429+ const auto & src_width = context.ints ().Get (src_info->bit_width );
430+ const auto & dest_width = context.ints ().Get (dest_info->bit_width );
431+
432+ bool fits = false ;
433+ if (src_info->is_signed && !dest_info->is_signed ) {
434+ // Signed -> unsigned: would truncate the sign bit.
435+ fits = false ;
436+ } else if (src_info->is_signed == dest_info->is_signed ) {
437+ // Signed -> signed or unsigned -> unsigned: allow widening or preserving
438+ // width.
439+ fits = src_width.sle (dest_width);
440+ } else {
441+ // Unsigned -> signed: strict widening required.
442+ fits = src_width.slt (dest_width);
443+ }
444+
445+ if (!fits) {
446+ return std::nullopt ;
447+ }
448+
449+ return BuildCustomWitness (context, loc_id, query_self_const_id,
450+ query_specific_interface_id, {});
451+ }
452+
453+ auto LookupCustomWitness (Context& context, SemIR::LocId loc_id,
454+ CoreInterface core_interface,
455+ SemIR::ConstantId query_self_const_id,
456+ SemIR::SpecificInterfaceId query_specific_interface_id)
457+ -> std::optional<SemIR::InstId> {
458+ switch (core_interface) {
459+ case CoreInterface::Destroy:
460+ return MakeDestroyWitness (context, loc_id, query_self_const_id,
461+ query_specific_interface_id);
462+ case CoreInterface::IntFitsIn:
463+ return MakeIntFitsInWitness (context, loc_id, query_self_const_id,
464+ query_specific_interface_id);
465+ case CoreInterface::Copy:
466+ case CoreInterface::Unknown:
467+ // TODO: Handle more interfaces, particularly copy, move, and conversion.
468+ return std::nullopt ;
469+ }
470+ }
471+
386472} // namespace Carbon::Check
0 commit comments