Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions core/prelude/operators/as.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,5 @@ impl forall [T:! type, U:! ImplicitAs(T)] const U as ImplicitAs(T) {
impl forall [T:! type, U:! type] T* as UnsafeAs(U*) {
fn Convert[self: T*]() -> U* = "pointer.unsafe_convert";
}

interface IntFitsIn(Dest:! type) {}
8 changes: 4 additions & 4 deletions core/prelude/types/cpp/int.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ impl u32 as ImplicitAs(CppCompat.ULong32) {
}

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

impl IntLiteral() as ImplicitAs(CppCompat.LongLong64) {
Expand Down Expand Up @@ -146,8 +146,8 @@ impl u64 as ImplicitAs(CppCompat.ULongLong64) {
}

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

// TODO: As from Long32 to Int(N) if N < 32.
Expand Down
21 changes: 20 additions & 1 deletion core/prelude/types/int.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,26 @@ final impl forall [From:! IntLiteral()] Int(From) as As(IntLiteral()) {
fn Convert[self: Self]() -> IntLiteral() = "int.convert_checked";
}

// TODO: Allow as an implicit conversion if N > M.
// Work around the inability to put a `where` clause on a generic parameter of
// type `IntLiteral`.
// TODO: Remove this once possible.
private interface AnyInt {
let Width:! IntLiteral();
fn AsInt[self: Self]() -> Int(Width);
}

impl forall [N:! IntLiteral()] Int(N) as AnyInt where .Width = N {
fn AsInt[self: Self]() -> Self { return self; }
}

impl forall [To:! IntLiteral(), From:! AnyInt & IntFitsIn(Int(To))]
From as ImplicitAs(Int(To)) {
fn Convert[self: Self]() -> Int(To) {
fn Impl(src: Int(From.Width)) -> Int(To) = "int.convert";
return Impl(self.AsInt());
}
}

final impl forall [From:! IntLiteral(), To:! IntLiteral()] Int(From) as As(Int(To)) {
fn Convert[self: Self]() -> Int(To) = "int.convert";
}
Expand Down
44 changes: 42 additions & 2 deletions core/prelude/types/uint.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,52 @@ final impl forall [From:! IntLiteral()] UInt(From) as As(IntLiteral()) {
fn Convert[self: Self]() -> IntLiteral() = "int.convert_checked";
}

// TODO: Allow as an implicit conversion if To > From.
// Work around the inability to put a `where` clause on a generic parameter of
// type `IntLiteral`.
// TODO: Remove this once possible.
private interface AnyUInt {
let Width:! IntLiteral();
fn AsUInt[self: Self]() -> UInt(Width);
}

impl forall [N:! IntLiteral()] UInt(N) as AnyUInt where .Width = N {
fn AsUInt[self: Self]() -> Self { return self; }
}

// Work around the inability to constrain types other than `.Self` by reversing
// the Self and argument type of `ImplicitAs`.
// TODO: Remove this once possible.
private interface FromUInt(From:! type) {
fn Convert(from: From) -> Self;
}

impl forall [To:! IntLiteral(), From:! AnyUInt & IntFitsIn(UInt(To))]
UInt(To) as FromUInt(From) {
fn Convert(from: From) -> Self {
fn Impl(src: UInt(From.Width)) -> Self = "int.convert";
return Impl(from.AsUInt());
}
}

impl forall [To:! IntLiteral(), From:! AnyUInt & IntFitsIn(Int(To))]
Int(To) as FromUInt(From) {
fn Convert(from: From) -> Self {
fn Impl(src: UInt(From.Width)) -> Self = "int.convert";
return Impl(from.AsUInt());
}
}

impl forall [From:! IntLiteral(), To:! FromUInt(UInt(From))]
UInt(From) as ImplicitAs(To) {
fn Convert[self: Self]() -> To {
return To.Convert(self);
}
}

final impl forall [From:! IntLiteral(), To:! IntLiteral()] UInt(From) as As(UInt(To)) {
fn Convert[self: Self]() -> UInt(To) = "int.convert";
}

// TODO: Allow as an implicit conversion if To > From.
final impl forall [From:! IntLiteral(), To:! IntLiteral()] UInt(From) as As(Int(To)) {
fn Convert[self: Self]() -> Int(To) = "int.convert";
}
Expand Down
1 change: 1 addition & 0 deletions toolchain/check/core_identifier.def
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ CARBON_CORE_IDENTIFIER(ImplicitAs)
CARBON_CORE_IDENTIFIER(Inc)
CARBON_CORE_IDENTIFIER(IndexWith)
CARBON_CORE_IDENTIFIER(Int)
CARBON_CORE_IDENTIFIER(IntFitsIn)
CARBON_CORE_IDENTIFIER(Iterate)
CARBON_CORE_IDENTIFIER(LeftShiftAssignWith)
CARBON_CORE_IDENTIFIER(LeftShiftWith)
Expand Down
9 changes: 6 additions & 3 deletions toolchain/check/cpp/impl_lookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ static auto GetCppAssociatedFunctions(const CoreInterface core_interface)
case CoreInterface::Destroy:
return {llvm::to_underlying(AssociatedFunction::Destructor)};
case CoreInterface::Unknown:
CARBON_FATAL(
"`CoreInterface::Unknown` doesn't have a `CppCoreFunction` mapping");
case CoreInterface::IntFitsIn:
CARBON_FATAL("No AssociatedFunction mapping for this interface");
}
}

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

return fn_id;
}

auto LookupCppImpl(Context& context, SemIR::LocId loc_id,
CoreInterface core_interface,
SemIR::ConstantId query_self_const_id,
Expand All @@ -142,18 +143,20 @@ auto LookupCppImpl(Context& context, SemIR::LocId loc_id,
return SemIR::InstId::None;
}

auto associated_functions = GetCppAssociatedFunctions(core_interface);
auto witness_id = SemIR::ErrorInst::InstId;

switch (core_interface) {
case CoreInterface::Copy:
case CoreInterface::Destroy: {
auto associated_functions = GetCppAssociatedFunctions(core_interface);
CARBON_CHECK(associated_functions.count() == 1);
witness_id = FindCppAssociatedFunction(
context, loc_id,
static_cast<AssociatedFunction>(associated_functions.to_ullong()),
class_decl);
} break;
case CoreInterface::IntFitsIn:
return SemIR::InstId::None;
case CoreInterface::Unknown:
CARBON_FATAL("shouldn't be called with `Unknown`");
}
Expand Down
106 changes: 96 additions & 10 deletions toolchain/check/custom_witness.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "toolchain/check/inst.h"
#include "toolchain/check/name_lookup.h"
#include "toolchain/check/type.h"
#include "toolchain/check/type_completion.h"
#include "toolchain/sem_ir/builtin_function_kind.h"
#include "toolchain/sem_ir/ids.h"
#include "toolchain/sem_ir/typed_insts.h"
Expand Down Expand Up @@ -283,7 +284,8 @@ auto GetCoreInterface(Context& context, SemIR::InterfaceId interface_id)

for (auto [core_identifier, core_interface] :
{std::pair{CoreIdentifier::Copy, CoreInterface::Copy},
std::pair{CoreIdentifier::Destroy, CoreInterface::Destroy}}) {
std::pair{CoreIdentifier::Destroy, CoreInterface::Destroy},
std::pair{CoreIdentifier::IntFitsIn, CoreInterface::IntFitsIn}}) {
if (interface.name_id ==
context.core_identifiers().AddNameId(core_identifier)) {
return core_interface;
Expand Down Expand Up @@ -348,16 +350,11 @@ static auto TypeCanDestroy(Context& context,
}
}

auto LookupCustomWitness(Context& context, SemIR::LocId loc_id,
CoreInterface core_interface,
SemIR::ConstantId query_self_const_id,
SemIR::SpecificInterfaceId query_specific_interface_id)
static auto MakeDestroyWitness(
Context& context, SemIR::LocId loc_id,
SemIR::ConstantId query_self_const_id,
SemIR::SpecificInterfaceId query_specific_interface_id)
-> std::optional<SemIR::InstId> {
// TODO: Handle more interfaces, particularly copy, move, and conversion.
if (core_interface != CoreInterface::Destroy) {
return std::nullopt;
}

auto query_specific_interface =
context.specific_interfaces().Get(query_specific_interface_id);

Expand All @@ -383,4 +380,93 @@ auto LookupCustomWitness(Context& context, SemIR::LocId loc_id,
query_specific_interface_id, {op_id});
}

static auto MakeIntFitsInWitness(
Context& context, SemIR::LocId loc_id,
SemIR::ConstantId query_self_const_id,
SemIR::SpecificInterfaceId query_specific_interface_id)
-> std::optional<SemIR::InstId> {
auto query_specific_interface =
context.specific_interfaces().Get(query_specific_interface_id);

auto args_id = query_specific_interface.specific_id;
if (!args_id.has_value()) {
return std::nullopt;
}
auto args_block_id = context.specifics().Get(args_id).args_id;
auto args_block = context.inst_blocks().Get(args_block_id);
if (args_block.size() != 1) {
return std::nullopt;
}

auto dest_const_id = context.constant_values().Get(args_block[0]);
if (!dest_const_id.is_constant()) {
return std::nullopt;
}

auto src_type_id = GetFacetAsType(context, query_self_const_id);
auto dest_type_id = GetFacetAsType(context, dest_const_id);

auto context_fn = [](DiagnosticContextBuilder& /*builder*/) -> void {};
if (!RequireCompleteType(context, src_type_id, loc_id, context_fn) ||
!RequireCompleteType(context, dest_type_id, loc_id, context_fn)) {
return std::nullopt;
}

auto src_info = context.types().TryGetIntTypeInfo(src_type_id);
auto dest_info = context.types().TryGetIntTypeInfo(dest_type_id);

if (!src_info || !dest_info) {
return std::nullopt;
}

// If the bit width is unknown (e.g., due to symbolic evaluation), we cannot
// determine whether it fits yet.
if (src_info->bit_width == IntId::None ||
dest_info->bit_width == IntId::None) {
return std::nullopt;
}

const auto& src_width = context.ints().Get(src_info->bit_width);
const auto& dest_width = context.ints().Get(dest_info->bit_width);

bool fits = false;
if (src_info->is_signed && !dest_info->is_signed) {
// Signed -> unsigned: would truncate the sign bit.
fits = false;
} else if (src_info->is_signed == dest_info->is_signed) {
// Signed -> signed or unsigned -> unsigned: allow widening or preserving
// width.
fits = src_width.sle(dest_width);
} else {
// Unsigned -> signed: strict widening required.
fits = src_width.slt(dest_width);
}

if (!fits) {
return std::nullopt;
}

return BuildCustomWitness(context, loc_id, query_self_const_id,
query_specific_interface_id, {});
}

auto LookupCustomWitness(Context& context, SemIR::LocId loc_id,
CoreInterface core_interface,
SemIR::ConstantId query_self_const_id,
SemIR::SpecificInterfaceId query_specific_interface_id)
-> std::optional<SemIR::InstId> {
switch (core_interface) {
case CoreInterface::Destroy:
return MakeDestroyWitness(context, loc_id, query_self_const_id,
query_specific_interface_id);
case CoreInterface::IntFitsIn:
return MakeIntFitsInWitness(context, loc_id, query_self_const_id,
query_specific_interface_id);
case CoreInterface::Copy:
case CoreInterface::Unknown:
// TODO: Handle more interfaces, particularly copy, move, and conversion.
return std::nullopt;
}
}

} // namespace Carbon::Check
1 change: 1 addition & 0 deletions toolchain/check/custom_witness.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ auto BuildCustomWitness(Context& context, SemIR::LocId loc_id,
enum class CoreInterface : std::int8_t {
Copy = 1 << 0,
Destroy = 1 << 1,
IntFitsIn = 1 << 2,

Unknown = -1,
};
Expand Down
16 changes: 8 additions & 8 deletions toolchain/check/testdata/array/bound_values.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ var b: array(1, 39999999999999999993);
// CHECK:STDOUT: %To.fe9: Core.IntLiteral = symbolic_binding To, 0 [symbolic]
// 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]
// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.979: %Core.IntLiteral.as.As.impl.Convert.type.2b1 = struct_value () [symbolic]
// CHECK:STDOUT: %From: Core.IntLiteral = symbolic_binding From, 0 [symbolic]
// CHECK:STDOUT: %From.fe9: Core.IntLiteral = symbolic_binding From, 0 [symbolic]
// CHECK:STDOUT: %As.impl_witness.e1d: <witness> = impl_witness imports.%As.impl_witness_table.7eb, @Core.IntLiteral.as.As.impl(%int_32) [concrete]
// 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]
// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.bbd: %Core.IntLiteral.as.As.impl.Convert.type.3e7 = struct_value () [concrete]
Expand All @@ -132,25 +132,25 @@ var b: array(1, 39999999999999999993);
// CHECK:STDOUT: %bound_method.d64: <bound method> = bound_method %int_3.1ba, %Core.IntLiteral.as.As.impl.Convert.specific_fn [concrete]
// CHECK:STDOUT: %int_3.d14: %u32 = int_value 3 [concrete]
// CHECK:STDOUT: %ImplicitAs.type.139: type = facet_type <@ImplicitAs, @ImplicitAs(Core.IntLiteral)> [concrete]
// CHECK:STDOUT: %UInt.as.ImplicitAs.impl.Convert.type.7f8: type = fn_type @UInt.as.ImplicitAs.impl.Convert, @UInt.as.ImplicitAs.impl(%From) [symbolic]
// 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]
// CHECK:STDOUT: %UInt.as.ImplicitAs.impl.Convert.0ea: %UInt.as.ImplicitAs.impl.Convert.type.7f8 = struct_value () [symbolic]
// CHECK:STDOUT: %ImplicitAs.impl_witness.895: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table.493, @UInt.as.ImplicitAs.impl(%int_32) [concrete]
// CHECK:STDOUT: %UInt.as.ImplicitAs.impl.Convert.type.543: type = fn_type @UInt.as.ImplicitAs.impl.Convert, @UInt.as.ImplicitAs.impl(%int_32) [concrete]
// CHECK:STDOUT: %ImplicitAs.impl_witness.895: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table.493, @UInt.as.ImplicitAs.impl.607(%int_32) [concrete]
// 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]
// CHECK:STDOUT: %UInt.as.ImplicitAs.impl.Convert.342: %UInt.as.ImplicitAs.impl.Convert.type.543 = struct_value () [concrete]
// CHECK:STDOUT: %ImplicitAs.facet.2c6: %ImplicitAs.type.139 = facet_value %u32, (%ImplicitAs.impl_witness.895) [concrete]
// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.70d: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(Core.IntLiteral, %ImplicitAs.facet.2c6) [concrete]
// CHECK:STDOUT: %.044: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.70d, %ImplicitAs.facet.2c6 [concrete]
// CHECK:STDOUT: %UInt.as.ImplicitAs.impl.Convert.bound: <bound method> = bound_method %int_3.d14, %UInt.as.ImplicitAs.impl.Convert.342 [concrete]
// 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]
// 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]
// CHECK:STDOUT: %bound_method.c86: <bound method> = bound_method %int_3.d14, %UInt.as.ImplicitAs.impl.Convert.specific_fn [concrete]
// CHECK:STDOUT: %array_type: type = array_type %int_3.1ba, %i32 [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// 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)]
// CHECK:STDOUT: %As.impl_witness_table.7eb = impl_witness_table (%Core.import_ref.600), @Core.IntLiteral.as.As.impl [concrete]
// 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)]
// CHECK:STDOUT: %ImplicitAs.impl_witness_table.493 = impl_witness_table (%Core.import_ref.483), @UInt.as.ImplicitAs.impl [concrete]
// 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)]
// CHECK:STDOUT: %ImplicitAs.impl_witness_table.493 = impl_witness_table (%Core.import_ref.483), @UInt.as.ImplicitAs.impl.607 [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
Expand All @@ -167,7 +167,7 @@ var b: array(1, 39999999999999999993);
// CHECK:STDOUT: %.loc6_18.2: %u32 = converted %int_3.loc6, %.loc6_18.1 [concrete = constants.%int_3.d14]
// 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]
// 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]
// 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]
// 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]
// CHECK:STDOUT: %bound_method.loc6_18.4: <bound method> = bound_method %.loc6_18.2, %specific_fn.loc6_18.2 [concrete = constants.%bound_method.c86]
// 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]
// CHECK:STDOUT: %.loc6_18.3: Core.IntLiteral = value_of_initializer %UInt.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_3.1ba]
Expand Down
Loading