Skip to content

Commit 96e8777

Browse files
committed
Support conversion between integer types.
Add an `IntFitsIn` interface with a custom witness, such that `T impls IntFitsIn(U)` if `T` is an integer type all of whose values fit losslessly into the integer type `U`. Use it to constrain implicit conversions between integer types. Assisted-by: Gemini 3 Pro via Antigravity
1 parent 4d00037 commit 96e8777

27 files changed

+1271
-339
lines changed

core/prelude/operators/as.carbon

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,5 @@ impl forall [T:! type, U:! ImplicitAs(T)] const U as ImplicitAs(T) {
4949
impl forall [T:! type, U:! type] T* as UnsafeAs(U*) {
5050
fn Convert[self: T*]() -> U* = "pointer.unsafe_convert";
5151
}
52+
53+
interface IntFitsIn(Dest:! type) {}

core/prelude/types/cpp/int.carbon

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ impl u32 as ImplicitAs(CppCompat.ULong32) {
9797
}
9898

9999
// TODO: ImplicitAs from ULong32 to UInt(N) if N > 32.
100-
final impl CppCompat.ULong32 as ImplicitAs(u32) {
101-
fn Convert[self: Self]() -> u32 = "int.convert";
100+
final impl CppCompat.ULong32 as ImplicitAs(u64) {
101+
fn Convert[self: Self]() -> u64 = "int.convert";
102102
}
103103

104104
impl IntLiteral() as ImplicitAs(CppCompat.LongLong64) {
@@ -146,8 +146,8 @@ impl u64 as ImplicitAs(CppCompat.ULongLong64) {
146146
}
147147

148148
// TODO: ImplicitAs from ULongLong64 to UInt(N) if N > 64.
149-
final impl CppCompat.ULongLong64 as ImplicitAs(u64) {
150-
fn Convert[self: Self]() -> u64 = "int.convert";
149+
final impl CppCompat.ULongLong64 as ImplicitAs(u128) {
150+
fn Convert[self: Self]() -> u128 = "int.convert";
151151
}
152152

153153
// TODO: As from Long32 to Int(N) if N < 32.

core/prelude/types/int.carbon

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,26 @@ final impl forall [From:! IntLiteral()] Int(From) as As(IntLiteral()) {
4242
fn Convert[self: Self]() -> IntLiteral() = "int.convert_checked";
4343
}
4444

45-
// TODO: Allow as an implicit conversion if N > M.
45+
// Work around the inability to put a `where` clause on a generic parameter of
46+
// type `IntLiteral`.
47+
// TODO: Remove this once possible.
48+
private interface AnyInt {
49+
let Width:! IntLiteral();
50+
fn AsInt[self: Self]() -> Int(Width);
51+
}
52+
53+
impl forall [N:! IntLiteral()] Int(N) as AnyInt where .Width = N {
54+
fn AsInt[self: Self]() -> Self { return self; }
55+
}
56+
57+
impl forall [To:! IntLiteral(), From:! AnyInt & IntFitsIn(Int(To))]
58+
From as ImplicitAs(Int(To)) {
59+
fn Convert[self: Self]() -> Int(To) {
60+
fn Impl(src: Int(From.Width)) -> Int(To) = "int.convert";
61+
return Impl(self.AsInt());
62+
}
63+
}
64+
4665
final impl forall [From:! IntLiteral(), To:! IntLiteral()] Int(From) as As(Int(To)) {
4766
fn Convert[self: Self]() -> Int(To) = "int.convert";
4867
}

core/prelude/types/uint.carbon

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,52 @@ final impl forall [From:! IntLiteral()] UInt(From) as As(IntLiteral()) {
4343
fn Convert[self: Self]() -> IntLiteral() = "int.convert_checked";
4444
}
4545

46-
// TODO: Allow as an implicit conversion if To > From.
46+
// Work around the inability to put a `where` clause on a generic parameter of
47+
// type `IntLiteral`.
48+
// TODO: Remove this once possible.
49+
private interface AnyUInt {
50+
let Width:! IntLiteral();
51+
fn AsUInt[self: Self]() -> UInt(Width);
52+
}
53+
54+
impl forall [N:! IntLiteral()] UInt(N) as AnyUInt where .Width = N {
55+
fn AsUInt[self: Self]() -> Self { return self; }
56+
}
57+
58+
// Work around the inability to constrain types other than `.Self` by reversing
59+
// the Self and argument type of `ImplicitAs`.
60+
// TODO: Remove this once possible.
61+
private interface FromUInt(From:! type) {
62+
fn Convert(from: From) -> Self;
63+
}
64+
65+
impl forall [To:! IntLiteral(), From:! AnyUInt & IntFitsIn(UInt(To))]
66+
UInt(To) as FromUInt(From) {
67+
fn Convert(from: From) -> Self {
68+
fn Impl(src: UInt(From.Width)) -> Self = "int.convert";
69+
return Impl(from.AsUInt());
70+
}
71+
}
72+
73+
impl forall [To:! IntLiteral(), From:! AnyUInt & IntFitsIn(Int(To))]
74+
Int(To) as FromUInt(From) {
75+
fn Convert(from: From) -> Self {
76+
fn Impl(src: UInt(From.Width)) -> Self = "int.convert";
77+
return Impl(from.AsUInt());
78+
}
79+
}
80+
81+
impl forall [From:! IntLiteral(), To:! FromUInt(UInt(From))]
82+
UInt(From) as ImplicitAs(To) {
83+
fn Convert[self: Self]() -> To {
84+
return To.Convert(self);
85+
}
86+
}
87+
4788
final impl forall [From:! IntLiteral(), To:! IntLiteral()] UInt(From) as As(UInt(To)) {
4889
fn Convert[self: Self]() -> UInt(To) = "int.convert";
4990
}
5091

51-
// TODO: Allow as an implicit conversion if To > From.
5292
final impl forall [From:! IntLiteral(), To:! IntLiteral()] UInt(From) as As(Int(To)) {
5393
fn Convert[self: Self]() -> Int(To) = "int.convert";
5494
}

toolchain/check/core_identifier.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ CARBON_CORE_IDENTIFIER(ImplicitAs)
4848
CARBON_CORE_IDENTIFIER(Inc)
4949
CARBON_CORE_IDENTIFIER(IndexWith)
5050
CARBON_CORE_IDENTIFIER(Int)
51+
CARBON_CORE_IDENTIFIER(IntFitsIn)
5152
CARBON_CORE_IDENTIFIER(Iterate)
5253
CARBON_CORE_IDENTIFIER(LeftShiftAssignWith)
5354
CARBON_CORE_IDENTIFIER(LeftShiftWith)

toolchain/check/cpp/impl_lookup.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ static auto GetCppAssociatedFunctions(const CoreInterface core_interface)
7777
case CoreInterface::Destroy:
7878
return {llvm::to_underlying(AssociatedFunction::Destructor)};
7979
case CoreInterface::Unknown:
80-
CARBON_FATAL(
81-
"`CoreInterface::Unknown` doesn't have a `CppCoreFunction` mapping");
80+
case CoreInterface::IntFitsIn:
81+
CARBON_FATAL("No AssociatedFunction mapping for this interface");
8282
}
8383
}
8484

@@ -131,6 +131,7 @@ static auto FindCppAssociatedFunction(Context& context, SemIR::LocId loc_id,
131131

132132
return fn_id;
133133
}
134+
134135
auto LookupCppImpl(Context& context, SemIR::LocId loc_id,
135136
CoreInterface core_interface,
136137
SemIR::ConstantId query_self_const_id,
@@ -142,18 +143,20 @@ auto LookupCppImpl(Context& context, SemIR::LocId loc_id,
142143
return SemIR::InstId::None;
143144
}
144145

145-
auto associated_functions = GetCppAssociatedFunctions(core_interface);
146146
auto witness_id = SemIR::ErrorInst::InstId;
147147

148148
switch (core_interface) {
149149
case CoreInterface::Copy:
150150
case CoreInterface::Destroy: {
151+
auto associated_functions = GetCppAssociatedFunctions(core_interface);
151152
CARBON_CHECK(associated_functions.count() == 1);
152153
witness_id = FindCppAssociatedFunction(
153154
context, loc_id,
154155
static_cast<AssociatedFunction>(associated_functions.to_ullong()),
155156
class_decl);
156157
} break;
158+
case CoreInterface::IntFitsIn:
159+
return SemIR::InstId::None;
157160
case CoreInterface::Unknown:
158161
CARBON_FATAL("shouldn't be called with `Unknown`");
159162
}

toolchain/check/custom_witness.cpp

Lines changed: 96 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
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

toolchain/check/custom_witness.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ auto BuildCustomWitness(Context& context, SemIR::LocId loc_id,
2424
enum class CoreInterface : std::int8_t {
2525
Copy = 1 << 0,
2626
Destroy = 1 << 1,
27+
IntFitsIn = 1 << 2,
2728

2829
Unknown = -1,
2930
};

toolchain/check/testdata/array/bound_values.carbon

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ var b: array(1, 39999999999999999993);
120120
// CHECK:STDOUT: %To.fe9: Core.IntLiteral = symbolic_binding To, 0 [symbolic]
121121
// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.type.2b1: type = fn_type @Core.IntLiteral.as.As.impl.Convert, @Core.IntLiteral.as.As.impl(%To.fe9) [symbolic]
122122
// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.979: %Core.IntLiteral.as.As.impl.Convert.type.2b1 = struct_value () [symbolic]
123-
// CHECK:STDOUT: %From: Core.IntLiteral = symbolic_binding From, 0 [symbolic]
123+
// CHECK:STDOUT: %From.fe9: Core.IntLiteral = symbolic_binding From, 0 [symbolic]
124124
// CHECK:STDOUT: %As.impl_witness.e1d: <witness> = impl_witness imports.%As.impl_witness_table.7eb, @Core.IntLiteral.as.As.impl(%int_32) [concrete]
125125
// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.type.3e7: type = fn_type @Core.IntLiteral.as.As.impl.Convert, @Core.IntLiteral.as.As.impl(%int_32) [concrete]
126126
// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.bbd: %Core.IntLiteral.as.As.impl.Convert.type.3e7 = struct_value () [concrete]
@@ -132,25 +132,25 @@ var b: array(1, 39999999999999999993);
132132
// CHECK:STDOUT: %bound_method.d64: <bound method> = bound_method %int_3.1ba, %Core.IntLiteral.as.As.impl.Convert.specific_fn [concrete]
133133
// CHECK:STDOUT: %int_3.d14: %u32 = int_value 3 [concrete]
134134
// CHECK:STDOUT: %ImplicitAs.type.139: type = facet_type <@ImplicitAs, @ImplicitAs(Core.IntLiteral)> [concrete]
135-
// CHECK:STDOUT: %UInt.as.ImplicitAs.impl.Convert.type.7f8: type = fn_type @UInt.as.ImplicitAs.impl.Convert, @UInt.as.ImplicitAs.impl(%From) [symbolic]
135+
// CHECK:STDOUT: %UInt.as.ImplicitAs.impl.Convert.type.7f8: type = fn_type @UInt.as.ImplicitAs.impl.Convert.1, @UInt.as.ImplicitAs.impl.607(%From.fe9) [symbolic]
136136
// CHECK:STDOUT: %UInt.as.ImplicitAs.impl.Convert.0ea: %UInt.as.ImplicitAs.impl.Convert.type.7f8 = struct_value () [symbolic]
137-
// CHECK:STDOUT: %ImplicitAs.impl_witness.895: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table.493, @UInt.as.ImplicitAs.impl(%int_32) [concrete]
138-
// CHECK:STDOUT: %UInt.as.ImplicitAs.impl.Convert.type.543: type = fn_type @UInt.as.ImplicitAs.impl.Convert, @UInt.as.ImplicitAs.impl(%int_32) [concrete]
137+
// CHECK:STDOUT: %ImplicitAs.impl_witness.895: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table.493, @UInt.as.ImplicitAs.impl.607(%int_32) [concrete]
138+
// CHECK:STDOUT: %UInt.as.ImplicitAs.impl.Convert.type.543: type = fn_type @UInt.as.ImplicitAs.impl.Convert.1, @UInt.as.ImplicitAs.impl.607(%int_32) [concrete]
139139
// CHECK:STDOUT: %UInt.as.ImplicitAs.impl.Convert.342: %UInt.as.ImplicitAs.impl.Convert.type.543 = struct_value () [concrete]
140140
// CHECK:STDOUT: %ImplicitAs.facet.2c6: %ImplicitAs.type.139 = facet_value %u32, (%ImplicitAs.impl_witness.895) [concrete]
141141
// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.70d: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(Core.IntLiteral, %ImplicitAs.facet.2c6) [concrete]
142142
// CHECK:STDOUT: %.044: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.70d, %ImplicitAs.facet.2c6 [concrete]
143143
// CHECK:STDOUT: %UInt.as.ImplicitAs.impl.Convert.bound: <bound method> = bound_method %int_3.d14, %UInt.as.ImplicitAs.impl.Convert.342 [concrete]
144-
// CHECK:STDOUT: %UInt.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %UInt.as.ImplicitAs.impl.Convert.342, @UInt.as.ImplicitAs.impl.Convert(%int_32) [concrete]
144+
// CHECK:STDOUT: %UInt.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %UInt.as.ImplicitAs.impl.Convert.342, @UInt.as.ImplicitAs.impl.Convert.1(%int_32) [concrete]
145145
// CHECK:STDOUT: %bound_method.c86: <bound method> = bound_method %int_3.d14, %UInt.as.ImplicitAs.impl.Convert.specific_fn [concrete]
146146
// CHECK:STDOUT: %array_type: type = array_type %int_3.1ba, %i32 [concrete]
147147
// CHECK:STDOUT: }
148148
// CHECK:STDOUT:
149149
// CHECK:STDOUT: imports {
150150
// CHECK:STDOUT: %Core.import_ref.600: @Core.IntLiteral.as.As.impl.%Core.IntLiteral.as.As.impl.Convert.type (%Core.IntLiteral.as.As.impl.Convert.type.2b1) = import_ref Core//prelude/types/uint, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.As.impl.%Core.IntLiteral.as.As.impl.Convert (constants.%Core.IntLiteral.as.As.impl.Convert.979)]
151151
// CHECK:STDOUT: %As.impl_witness_table.7eb = impl_witness_table (%Core.import_ref.600), @Core.IntLiteral.as.As.impl [concrete]
152-
// CHECK:STDOUT: %Core.import_ref.483: @UInt.as.ImplicitAs.impl.%UInt.as.ImplicitAs.impl.Convert.type (%UInt.as.ImplicitAs.impl.Convert.type.7f8) = import_ref Core//prelude/types/uint, loc{{\d+_\d+}}, loaded [symbolic = @UInt.as.ImplicitAs.impl.%UInt.as.ImplicitAs.impl.Convert (constants.%UInt.as.ImplicitAs.impl.Convert.0ea)]
153-
// CHECK:STDOUT: %ImplicitAs.impl_witness_table.493 = impl_witness_table (%Core.import_ref.483), @UInt.as.ImplicitAs.impl [concrete]
152+
// CHECK:STDOUT: %Core.import_ref.483: @UInt.as.ImplicitAs.impl.607.%UInt.as.ImplicitAs.impl.Convert.type (%UInt.as.ImplicitAs.impl.Convert.type.7f8) = import_ref Core//prelude/types/uint, loc{{\d+_\d+}}, loaded [symbolic = @UInt.as.ImplicitAs.impl.607.%UInt.as.ImplicitAs.impl.Convert (constants.%UInt.as.ImplicitAs.impl.Convert.0ea)]
153+
// CHECK:STDOUT: %ImplicitAs.impl_witness_table.493 = impl_witness_table (%Core.import_ref.483), @UInt.as.ImplicitAs.impl.607 [concrete]
154154
// CHECK:STDOUT: }
155155
// CHECK:STDOUT:
156156
// CHECK:STDOUT: file {
@@ -167,7 +167,7 @@ var b: array(1, 39999999999999999993);
167167
// CHECK:STDOUT: %.loc6_18.2: %u32 = converted %int_3.loc6, %.loc6_18.1 [concrete = constants.%int_3.d14]
168168
// CHECK:STDOUT: %impl.elem0.loc6_18.2: %.044 = impl_witness_access constants.%ImplicitAs.impl_witness.895, element0 [concrete = constants.%UInt.as.ImplicitAs.impl.Convert.342]
169169
// CHECK:STDOUT: %bound_method.loc6_18.3: <bound method> = bound_method %.loc6_18.2, %impl.elem0.loc6_18.2 [concrete = constants.%UInt.as.ImplicitAs.impl.Convert.bound]
170-
// CHECK:STDOUT: %specific_fn.loc6_18.2: <specific function> = specific_function %impl.elem0.loc6_18.2, @UInt.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%UInt.as.ImplicitAs.impl.Convert.specific_fn]
170+
// CHECK:STDOUT: %specific_fn.loc6_18.2: <specific function> = specific_function %impl.elem0.loc6_18.2, @UInt.as.ImplicitAs.impl.Convert.1(constants.%int_32) [concrete = constants.%UInt.as.ImplicitAs.impl.Convert.specific_fn]
171171
// CHECK:STDOUT: %bound_method.loc6_18.4: <bound method> = bound_method %.loc6_18.2, %specific_fn.loc6_18.2 [concrete = constants.%bound_method.c86]
172172
// CHECK:STDOUT: %UInt.as.ImplicitAs.impl.Convert.call: init Core.IntLiteral = call %bound_method.loc6_18.4(%.loc6_18.2) [concrete = constants.%int_3.1ba]
173173
// CHECK:STDOUT: %.loc6_18.3: Core.IntLiteral = value_of_initializer %UInt.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_3.1ba]

0 commit comments

Comments
 (0)