| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758 |
- // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
- // Exceptions. See /LICENSE for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- #include "toolchain/check/custom_witness.h"
- #include "toolchain/base/kind_switch.h"
- #include "toolchain/check/facet_type.h"
- #include "toolchain/check/function.h"
- #include "toolchain/check/generic.h"
- #include "toolchain/check/impl.h"
- #include "toolchain/check/impl_lookup.h"
- #include "toolchain/check/import_ref.h"
- #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/associated_constant.h"
- #include "toolchain/sem_ir/builtin_function_kind.h"
- #include "toolchain/sem_ir/constant.h"
- #include "toolchain/sem_ir/ids.h"
- #include "toolchain/sem_ir/type_info.h"
- #include "toolchain/sem_ir/typed_insts.h"
- namespace Carbon::Check {
- // Given a value whose type `IsFacetTypeOrError`, returns the corresponding
- // type.
- static auto GetFacetAsType(Context& context,
- SemIR::ConstantId facet_or_type_const_id)
- -> SemIR::TypeId {
- auto facet_or_type_id =
- context.constant_values().GetInstId(facet_or_type_const_id);
- auto type_type_id = context.insts().Get(facet_or_type_id).type_id();
- CARBON_CHECK(context.types().IsFacetTypeOrError(type_type_id));
- if (context.types().Is<SemIR::FacetType>(type_type_id)) {
- // It's a facet; access its type.
- facet_or_type_id = context.types().GetTypeInstId(
- GetFacetAccessType(context, facet_or_type_id));
- }
- return context.types().GetTypeIdForTypeInstId(facet_or_type_id);
- }
- // Returns a manufactured `Copy.Op` function with the `self` parameter typed
- // to `self_type_id`.
- static auto MakeCopyOpFunction(Context& context, SemIR::LocId loc_id,
- SemIR::TypeId self_type_id,
- SemIR::NameScopeId parent_scope_id)
- -> SemIR::InstId {
- auto name_id = context.core_identifiers().AddNameId(CoreIdentifier::Op);
- auto [decl_id, function_id] =
- MakeGeneratedFunctionDecl(context, loc_id,
- {.parent_scope_id = parent_scope_id,
- .name_id = name_id,
- .self_type_id = self_type_id,
- .self_kind = ParamPatternKind::Value,
- .return_type_id = self_type_id});
- auto& function = context.functions().Get(function_id);
- function.SetCoreWitness(SemIR::BuiltinFunctionKind::PrimitiveCopy);
- return decl_id;
- }
- // Returns the body for `Destroy.Op`. This will return `None` if using the
- // builtin `NoOp` is appropriate.
- // Returns a FacetType that contains only the query interface.
- static auto GetFacetTypeForQuerySpecificInterface(
- Context& context, SemIR::LocId loc_id,
- SemIR::SpecificInterfaceId query_specific_interface_id)
- -> SemIR::ConstantId {
- const auto query_specific_interface =
- context.specific_interfaces().Get(query_specific_interface_id);
- // The Self facet will have type FacetType, for the query interface.
- auto const_id = EvalOrAddInst<SemIR::FacetType>(
- context, loc_id,
- FacetTypeFromInterface(context, query_specific_interface.interface_id,
- query_specific_interface.specific_id));
- return const_id;
- }
- // Starts a block for lookup-related instructions, and returns the `FacetType`
- // for lookups in `HasWitnessForRepeatedField`.
- static auto PrepareForHasWitness(
- Context& context, SemIR::LocId loc_id,
- SemIR::SpecificInterfaceId query_specific_interface_id)
- -> SemIR::ConstantId {
- context.inst_block_stack().Push();
- StartGenericDecl(context);
- return GetFacetTypeForQuerySpecificInterface(context, loc_id,
- query_specific_interface_id);
- }
- // Cleans up state `PrepareForHasWitness`.
- static auto CleanupAfterHasWitness(Context& context) -> void {
- DiscardGenericDecl(context);
- context.inst_block_stack().PopAndDiscard();
- }
- // Returns true if `type_inst_id` has a witness for the query interface, which
- // comes from `PrepareForHasWitness`.
- static auto HasWitnessForRepeatedField(
- Context& context, SemIR::LocId loc_id, SemIR::InstId type_inst_id,
- SemIR::ConstantId query_facet_type_const_id) -> bool {
- auto type_const_id = context.constant_values().Get(type_inst_id);
- auto block_or_err = LookupImplWitness(context, loc_id, type_const_id,
- query_facet_type_const_id);
- return block_or_err.has_value();
- }
- // The format for `Destroy.Op`.
- enum class DestroyFormat {
- NoDestroy,
- Trivial,
- NonTrivial,
- };
- // Similar to `HasWitnessForRepeatedField`, but for cases where there's only one
- // field, this can handle the call to `PrepareForHasWitness`.
- static auto HasWitnessForOneField(
- Context& context, SemIR::LocId loc_id, SemIR::InstId field_inst_id,
- SemIR::SpecificInterfaceId query_specific_interface_id) -> DestroyFormat {
- auto query_facet_type_const_id =
- PrepareForHasWitness(context, loc_id, query_specific_interface_id);
- auto has_witness = HasWitnessForRepeatedField(context, loc_id, field_inst_id,
- query_facet_type_const_id);
- CleanupAfterHasWitness(context);
- return has_witness ? DestroyFormat::NonTrivial : DestroyFormat::NoDestroy;
- }
- // Returns true if `class_type` should impl `Destroy`.
- static auto CanDestroyClass(
- Context& context, SemIR::LocId loc_id, SemIR::ClassType class_type,
- const SemIR::CompleteTypeInfo& complete_info,
- SemIR::SpecificInterfaceId query_specific_interface_id, bool is_partial)
- -> DestroyFormat {
- // Abstract classes can't be destroyed.
- if (!is_partial && complete_info.IsAbstract()) {
- return DestroyFormat::NoDestroy;
- }
- auto class_info = context.classes().Get(class_type.class_id);
- // `LookupCppImpl` handles C++ types.
- if (context.name_scopes().Get(class_info.scope_id).is_cpp_scope()) {
- return DestroyFormat::NoDestroy;
- }
- auto object_repr_id =
- class_info.GetObjectRepr(context.sem_ir(), class_type.specific_id);
- return HasWitnessForOneField(context, loc_id,
- context.types().GetTypeInstId(object_repr_id),
- query_specific_interface_id);
- }
- // Returns true if the `Self` should impl `Destroy`. This will recurse into impl
- // lookup of `Destroy` for members, similar to `where .Self.members each impls
- // Destroy`.
- static auto CanDestroyType(
- Context& context, SemIR::LocId loc_id,
- SemIR::ConstantId query_self_const_id,
- SemIR::SpecificInterfaceId query_specific_interface_id) -> DestroyFormat {
- auto query_specific_interface =
- context.specific_interfaces().Get(query_specific_interface_id);
- auto destroy_interface_id = query_specific_interface.interface_id;
- auto inst_id = context.constant_values().GetInstId(
- GetCanonicalFacetOrTypeValue(context, query_self_const_id));
- auto inst = context.insts().Get(inst_id);
- // For facet values, look if the FacetType provides the same.
- if (auto facet_type =
- context.types().TryGetAs<SemIR::FacetType>(inst.type_id())) {
- const auto& info = context.facet_types().Get(facet_type->facet_type_id);
- for (auto interface : info.extend_constraints) {
- if (interface.interface_id == destroy_interface_id) {
- return DestroyFormat::Trivial;
- }
- }
- return DestroyFormat::NoDestroy;
- }
- // Incomplete types can not be destroyed.
- auto type_id = context.types().GetTypeIdForTypeInstId(inst_id);
- if (!TryToCompleteType(context, type_id, loc_id)) {
- return DestroyFormat::NoDestroy;
- }
- CARBON_KIND_SWITCH(inst) {
- case SemIR::ImplWitnessAccess::Kind:
- case SemIR::SymbolicBinding::Kind: {
- // A symbolic facet of type `type`. Such symbolic values can't be
- // destroyed.
- return DestroyFormat::NoDestroy;
- }
- case CARBON_KIND(SemIR::ArrayType array_type): {
- // A zero element array is always trivially destructible.
- if (auto int_bound =
- context.sem_ir().GetZExtIntValue(array_type.bound_id);
- !int_bound || *int_bound == 0) {
- return DestroyFormat::Trivial;
- }
- // Verify the element can be destroyed.
- return HasWitnessForOneField(context, loc_id,
- array_type.element_type_inst_id,
- query_specific_interface_id);
- }
- case SemIR::Call::Kind:
- // TODO: These seem like they shouldn't be getting directly queried for
- // destroy. The use is in a test that was TODO before this TODO.
- return DestroyFormat::NoDestroy;
- case CARBON_KIND(SemIR::ClassType class_type): {
- return CanDestroyClass(context, loc_id, class_type,
- context.types().GetCompleteTypeInfo(type_id),
- query_specific_interface_id,
- /*is_partial=*/false);
- }
- case CARBON_KIND(SemIR::ConstType const_type): {
- return HasWitnessForOneField(context, loc_id, const_type.inner_id,
- query_specific_interface_id);
- }
- case CARBON_KIND(SemIR::MaybeUnformedType maybe_unformed_type): {
- return HasWitnessForOneField(context, loc_id,
- maybe_unformed_type.inner_id,
- query_specific_interface_id);
- }
- case CARBON_KIND(SemIR::PartialType partial_type): {
- // In contrast with something like `const`, need to treat the inner
- // class differently based on the `partial` modifier.
- auto class_type =
- context.insts().GetAs<SemIR::ClassType>(partial_type.inner_id);
- return CanDestroyClass(context, loc_id, class_type,
- context.types().GetCompleteTypeInfo(type_id),
- query_specific_interface_id,
- /*is_partial=*/true);
- }
- case CARBON_KIND(SemIR::StructType struct_type): {
- auto fields = context.struct_type_fields().Get(struct_type.fields_id);
- if (fields.empty()) {
- return DestroyFormat::Trivial;
- }
- auto query_facet_type_const_id =
- PrepareForHasWitness(context, loc_id, query_specific_interface_id);
- bool has_witness = true;
- for (const auto& field : fields) {
- if (!HasWitnessForRepeatedField(context, loc_id, field.type_inst_id,
- query_facet_type_const_id)) {
- has_witness = false;
- break;
- }
- }
- CleanupAfterHasWitness(context);
- return has_witness ? DestroyFormat::NonTrivial : DestroyFormat::NoDestroy;
- }
- case CARBON_KIND(SemIR::TupleType tuple_type): {
- auto block = context.inst_blocks().Get(tuple_type.type_elements_id);
- if (block.empty()) {
- return DestroyFormat::Trivial;
- }
- auto query_facet_type_const_id =
- PrepareForHasWitness(context, loc_id, query_specific_interface_id);
- bool has_witness = true;
- for (const auto& element_id : block) {
- if (!HasWitnessForRepeatedField(context, loc_id, element_id,
- query_facet_type_const_id)) {
- has_witness = false;
- break;
- }
- }
- CleanupAfterHasWitness(context);
- return has_witness ? DestroyFormat::NonTrivial : DestroyFormat::NoDestroy;
- }
- case SemIR::BoolType::Kind:
- case SemIR::FacetType::Kind:
- case SemIR::FloatType::Kind:
- case SemIR::IntLiteralType::Kind:
- case SemIR::IntType::Kind:
- case SemIR::PointerType::Kind:
- case SemIR::TypeType::Kind:
- // Trivially destructible.
- return DestroyFormat::Trivial;
- default:
- CARBON_FATAL("Unexpected type for CanDestroyType: {0}", inst.kind());
- }
- }
- // Returns the body for `Destroy.Op`.
- //
- // TODO: This is a placeholder still not actually destroying things, intended to
- // maintain mostly-consistent behavior with current logic while working. That
- // also means using `self`.
- static auto MakeDestroyOpBody(Context& context, SemIR::LocId loc_id,
- SemIR::TypeId self_type_id,
- SemIR::InstId self_param_id)
- -> SemIR::InstBlockId {
- context.inst_block_stack().Push();
- auto inst = context.types().GetAsInst(self_type_id);
- CARBON_KIND_SWITCH(inst) {
- case SemIR::ArrayType::Kind:
- case SemIR::ClassType::Kind:
- case SemIR::ConstType::Kind:
- case SemIR::MaybeUnformedType::Kind:
- case SemIR::PartialType::Kind:
- case SemIR::StructType::Kind:
- case SemIR::TupleType::Kind:
- (void)self_param_id;
- // TODO: Implement destruction of the type.
- break;
- default:
- CARBON_FATAL("Unexpected type for MakeDestroyOpBody: {0}", inst);
- }
- AddInst(context, loc_id, SemIR::Return{});
- return context.inst_block_stack().Pop();
- }
- // Returns a manufactured `Destroy.Op` function with the `self` parameter typed
- // to `self_type_id`.
- static auto MakeDestroyOpFunction(Context& context, SemIR::LocId loc_id,
- SemIR::TypeId self_type_id,
- SemIR::NameScopeId parent_scope_id,
- DestroyFormat format) -> SemIR::InstId {
- auto name_id = context.core_identifiers().AddNameId(CoreIdentifier::Op);
- auto [decl_id, function_id] =
- MakeGeneratedFunctionDecl(context, loc_id,
- {.parent_scope_id = parent_scope_id,
- .name_id = name_id,
- .self_type_id = self_type_id,
- .self_kind = ParamPatternKind::Ref});
- auto& function = context.functions().Get(function_id);
- if (format == DestroyFormat::Trivial) {
- function.SetCoreWitness(SemIR::BuiltinFunctionKind::NoOp);
- } else {
- CARBON_CHECK(format == DestroyFormat::NonTrivial);
- function.SetCoreWitness(SemIR::BuiltinFunctionKind::None);
- auto body_id = MakeDestroyOpBody(context, loc_id, self_type_id,
- function.self_param_id);
- function.body_block_ids.push_back(body_id);
- }
- return decl_id;
- }
- static auto MakeCustomWitnessConstantInst(
- Context& context, SemIR::LocId loc_id,
- SemIR::SpecificInterfaceId query_specific_interface_id,
- SemIR::InstBlockId associated_entities_block_id) -> SemIR::InstId {
- // The witness is a CustomWitness of the query interface with a table that
- // contains each associated entity.
- auto const_id = EvalOrAddInst<SemIR::CustomWitness>(
- context, loc_id,
- {.type_id = GetSingletonType(context, SemIR::WitnessType::TypeInstId),
- .elements_id = associated_entities_block_id,
- .query_specific_interface_id = query_specific_interface_id});
- return context.constant_values().GetInstId(const_id);
- }
- struct TypesForSelfFacet {
- // A FacetType that contains only the query interface.
- SemIR::TypeId facet_type_for_query_specific_interface;
- // The query self as a type, which involves a conversion if it was a facet.
- SemIR::TypeId query_self_as_type_id;
- };
- static auto GetTypesForSelfFacet(
- Context& context, SemIR::LocId loc_id,
- SemIR::ConstantId query_self_const_id,
- SemIR::SpecificInterfaceId query_specific_interface_id)
- -> TypesForSelfFacet {
- // The Self facet will have type FacetType, for the query interface.
- auto facet_type_for_query_specific_interface =
- context.types().GetTypeIdForTypeConstantId(
- GetFacetTypeForQuerySpecificInterface(context, loc_id,
- query_specific_interface_id));
- // The Self facet needs to point to a type value. If it's not one already,
- // convert to type.
- auto query_self_as_type_id = GetFacetAsType(context, query_self_const_id);
- return {facet_type_for_query_specific_interface, query_self_as_type_id};
- }
- // Build a new facet from the query self, using a CustomWitness for the query
- // interface with an entry for each associated entity so far.
- static auto MakeSelfFacetWithCustomWitness(
- Context& context, SemIR::LocId loc_id, TypesForSelfFacet query_types,
- SemIR::SpecificInterfaceId query_specific_interface_id,
- SemIR::InstBlockId associated_entities_block_id) -> SemIR::ConstantId {
- // We are building a facet value for a single interface, so the witness block
- // is a single witness for that interface.
- auto witnesses_block_id =
- context.inst_blocks().Add({MakeCustomWitnessConstantInst(
- context, loc_id, query_specific_interface_id,
- associated_entities_block_id)});
- return EvalOrAddInst<SemIR::FacetValue>(
- context, loc_id,
- {.type_id = query_types.facet_type_for_query_specific_interface,
- .type_inst_id =
- context.types().GetTypeInstId(query_types.query_self_as_type_id),
- .witnesses_block_id = witnesses_block_id});
- }
- auto BuildCustomWitness(Context& context, SemIR::LocId loc_id,
- SemIR::ConstantId query_self_const_id,
- SemIR::SpecificInterfaceId query_specific_interface_id,
- llvm::ArrayRef<SemIR::InstId> values) -> SemIR::InstId {
- const auto query_specific_interface =
- context.specific_interfaces().Get(query_specific_interface_id);
- const auto& interface =
- context.interfaces().Get(query_specific_interface.interface_id);
- auto assoc_entities =
- context.inst_blocks().GetOrEmpty(interface.associated_entities_id);
- if (assoc_entities.size() != values.size()) {
- context.TODO(loc_id, ("Unsupported definition of interface " +
- context.names().GetFormatted(interface.name_id))
- .str());
- return SemIR::ErrorInst::InstId;
- }
- auto query_types_for_self_facet = GetTypesForSelfFacet(
- context, loc_id, query_self_const_id, query_specific_interface_id);
- // The values that will go in the witness table.
- llvm::SmallVector<SemIR::InstId> entries;
- auto interface_with_self_specific_id = SemIR::SpecificId::None;
- enum class AssociatedEntityState {
- None,
- AssociatedConstant,
- AssociatedFunction
- };
- auto associated_entity_state = AssociatedEntityState::None;
- // Build a witness with the current contents of the witness table. Each
- // specific interface has at most two witness tables: an optional table
- // for associated constants and a table for associated functions.
- //
- // We need to separate associated constants and associated functions since
- // associated entities can't depend on other entities in their own witness
- // table. To ensure that we don't end up with n^2 witness tables, and so
- // that we don't need to loop over the associated entities more than once,
- // we require that interfaces with a custom witness specify all of their
- // associated constants before their associated functions. Interfaces with
- // custom witness tables are hardcoded in the compiler, so this
- // restriction doesn't impact whether or not a type is definable.
- auto update_interface_with_self_specific_id =
- [&](SemIR::InstId assoc_entity_id) {
- auto decl_id =
- context.constant_values().GetConstantInstId(assoc_entity_id);
- CARBON_CHECK(decl_id.has_value(), "Non-constant associated entity");
- auto decl = context.insts().Get(decl_id);
- auto new_associated_entity_state =
- decl.kind() == SemIR::AssociatedConstantDecl::Kind
- ? AssociatedEntityState::AssociatedConstant
- : AssociatedEntityState::AssociatedFunction;
- CARBON_CHECK(new_associated_entity_state >= associated_entity_state,
- "Implementation restriction: associated constants must be "
- "defined before associated functions");
- if (associated_entity_state < new_associated_entity_state) {
- auto self_facet = MakeSelfFacetWithCustomWitness(
- context, loc_id, query_types_for_self_facet,
- query_specific_interface_id, context.inst_blocks().Add(entries));
- interface_with_self_specific_id = MakeSpecificWithInnerSelf(
- context, loc_id, interface.generic_id,
- interface.generic_with_self_id,
- query_specific_interface.specific_id, self_facet);
- associated_entity_state = new_associated_entity_state;
- }
- };
- // Fill in the witness table.
- for (const auto& [assoc_entity_id, value_id] :
- llvm::zip_equal(assoc_entities, values)) {
- LoadImportRef(context, assoc_entity_id);
- update_interface_with_self_specific_id(assoc_entity_id);
- CARBON_CHECK(
- !context.specifics().Get(interface_with_self_specific_id).HasError());
- auto decl_id =
- context.constant_values().GetInstId(SemIR::GetConstantValueInSpecific(
- context.sem_ir(), interface_with_self_specific_id,
- assoc_entity_id));
- CARBON_CHECK(decl_id.has_value(), "Non-constant associated entity");
- auto decl = context.insts().Get(decl_id);
- CARBON_KIND_SWITCH(decl) {
- case CARBON_KIND(SemIR::StructValue struct_value): {
- if (struct_value.type_id == SemIR::ErrorInst::TypeId) {
- return SemIR::ErrorInst::InstId;
- }
- // TODO: If a thunk is needed, this will build a different value each
- // time it's called, so we won't properly deduplicate repeated
- // witnesses.
- entries.push_back(CheckAssociatedFunctionImplementation(
- context,
- context.types().GetAs<SemIR::FunctionType>(struct_value.type_id),
- query_specific_interface.specific_id, value_id,
- /*defer_thunk_definition=*/false));
- break;
- }
- case CARBON_KIND(SemIR::AssociatedConstantDecl decl): {
- if (decl.type_id == SemIR::ErrorInst::TypeId) {
- return SemIR::ErrorInst::InstId;
- }
- // TODO: remove once we have a test-case for all associated constants.
- // Special-case the ones we want to support in this if-statement, until
- // we're able to account for everything.
- if (decl.type_id != SemIR::TypeType::TypeId) {
- context.TODO(loc_id,
- "Associated constant of type other than `TypeType` in "
- "synthesized impl");
- return SemIR::ErrorInst::InstId;
- }
- auto type_id = context.insts().Get(value_id).type_id();
- CARBON_CHECK(type_id == SemIR::TypeType::TypeId ||
- type_id == SemIR::ErrorInst::TypeId);
- auto impl_witness_associated_constant =
- AddInst<SemIR::ImplWitnessAssociatedConstant>(
- context, loc_id, {.type_id = type_id, .inst_id = value_id});
- entries.push_back(impl_witness_associated_constant);
- break;
- }
- default:
- CARBON_CHECK(decl_id == SemIR::ErrorInst::InstId,
- "Unexpected kind of associated entity {0}", decl);
- return SemIR::ErrorInst::InstId;
- }
- }
- return MakeCustomWitnessConstantInst(context, loc_id,
- query_specific_interface_id,
- context.inst_blocks().Add(entries));
- }
- auto AsCoreIdentifier(SemIR::CoreInterface core_interface) -> CoreIdentifier {
- using SemIR::CoreInterface;
- switch (core_interface) {
- case CoreInterface::AddAssignWith:
- return CoreIdentifier::AddAssignWith;
- case CoreInterface::AddWith:
- return CoreIdentifier::AddWith;
- case CoreInterface::Copy:
- return CoreIdentifier::Copy;
- case CoreInterface::CppUnsafeDeref:
- return CoreIdentifier::CppUnsafeDeref;
- case CoreInterface::Dec:
- return CoreIdentifier::Dec;
- case CoreInterface::Default:
- return CoreIdentifier::Default;
- case CoreInterface::Destroy:
- return CoreIdentifier::Destroy;
- case CoreInterface::DivAssignWith:
- return CoreIdentifier::DivAssignWith;
- case CoreInterface::DivWith:
- return CoreIdentifier::DivWith;
- case CoreInterface::Inc:
- return CoreIdentifier::Inc;
- case CoreInterface::IntFitsIn:
- return CoreIdentifier::IntFitsIn;
- case CoreInterface::ModAssignWith:
- return CoreIdentifier::ModAssignWith;
- case CoreInterface::ModWith:
- return CoreIdentifier::ModWith;
- case CoreInterface::MulAssignWith:
- return CoreIdentifier::MulAssignWith;
- case CoreInterface::MulWith:
- return CoreIdentifier::MulWith;
- case CoreInterface::Negate:
- return CoreIdentifier::Negate;
- case CoreInterface::SubAssignWith:
- return CoreIdentifier::SubAssignWith;
- case CoreInterface::SubWith:
- return CoreIdentifier::SubWith;
- case CoreInterface::Unknown:
- CARBON_FATAL("{0} doesn't have a `CoreIdentifier` mapping",
- core_interface);
- }
- }
- auto GetCoreInterface(Context& context, SemIR::InterfaceId interface_id)
- -> SemIR::CoreInterface {
- const auto& interface = context.interfaces().Get(interface_id);
- if (!context.name_scopes().IsCorePackage(interface.parent_scope_id) ||
- !interface.name_id.AsIdentifierId().has_value()) {
- return SemIR::CoreInterface::Unknown;
- }
- return interface.core_interface;
- }
- auto BuildPrimitiveCopyWitness(
- Context& context, SemIR::LocId loc_id, SemIR::NameScopeId parent_scope_id,
- SemIR::ConstantId query_self_const_id,
- SemIR::SpecificInterfaceId query_specific_interface_id) -> SemIR::InstId {
- auto self_type_id = GetFacetAsType(context, query_self_const_id);
- auto op_id =
- MakeCopyOpFunction(context, loc_id, self_type_id, parent_scope_id);
- return BuildCustomWitness(context, loc_id, query_self_const_id,
- query_specific_interface_id, {op_id});
- }
- static auto MakeDestroyWitness(
- Context& context, SemIR::LocId loc_id,
- SemIR::ConstantId query_self_const_id,
- SemIR::SpecificInterfaceId query_specific_interface_id, bool build_witness)
- -> std::optional<SemIR::InstId> {
- auto format = CanDestroyType(context, loc_id, query_self_const_id,
- query_specific_interface_id);
- if (format == DestroyFormat::NoDestroy) {
- return std::nullopt;
- }
- if (!build_witness || query_self_const_id.is_symbolic()) {
- // The type can be destroyed, but we shouldn't make a witness right now.
- return SemIR::InstId::None;
- }
- // Mark functions with the interface's scope as a hint to mangling. This
- // does not add them to the scope.
- auto query_specific_interface =
- context.specific_interfaces().Get(query_specific_interface_id);
- auto parent_scope_id = context.interfaces()
- .Get(query_specific_interface.interface_id)
- .scope_without_self_id;
- auto self_type_id = GetFacetAsType(context, query_self_const_id);
- auto op_id = MakeDestroyOpFunction(context, loc_id, self_type_id,
- parent_scope_id, format);
- return BuildCustomWitness(context, loc_id, query_self_const_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, bool build_witness)
- -> 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;
- }
- if (!build_witness) {
- return SemIR::InstId::None;
- }
- return BuildCustomWitness(context, loc_id, query_self_const_id,
- query_specific_interface_id, {});
- }
- auto LookupCustomWitness(Context& context, SemIR::LocId loc_id,
- SemIR::CoreInterface core_interface,
- SemIR::ConstantId query_self_const_id,
- SemIR::SpecificInterfaceId query_specific_interface_id,
- bool build_witness) -> std::optional<SemIR::InstId> {
- switch (core_interface) {
- case SemIR::CoreInterface::Destroy:
- return MakeDestroyWitness(context, loc_id, query_self_const_id,
- query_specific_interface_id, build_witness);
- case SemIR::CoreInterface::IntFitsIn:
- return MakeIntFitsInWitness(context, loc_id, query_self_const_id,
- query_specific_interface_id, build_witness);
- case SemIR::CoreInterface::AddAssignWith:
- case SemIR::CoreInterface::AddWith:
- case SemIR::CoreInterface::Copy:
- case SemIR::CoreInterface::CppUnsafeDeref:
- case SemIR::CoreInterface::Dec:
- case SemIR::CoreInterface::Default:
- case SemIR::CoreInterface::DivAssignWith:
- case SemIR::CoreInterface::DivWith:
- case SemIR::CoreInterface::Inc:
- case SemIR::CoreInterface::ModAssignWith:
- case SemIR::CoreInterface::ModWith:
- case SemIR::CoreInterface::MulAssignWith:
- case SemIR::CoreInterface::MulWith:
- case SemIR::CoreInterface::Negate:
- case SemIR::CoreInterface::SubAssignWith:
- case SemIR::CoreInterface::SubWith:
- case SemIR::CoreInterface::Unknown:
- // TODO: Handle more interfaces, particularly copy, move, and conversion.
- return std::nullopt;
- }
- }
- } // namespace Carbon::Check
|