mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-11-10 12:28:25 +00:00
`IdentifiedFacetType` is a concept that is only used by check. It does not appear in the semir output, unlike `DeclaredFacetType`s. `IdentifiedFacetType` is used in check to collapse named constraints together, building a single structure of requirements from a `DeclaredFacetType` and any referenced named constraints. This is used for operations like impl lookup, and building `FacetValue`s. The outputs of `IdentifiedFacetType` are now in an intermediate state, where they contain `IdentifiedWitness`es, and need to be processed before they are used. We'd like to build that into the API of `IdentifiedFacetType` so that their use is more robust. But the `IdentifiedWitnesses` need to be replaced by subst, which is a part of check, so we need to move `IdentifiedFacetType` to check in order to build the type on top of subst.
1064 lines
45 KiB
C++
1064 lines
45 KiB
C++
// 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/impl.h"
|
|
|
|
#include "toolchain/base/kind_switch.h"
|
|
#include "toolchain/check/context.h"
|
|
#include "toolchain/check/convert.h"
|
|
#include "toolchain/check/deduce.h"
|
|
#include "toolchain/check/eval.h"
|
|
#include "toolchain/check/facet_type.h"
|
|
#include "toolchain/check/function.h"
|
|
#include "toolchain/check/generic.h"
|
|
#include "toolchain/check/identified_facet_type.h"
|
|
#include "toolchain/check/impl_lookup.h"
|
|
#include "toolchain/check/import_ref.h"
|
|
#include "toolchain/check/inst.h"
|
|
#include "toolchain/check/interface.h"
|
|
#include "toolchain/check/merge.h"
|
|
#include "toolchain/check/name_lookup.h"
|
|
#include "toolchain/check/name_scope.h"
|
|
#include "toolchain/check/period_self.h"
|
|
#include "toolchain/check/subst.h"
|
|
#include "toolchain/check/thunk.h"
|
|
#include "toolchain/check/type.h"
|
|
#include "toolchain/check/type_completion.h"
|
|
#include "toolchain/check/type_structure.h"
|
|
#include "toolchain/diagnostics/emitter.h"
|
|
#include "toolchain/sem_ir/generic.h"
|
|
#include "toolchain/sem_ir/ids.h"
|
|
#include "toolchain/sem_ir/impl.h"
|
|
#include "toolchain/sem_ir/inst.h"
|
|
#include "toolchain/sem_ir/specific_interface.h"
|
|
#include "toolchain/sem_ir/typed_insts.h"
|
|
|
|
namespace Carbon::Check {
|
|
|
|
// Adds the location of the associated function to a diagnostic.
|
|
static auto NoteAssociatedFunction(Context& context, DiagnosticBuilder& builder,
|
|
SemIR::FunctionId function_id) -> void {
|
|
CARBON_DIAGNOSTIC_LABEL(AssociatedFunctionHere, Info,
|
|
"associated function {0} declared here",
|
|
SemIR::NameId);
|
|
const auto& function = context.functions().Get(function_id);
|
|
builder.Attach(function.latest_decl_id(), AssociatedFunctionHere,
|
|
function.name_id);
|
|
}
|
|
|
|
auto CheckAssociatedFunctionImplementation(
|
|
Context& context, SemIR::FunctionType interface_function_type,
|
|
SemIR::SpecificId enclosing_specific_id, SemIR::InstId impl_decl_id,
|
|
bool defer_thunk_definition) -> SemIR::InstId {
|
|
auto impl_function_decl = context.insts().Get(impl_decl_id).type_id();
|
|
if (!context.types().Is<SemIR::FunctionType>(impl_function_decl)) {
|
|
if (impl_decl_id != SemIR::ErrorInst::InstId) {
|
|
CARBON_DIAGNOSTIC(ImplFunctionWithNonFunction, Error,
|
|
"associated function {0} implemented by non-function",
|
|
SemIR::NameId);
|
|
auto builder = context.emitter().Build(
|
|
impl_decl_id, ImplFunctionWithNonFunction,
|
|
context.functions().Get(interface_function_type.function_id).name_id);
|
|
NoteAssociatedFunction(context, builder,
|
|
interface_function_type.function_id);
|
|
builder.Emit();
|
|
}
|
|
|
|
return SemIR::ErrorInst::InstId;
|
|
}
|
|
|
|
// Map from the specific for the function type to the specific for the
|
|
// function signature. The function signature may have additional generic
|
|
// parameters.
|
|
auto interface_function_specific_id =
|
|
GetSelfSpecificForInterfaceMemberWithSelfType(
|
|
context, SemIR::LocId(impl_decl_id),
|
|
interface_function_type.specific_id,
|
|
context.functions()
|
|
.Get(interface_function_type.function_id)
|
|
.generic_id,
|
|
enclosing_specific_id);
|
|
|
|
return BuildThunk(context, interface_function_type.function_id,
|
|
interface_function_specific_id,
|
|
/*override_self_type_id=*/SemIR::TypeId::None, impl_decl_id,
|
|
defer_thunk_definition);
|
|
}
|
|
|
|
static auto GetScopeInstId(Context& context, SemIR::InstId scope_inst_id)
|
|
-> SemIR::InstId {
|
|
if (!scope_inst_id.has_value()) {
|
|
return SemIR::InstId::None;
|
|
}
|
|
auto inst_id = context.constant_values().GetConstantInstId(scope_inst_id);
|
|
if (auto struct_val = context.insts().TryGetAs<SemIR::StructValue>(inst_id)) {
|
|
inst_id = context.types().GetTypeInstId(struct_val->type_id);
|
|
}
|
|
return inst_id;
|
|
}
|
|
|
|
enum class ImplRedeclType {
|
|
ValidRedecl,
|
|
Mismatch,
|
|
DiagnosedInvalidRedecl,
|
|
};
|
|
|
|
// Returns whether the scope of the `new_impl` is the same as the scope of the
|
|
// `prev_impl`. If the `new_impl` is in an invalid scope for a redecl, that is
|
|
// diagnosed.
|
|
static auto ScopesMatch(Context& context, const SemIR::Impl& new_impl,
|
|
const SemIR::Impl& prev_impl) -> ImplRedeclType {
|
|
auto new_id = GetScopeInstId(context, new_impl.parent_scope_inst_id);
|
|
auto prev_id = GetScopeInstId(context, prev_impl.parent_scope_inst_id);
|
|
if (new_id.has_value()) {
|
|
auto new_scope_inst = context.insts().Get(new_id);
|
|
CARBON_KIND_SWITCH(new_scope_inst) {
|
|
case CARBON_KIND(SemIR::ClassType new_scope): {
|
|
if (auto prev_scope =
|
|
context.insts().TryGetAs<SemIR::ClassType>(prev_id)) {
|
|
if (new_scope.class_id == prev_scope->class_id) {
|
|
return ImplRedeclType::ValidRedecl;
|
|
}
|
|
}
|
|
return ImplRedeclType::Mismatch;
|
|
}
|
|
case CARBON_KIND(SemIR::ParameterizedClassType new_scope): {
|
|
if (auto prev_scope =
|
|
context.insts().TryGetAs<SemIR::ParameterizedClassType>(
|
|
prev_id)) {
|
|
if (new_scope.class_id == prev_scope->class_id) {
|
|
return ImplRedeclType::ValidRedecl;
|
|
}
|
|
}
|
|
return ImplRedeclType::Mismatch;
|
|
}
|
|
case CARBON_KIND(SemIR::Namespace new_scope): {
|
|
if (auto prev_scope =
|
|
context.insts().TryGetAs<SemIR::Namespace>(prev_id)) {
|
|
if (new_scope.name_scope_id == prev_scope->name_scope_id) {
|
|
return ImplRedeclType::ValidRedecl;
|
|
}
|
|
}
|
|
return ImplRedeclType::Mismatch;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// The redecl is is an invalid scope.
|
|
CARBON_DIAGNOSTIC(ImplDeclInInvalidScope, Error,
|
|
"impl redeclaration not in a declarative scope; "
|
|
"redeclaration is allowed only in a class or namespace");
|
|
context.emitter().Emit(new_impl.latest_decl_id(), ImplDeclInInvalidScope);
|
|
return ImplRedeclType::DiagnosedInvalidRedecl;
|
|
}
|
|
|
|
// Returns true if impl redeclaration parameters and scopes match.
|
|
//
|
|
// TODO: Generalize things to validate re-declarations of other entity types,
|
|
// which also have some similar rules such as sharing scopes.
|
|
static auto VerifyImplRedecl(Context& context, const SemIR::Impl& new_impl,
|
|
const SemIR::Impl& prev_impl) -> ImplRedeclType {
|
|
// If the parameters aren't the same, then this is not a redeclaration of this
|
|
// `impl`. Keep looking for a prior declaration without issuing a diagnostic.
|
|
if (!CheckRedeclParamsMatch(context, DeclParams(new_impl),
|
|
DeclParams(prev_impl), SemIR::SpecificId::None,
|
|
/*diagnose=*/false, /*check_syntax=*/true)) {
|
|
return ImplRedeclType::Mismatch;
|
|
}
|
|
|
|
// If the scopes are different, it is not treated as a redeclaration.
|
|
if (auto scope_result = ScopesMatch(context, new_impl, prev_impl);
|
|
scope_result != ImplRedeclType::ValidRedecl) {
|
|
return scope_result;
|
|
}
|
|
|
|
// Following #4672, disallowing defining non-extern declarations in another
|
|
// file.
|
|
if (auto import_ref =
|
|
context.insts().TryGetAs<SemIR::AnyImportRef>(prev_impl.self_id)) {
|
|
// TODO: Handle extern.
|
|
CARBON_DIAGNOSTIC(RedeclImportedImpl, Error,
|
|
"redeclaration of imported impl");
|
|
// TODO: Note imported declaration
|
|
context.emitter().Emit(new_impl.latest_decl_id(), RedeclImportedImpl);
|
|
return ImplRedeclType::DiagnosedInvalidRedecl;
|
|
}
|
|
|
|
if (new_impl.has_definition_started() && prev_impl.has_definition_started()) {
|
|
// Impls aren't merged in order to avoid generic region lookup into a
|
|
// mismatching table.
|
|
CARBON_DIAGNOSTIC(ImplRedefinition, Error,
|
|
"redefinition of `impl {0} as {1}`", InstIdAsRawType,
|
|
InstIdAsRawType);
|
|
CARBON_DIAGNOSTIC_LABEL(ImplPreviousDefinition, Info,
|
|
"previous definition was here");
|
|
context.emitter()
|
|
.Build(new_impl.latest_decl_id(), ImplRedefinition, new_impl.self_id,
|
|
new_impl.constraint_id)
|
|
.Attach(prev_impl.definition_id, ImplPreviousDefinition)
|
|
.Emit();
|
|
return ImplRedeclType::DiagnosedInvalidRedecl;
|
|
}
|
|
|
|
return ImplRedeclType::ValidRedecl;
|
|
}
|
|
|
|
// Looks for any unused generic bindings. If one is found, it is diagnosed and
|
|
// false is returned.
|
|
static auto VerifyAllGenericBindingsUsed(Context& context, SemIR::LocId loc_id,
|
|
SemIR::LocId implicit_params_loc_id,
|
|
SemIR::ImplId impl_id,
|
|
SemIR::Impl& impl) -> bool {
|
|
if (impl.witness_id == SemIR::ErrorInst::InstId) {
|
|
return true;
|
|
}
|
|
if (!impl.generic_id.has_value()) {
|
|
return true;
|
|
}
|
|
|
|
if (impl.implicit_param_patterns_id.has_value()) {
|
|
for (auto inst_id :
|
|
context.inst_blocks().Get(impl.implicit_param_patterns_id)) {
|
|
if (inst_id == SemIR::ErrorInst::InstId) {
|
|
// An error was already diagnosed for a generic binding.
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
auto deduced_specific_id = DeduceImplArguments(
|
|
context, loc_id, impl_id, context.constant_values().Get(impl.self_id),
|
|
impl.interface.specific_id);
|
|
if (deduced_specific_id.has_value()) {
|
|
// Deduction succeeded, all bindings were used.
|
|
return true;
|
|
}
|
|
|
|
CARBON_DIAGNOSTIC(ImplUnusedBinding, Error,
|
|
"`impl` with unused generic binding");
|
|
// TODO: This location may be incorrect, the binding may be inherited
|
|
// from an outer declaration. It would be nice to get the particular
|
|
// binding that was undeducible back from DeduceImplArguments here and
|
|
// use that.
|
|
auto diag_loc_id =
|
|
implicit_params_loc_id.has_value() ? implicit_params_loc_id : loc_id;
|
|
context.emitter().Emit(diag_loc_id, ImplUnusedBinding);
|
|
return false;
|
|
}
|
|
|
|
// Apply an `extend impl` declaration by extending the parent scope with the
|
|
// `impl`. If there's an error it is diagnosed and false is returned.
|
|
static auto ApplyExtendImplAs(Context& context, SemIR::LocId loc_id,
|
|
const SemIR::Impl& impl,
|
|
Parse::NodeId extend_node,
|
|
SemIR::LocId implicit_params_loc_id) -> bool {
|
|
auto parent_scope_id = context.decl_name_stack().PeekParentScopeId();
|
|
|
|
// TODO: Also handle the parent scope being a mixin.
|
|
auto class_scope = TryAsClassScope(context, parent_scope_id);
|
|
if (!class_scope) {
|
|
if (impl.witness_id != SemIR::ErrorInst::InstId) {
|
|
CARBON_DIAGNOSTIC(
|
|
ExtendImplOutsideClass, Error,
|
|
"`extend impl` can only be used in an interface or class");
|
|
context.emitter().Emit(loc_id, ExtendImplOutsideClass);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
auto& parent_scope = *class_scope->name_scope;
|
|
|
|
// An error was already diagnosed, but this is `extend impl as` inside a
|
|
// class, so propagate the error into the enclosing class scope.
|
|
if (impl.witness_id == SemIR::ErrorInst::InstId) {
|
|
parent_scope.set_has_error();
|
|
return false;
|
|
}
|
|
|
|
if (implicit_params_loc_id.has_value()) {
|
|
CARBON_DIAGNOSTIC(ExtendImplForall, Error,
|
|
"cannot `extend` a parameterized `impl`");
|
|
context.emitter().Emit(extend_node, ExtendImplForall);
|
|
parent_scope.set_has_error();
|
|
return false;
|
|
}
|
|
|
|
if (!RequireCompleteType(
|
|
context, context.types().GetTypeIdForTypeInstId(impl.constraint_id),
|
|
SemIR::LocId(impl.constraint_id), [&](auto& builder) {
|
|
CARBON_DIAGNOSTIC_CONTEXT(
|
|
ExtendImplAsIncomplete,
|
|
"`extend impl as` incomplete facet type {0}", InstIdAsType);
|
|
builder.Attach(impl.latest_decl_id(), ExtendImplAsIncomplete,
|
|
impl.constraint_id);
|
|
})) {
|
|
parent_scope.set_has_error();
|
|
return false;
|
|
}
|
|
|
|
if (!impl.generic_id.has_value()) {
|
|
parent_scope.AddExtendedScope(impl.constraint_id);
|
|
} else {
|
|
// The extended scope instruction must be part of the enclosing scope (and
|
|
// generic). A specific for the enclosing scope will be applied to it when
|
|
// using the instruction later. To do so, we wrap the constraint facet type
|
|
// it in a SpecificConstant, which preserves the impl declaration's
|
|
// specific along with the facet type.
|
|
auto constraint_id_in_self_specific = AddTypeInst<SemIR::SpecificConstant>(
|
|
context, SemIR::LocId(impl.constraint_id),
|
|
{.type_id = SemIR::TypeType::TypeId,
|
|
.inst_id = impl.constraint_id,
|
|
.specific_id = context.generics().GetSelfSpecific(impl.generic_id)});
|
|
parent_scope.AddExtendedScope(constraint_id_in_self_specific);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
auto FindImplId(Context& context, const SemIR::Impl& query_impl)
|
|
-> std::variant<RedeclaredImpl, NewImpl> {
|
|
// Look for an existing matching declaration.
|
|
auto lookup_bucket_ref = context.impls().GetOrAddLookupBucket(query_impl);
|
|
// TODO: Detect two impl declarations with the same self type and interface,
|
|
// and issue an error if they don't match.
|
|
for (auto prev_impl_id : lookup_bucket_ref) {
|
|
auto& prev_impl = context.impls().Get(prev_impl_id);
|
|
|
|
auto redecl_type = VerifyImplRedecl(context, query_impl, prev_impl);
|
|
switch (redecl_type) {
|
|
case ImplRedeclType::ValidRedecl:
|
|
// Found a valid redecl.
|
|
return RedeclaredImpl{.prev_impl_id = prev_impl_id};
|
|
case ImplRedeclType::Mismatch:
|
|
// Did not match as a redecl, try again.
|
|
break;
|
|
case ImplRedeclType::DiagnosedInvalidRedecl:
|
|
// Found an invalid redecl, which has been diagnosed as such. Treat it
|
|
// as a new decl, with an error.
|
|
return NewImpl{.lookup_bucket = lookup_bucket_ref,
|
|
.find_had_error = true};
|
|
}
|
|
}
|
|
|
|
return NewImpl{.lookup_bucket = lookup_bucket_ref, .find_had_error = false};
|
|
}
|
|
|
|
// Sets the `ImplId` in the `ImplWitnessTable`.
|
|
static auto AssignImplIdInWitness(Context& context, SemIR::ImplId impl_id,
|
|
SemIR::InstId witness_id) -> void {
|
|
if (witness_id == SemIR::ErrorInst::InstId) {
|
|
return;
|
|
}
|
|
auto witness = context.insts().GetAs<SemIR::ImplWitness>(witness_id);
|
|
auto witness_table =
|
|
context.insts().GetAs<SemIR::ImplWitnessTable>(witness.witness_table_id);
|
|
witness_table.impl_id = impl_id;
|
|
// Note: The `ImplWitnessTable` instruction is `Unique`, so while this marks
|
|
// the instruction as being a dependent instruction of a generic impl, it will
|
|
// not be substituted into the eval block.
|
|
ReplaceInstBeforeConstantUse(context, witness.witness_table_id,
|
|
witness_table);
|
|
}
|
|
|
|
auto AddImpl(Context& context, const SemIR::Impl& impl,
|
|
SemIR::ImplStore::LookupBucketRef lookup_bucket,
|
|
Parse::NodeId extend_node, SemIR::LocId implicit_params_loc_id)
|
|
-> SemIR::ImplId {
|
|
auto impl_decl_id = impl.latest_decl_id();
|
|
|
|
// From here on, use the `Impl` from the `ImplStore` instead of `impl` in
|
|
// order to make and see any changes to the `Impl`.
|
|
auto impl_id = context.impls().Add(impl);
|
|
lookup_bucket.push_back(impl_id);
|
|
AssignImplIdInWitness(context, impl_id, impl.witness_id);
|
|
|
|
auto& stored_impl = context.impls().Get(impl_id);
|
|
|
|
// Look to see if there are any generic bindings on the `impl` declaration
|
|
// that are not deducible. If so, and the `impl` does not actually use all
|
|
// its generic bindings, and will never be matched. This should be
|
|
// diagnossed to the user.
|
|
if (!VerifyAllGenericBindingsUsed(context, SemIR::LocId(impl_decl_id),
|
|
implicit_params_loc_id, impl_id,
|
|
stored_impl)) {
|
|
FillImplWitnessWithErrors(context, stored_impl);
|
|
}
|
|
|
|
if (extend_node.has_value()) {
|
|
if (!ApplyExtendImplAs(context, SemIR::LocId(impl_decl_id), stored_impl,
|
|
extend_node, implicit_params_loc_id)) {
|
|
FillImplWitnessWithErrors(context, stored_impl);
|
|
}
|
|
}
|
|
|
|
return impl_id;
|
|
}
|
|
|
|
// Substitute the impl's ImplWitness for any IdentifiedWitness that refers to
|
|
// the current impl's interface, as well as any ImplSelfWitness which always
|
|
// implicitly refers to the current impl.
|
|
class SubstWithImplWitness : public SubstIdentifiedWitnessesCallbacks {
|
|
public:
|
|
SubstWithImplWitness(Context* context, const SemIR::Impl* impl,
|
|
SemIR::InstId impl_witness_id)
|
|
: SubstIdentifiedWitnessesCallbacks(context),
|
|
impl_(impl),
|
|
impl_witness_id_(impl_witness_id) {
|
|
CARBON_CHECK(impl_witness_id.has_value());
|
|
}
|
|
|
|
auto ReplaceIdentified(SemIR::IdentifiedWitness witness)
|
|
-> SemIR::InstId override {
|
|
auto impl_self = context().constant_values().Get(impl_->self_id);
|
|
auto witness_self =
|
|
context().constant_values().Get(witness.query_self_inst_id);
|
|
if (impl_self != witness_self) {
|
|
return SemIR::InstId::None;
|
|
}
|
|
auto witness_interface = context().specific_interfaces().Get(
|
|
witness.query_specific_interface_id);
|
|
if (impl_->interface != witness_interface) {
|
|
return SemIR::InstId::None;
|
|
}
|
|
return impl_witness_id_;
|
|
}
|
|
|
|
auto ReplaceImplSelfWitness(SemIR::ImplSelfWitness witness)
|
|
-> SemIR::InstId override {
|
|
CARBON_CHECK(context().constant_values().Get(impl_->self_id) ==
|
|
context().constant_values().Get(witness.period_self));
|
|
CARBON_CHECK(context().specific_interfaces().Get(
|
|
witness.specific_interface_id) == impl_->interface);
|
|
return impl_witness_id_;
|
|
}
|
|
|
|
private:
|
|
const SemIR::Impl* impl_;
|
|
SemIR::InstId impl_witness_id_;
|
|
};
|
|
|
|
auto AddImplWitnessForDeclaration(Context& context, SemIR::LocId loc_id,
|
|
SemIR::LocId constraint_loc_id,
|
|
const SemIR::Impl& impl,
|
|
IdentifiedFacetTypeId identified_id,
|
|
SemIR::SpecificId self_specific_id)
|
|
-> SemIR::InstId {
|
|
const auto& identified = context.identified_facet_types().Get(identified_id);
|
|
|
|
if (identified.rewrites().empty()) {
|
|
// The witness table is not needed until the definition. Make a placeholder
|
|
// for the declaration.
|
|
auto witness_table_inst_id = AddInst<SemIR::ImplWitnessTable>(
|
|
context, loc_id,
|
|
{.elements_id = context.inst_blocks().AddPlaceholder(),
|
|
.impl_id = SemIR::ImplId::None});
|
|
return AddInst<SemIR::ImplWitness>(
|
|
context, loc_id,
|
|
{.type_id = GetSingletonType(context, SemIR::WitnessType::TypeInstId),
|
|
.witness_table_id = witness_table_inst_id,
|
|
.specific_id = self_specific_id});
|
|
}
|
|
|
|
const auto& interface = context.interfaces().Get(impl.interface.interface_id);
|
|
if (!interface.is_complete()) {
|
|
// This is a declaration with rewrite constraints into `.Self`, but the
|
|
// interface is not complete. Those rewrites have already been diagnosed as
|
|
// an error in their member access.
|
|
return SemIR::ErrorInst::InstId;
|
|
}
|
|
|
|
auto assoc_entities =
|
|
context.inst_blocks().Get(interface.associated_entities_id);
|
|
// TODO: When this function is used for things other than just impls, may want
|
|
// to only load the specific associated entities that are mentioned in rewrite
|
|
// rules.
|
|
for (auto decl_id : assoc_entities) {
|
|
LoadImportRef(context, decl_id);
|
|
}
|
|
|
|
SemIR::InstId witness_inst_id = SemIR::InstId::None;
|
|
llvm::MutableArrayRef<SemIR::InstId> table;
|
|
{
|
|
auto elements_id =
|
|
context.inst_blocks().AddUninitialized(assoc_entities.size());
|
|
table = context.inst_blocks().GetMutable(elements_id);
|
|
for (auto& uninit : table) {
|
|
uninit = SemIR::InstId::ImplWitnessTablePlaceholder;
|
|
}
|
|
|
|
auto witness_table_inst_id = AddInst<SemIR::ImplWitnessTable>(
|
|
context, loc_id,
|
|
{.elements_id = elements_id, .impl_id = SemIR::ImplId::None});
|
|
|
|
witness_inst_id = AddInst<SemIR::ImplWitness>(
|
|
context, loc_id,
|
|
{.type_id = GetSingletonType(context, SemIR::WitnessType::TypeInstId),
|
|
.witness_table_id = witness_table_inst_id,
|
|
.specific_id = self_specific_id});
|
|
}
|
|
|
|
// An `impl as` statement has exactly one extended interface, and the rewrites
|
|
// in the IdentifiedFacetType only contains rewrites into extended interfaces.
|
|
// So we don't need to filter them any further here.
|
|
for (auto rewrite : identified.rewrites()) {
|
|
// We use the rewrite LHS without replacing the witness so that the
|
|
// ImplWitnessAccess has no chance to be evaluated away.
|
|
auto access = context.constant_values().GetInstAs<SemIR::ImplWitnessAccess>(
|
|
rewrite.lhs);
|
|
auto& table_entry = table[access.index.index];
|
|
if (table_entry == SemIR::ErrorInst::InstId) {
|
|
// Don't overwrite an error value. This prioritizes not generating
|
|
// multiple errors for one associated constant over picking a value
|
|
// for it to use to attempt recovery.
|
|
continue;
|
|
}
|
|
|
|
// The LHS of the rewrite contains a witness that could be replaced with
|
|
// ImplWitness, however that would resolve the ImplWitnessAccess that
|
|
// contains it, and try to access the very element of the witness table that
|
|
// we are setting. That results in a diagnostic for accessing the table
|
|
// entry before it's set. So we just leave it be.
|
|
//
|
|
// The RHS of the rewrite may also contain ImplSelfWitness for the current
|
|
// impl. Usually they are gone already, as early rewrite replacement and
|
|
// rewrite resolution replace any references to an associated constant with
|
|
// the value of that associated constant. However FacetValues using `.Self`
|
|
// may encode such witnesses, and it is also possible when an associated
|
|
// constant is used from the impl declaration but is not set in the
|
|
// declaration.
|
|
SubstWithImplWitness callbacks(&context, &impl, witness_inst_id);
|
|
rewrite.rhs =
|
|
SubstIdentifiedWitnesses(context, loc_id, rewrite.rhs, callbacks);
|
|
|
|
auto rewrite_inst_id = context.constant_values().GetInstId(rewrite.rhs);
|
|
if (rewrite_inst_id == SemIR::ErrorInst::InstId) {
|
|
table_entry = SemIR::ErrorInst::InstId;
|
|
continue;
|
|
}
|
|
|
|
// We don't want to leak `.Self` into the witness table, since it loses its
|
|
// meaning outside of the impl declaration. Replace any use of `.Self` here
|
|
// with the impl's self type.
|
|
rewrite_inst_id = context.constant_values().GetInstId(SubstPeriodSelf(
|
|
context, loc_id, context.constant_values().Get(rewrite_inst_id),
|
|
context.constant_values().Get(impl.self_id)));
|
|
|
|
auto decl_id = context.constant_values().GetConstantInstId(
|
|
assoc_entities[access.index.index]);
|
|
CARBON_CHECK(decl_id.has_value(), "Non-constant associated entity");
|
|
if (decl_id == SemIR::ErrorInst::InstId) {
|
|
table_entry = SemIR::ErrorInst::InstId;
|
|
continue;
|
|
}
|
|
|
|
auto assoc_constant_decl =
|
|
context.insts().TryGetAs<SemIR::AssociatedConstantDecl>(decl_id);
|
|
if (!assoc_constant_decl) {
|
|
auto type_id = context.insts().Get(decl_id).type_id();
|
|
auto type_inst = context.types().GetAsInst(type_id);
|
|
auto fn_type = type_inst.As<SemIR::FunctionType>();
|
|
const auto& fn = context.functions().Get(fn_type.function_id);
|
|
CARBON_DIAGNOSTIC(RewriteForAssociatedFunction, Error,
|
|
"rewrite specified for associated function {0}",
|
|
SemIR::NameId);
|
|
context.emitter().Emit(constraint_loc_id, RewriteForAssociatedFunction,
|
|
fn.name_id);
|
|
table_entry = SemIR::ErrorInst::InstId;
|
|
continue;
|
|
}
|
|
|
|
// If the associated constant has a symbolic type, convert the rewrite
|
|
// value to that type now we know the value of `Self`.
|
|
SemIR::TypeId assoc_const_type_id = assoc_constant_decl->type_id;
|
|
if (assoc_const_type_id.is_symbolic()) {
|
|
auto interface_with_self_specific_id = MakeSpecificWithInnerSelf(
|
|
context, loc_id, interface.generic_id, interface.generic_with_self_id,
|
|
impl.interface.specific_id,
|
|
context.constant_values().Get(impl.self_id));
|
|
|
|
// Get the type of the associated constant in this interface with this
|
|
// value for `Self`.
|
|
assoc_const_type_id = GetTypeForSpecificAssociatedEntity(
|
|
context, interface_with_self_specific_id, decl_id);
|
|
// Perform the conversion of the value to the type. We skipped this when
|
|
// forming the facet type because the type of the associated constant
|
|
// was symbolic.
|
|
auto converted_inst_id = ConvertToValueOfType(
|
|
context, constraint_loc_id, rewrite_inst_id, assoc_const_type_id);
|
|
// Canonicalize the converted constant value.
|
|
converted_inst_id =
|
|
context.constant_values().GetConstantInstId(converted_inst_id);
|
|
// The result of conversion can be non-constant even if the original
|
|
// value was constant.
|
|
if (converted_inst_id.has_value()) {
|
|
rewrite_inst_id = converted_inst_id;
|
|
} else {
|
|
const auto& assoc_const = context.associated_constants().Get(
|
|
assoc_constant_decl->assoc_const_id);
|
|
CARBON_DIAGNOSTIC(
|
|
AssociatedConstantNotConstantAfterConversion, Error,
|
|
"associated constant {0} given value {1} that is not constant "
|
|
"after conversion to {2}",
|
|
SemIR::NameId, InstIdAsConstant, SemIR::TypeId);
|
|
context.emitter().Emit(
|
|
constraint_loc_id, AssociatedConstantNotConstantAfterConversion,
|
|
assoc_const.name_id, rewrite_inst_id, assoc_const_type_id);
|
|
rewrite_inst_id = SemIR::ErrorInst::InstId;
|
|
}
|
|
}
|
|
|
|
CARBON_CHECK(rewrite_inst_id == context.constant_values().GetConstantInstId(
|
|
rewrite_inst_id),
|
|
"Rewritten value for associated constant is not canonical.");
|
|
|
|
// The same entry may be written by a named constraint and the impl, or by
|
|
// two named constraints, etc. They need to all match.
|
|
if (table_entry != SemIR::InstId::ImplWitnessTablePlaceholder) {
|
|
if (context.constant_values().Get(table_entry) !=
|
|
context.constant_values().Get(rewrite_inst_id)) {
|
|
if (table_entry != SemIR::ErrorInst::InstId) {
|
|
const auto& assoc_const = context.associated_constants().Get(
|
|
assoc_constant_decl->assoc_const_id);
|
|
CARBON_DIAGNOSTIC(
|
|
AssociatedConstantGivenDifferentValues, Error,
|
|
"associated constant {0} given value {1} that is different from "
|
|
"previously given value {2}",
|
|
SemIR::NameId, InstIdAsConstant, InstIdAsConstant);
|
|
context.emitter().Emit(
|
|
constraint_loc_id, AssociatedConstantGivenDifferentValues,
|
|
assoc_const.name_id, rewrite_inst_id, table_entry);
|
|
}
|
|
table_entry = SemIR::ErrorInst::InstId;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
table_entry = AddInst<SemIR::ImplWitnessAssociatedConstant>(
|
|
context, loc_id,
|
|
{.type_id = context.insts().Get(rewrite_inst_id).type_id(),
|
|
.inst_id = rewrite_inst_id});
|
|
}
|
|
return witness_inst_id;
|
|
}
|
|
|
|
auto ImplWitnessStartDefinition(Context& context, SemIR::Impl& impl) -> void {
|
|
CARBON_CHECK(impl.is_being_defined());
|
|
CARBON_CHECK(impl.witness_id.has_value());
|
|
if (impl.witness_id == SemIR::ErrorInst::InstId) {
|
|
return;
|
|
}
|
|
|
|
{
|
|
if (!RequireCompleteType(
|
|
context, context.types().GetTypeIdForTypeInstId(impl.constraint_id),
|
|
SemIR::LocId(impl.constraint_id), [&](auto& builder) {
|
|
CARBON_DIAGNOSTIC_CONTEXT(
|
|
ImplAsIncompleteFacetTypeDefinition,
|
|
"definition of impl as incomplete facet type {0}",
|
|
InstIdAsType);
|
|
builder.Attach(SemIR::LocId(impl.latest_decl_id()),
|
|
ImplAsIncompleteFacetTypeDefinition,
|
|
impl.constraint_id);
|
|
})) {
|
|
FillImplWitnessWithErrors(context, impl);
|
|
return;
|
|
}
|
|
}
|
|
|
|
const auto& interface = context.interfaces().Get(impl.interface.interface_id);
|
|
|
|
auto assoc_entities =
|
|
context.inst_blocks().Get(interface.associated_entities_id);
|
|
for (auto decl_id : assoc_entities) {
|
|
LoadImportRef(context, decl_id);
|
|
}
|
|
|
|
auto witness = context.insts().GetAs<SemIR::ImplWitness>(impl.witness_id);
|
|
auto witness_table =
|
|
context.insts().GetAs<SemIR::ImplWitnessTable>(witness.witness_table_id);
|
|
auto witness_block =
|
|
context.inst_blocks().GetMutable(witness_table.elements_id);
|
|
|
|
// The impl declaration may have created a placeholder witness table, or a
|
|
// full witness table. We can detect that the witness table is a placeholder
|
|
// table if it's not the `Empty` id, but it is empty still. If it was a
|
|
// placeholder, we can replace the placeholder here with a table of the proper
|
|
// size, since the interface must be complete for the impl definition.
|
|
bool witness_table_is_placeholder =
|
|
witness_table.elements_id != SemIR::InstBlockId::Empty &&
|
|
witness_block.empty();
|
|
if (witness_table_is_placeholder) {
|
|
// TODO: Since our `empty_table` repeats the same value throughout, we could
|
|
// skip an allocation here if there was a `ReplacePlaceholder` function that
|
|
// took a size and value instead of an array of values.
|
|
llvm::SmallVector<SemIR::InstId> empty_table(
|
|
assoc_entities.size(), SemIR::InstId::ImplWitnessTablePlaceholder);
|
|
context.inst_blocks().ReplacePlaceholder(witness_table.elements_id,
|
|
empty_table);
|
|
witness_block = context.inst_blocks().GetMutable(witness_table.elements_id);
|
|
}
|
|
|
|
// Check we have a value for all non-function associated constants in the
|
|
// witness.
|
|
for (auto [assoc_entity, witness_value] :
|
|
llvm::zip_equal(assoc_entities, witness_block)) {
|
|
auto decl_id = context.constant_values().GetConstantInstId(assoc_entity);
|
|
CARBON_CHECK(decl_id.has_value(), "Non-constant associated entity");
|
|
if (auto decl =
|
|
context.insts().TryGetAs<SemIR::AssociatedConstantDecl>(decl_id)) {
|
|
if (witness_value == SemIR::InstId::ImplWitnessTablePlaceholder) {
|
|
CARBON_DIAGNOSTIC(ImplAssociatedConstantNeedsValue, Error,
|
|
"associated constant {0} not given a value in impl "
|
|
"of interface {1}",
|
|
SemIR::NameId, SemIR::NameId);
|
|
CARBON_DIAGNOSTIC_LABEL(AssociatedConstantHere, Info,
|
|
"associated constant declared here");
|
|
context.emitter()
|
|
.Build(impl.definition_id, ImplAssociatedConstantNeedsValue,
|
|
context.associated_constants()
|
|
.Get(decl->assoc_const_id)
|
|
.name_id,
|
|
interface.name_id)
|
|
.Attach(assoc_entity, AssociatedConstantHere)
|
|
.Emit();
|
|
|
|
witness_value = SemIR::ErrorInst::InstId;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Adds functions to the witness that the specified impl implements the given
|
|
// interface.
|
|
auto FinishImplWitness(Context& context, const SemIR::Impl& impl) -> void {
|
|
CARBON_CHECK(impl.is_being_defined());
|
|
CARBON_CHECK(impl.witness_id.has_value());
|
|
if (impl.witness_id == SemIR::ErrorInst::InstId) {
|
|
return;
|
|
}
|
|
auto witness = context.insts().GetAs<SemIR::ImplWitness>(impl.witness_id);
|
|
auto witness_table =
|
|
context.insts().GetAs<SemIR::ImplWitnessTable>(witness.witness_table_id);
|
|
auto witness_block =
|
|
context.inst_blocks().GetMutable(witness_table.elements_id);
|
|
auto& impl_scope = context.name_scopes().Get(impl.scope_id);
|
|
const auto& interface = context.interfaces().Get(impl.interface.interface_id);
|
|
auto assoc_entities =
|
|
context.inst_blocks().Get(interface.associated_entities_id);
|
|
llvm::SmallVector<SemIR::InstId> used_decl_ids;
|
|
|
|
auto self_facet = GetConstantFacetValueForTypeAndInterface(
|
|
context, impl.self_id, impl.interface, impl.witness_id);
|
|
auto interface_with_self_specific_id = MakeSpecificWithInnerSelf(
|
|
context, SemIR::LocId(impl.definition_id), interface.generic_id,
|
|
interface.generic_with_self_id, impl.interface.specific_id, self_facet);
|
|
|
|
for (auto [assoc_entity, witness_value] :
|
|
llvm::zip_equal(assoc_entities, witness_block)) {
|
|
auto decl_id =
|
|
context.constant_values().GetInstId(SemIR::GetConstantValueInSpecific(
|
|
context.sem_ir(), interface_with_self_specific_id, assoc_entity));
|
|
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) {
|
|
witness_value = SemIR::ErrorInst::InstId;
|
|
break;
|
|
}
|
|
auto type_inst = context.types().GetAsInst(struct_value.type_id);
|
|
auto fn_type = type_inst.TryAs<SemIR::FunctionType>();
|
|
if (!fn_type) {
|
|
CARBON_FATAL("Unexpected type: {0}", type_inst);
|
|
}
|
|
auto& fn = context.functions().Get(fn_type->function_id);
|
|
auto lookup_result =
|
|
LookupNameInExactScope(context, SemIR::LocId(decl_id), fn.name_id,
|
|
impl.scope_id, impl_scope);
|
|
using InterfaceModifier = SemIR::Function::InterfaceModifier;
|
|
if (lookup_result.is_found()) {
|
|
if (fn.interface_modifier == InterfaceModifier::Final) {
|
|
CARBON_DIAGNOSTIC(ImplFinalFunction, Error,
|
|
"attempted to implement `{0}`, which is final in "
|
|
"interface `{1}`",
|
|
SemIR::NameId, SemIR::NameId);
|
|
auto builder =
|
|
context.emitter().Build(impl.definition_id, ImplFinalFunction,
|
|
fn.name_id, interface.name_id);
|
|
NoteAssociatedFunction(context, builder, fn_type->function_id);
|
|
builder.Emit();
|
|
witness_value = SemIR::ErrorInst::InstId;
|
|
break;
|
|
}
|
|
used_decl_ids.push_back(lookup_result.target_inst_id());
|
|
witness_value = CheckAssociatedFunctionImplementation(
|
|
context, *fn_type,
|
|
context.generics().GetSelfSpecific(impl.generic_id),
|
|
lookup_result.target_inst_id(),
|
|
/*defer_thunk_definition=*/true);
|
|
break;
|
|
}
|
|
|
|
if (fn.interface_modifier != InterfaceModifier::None) {
|
|
// We are updating the impl witness table in-place, and we pulled this
|
|
// instruction out of a constant value in a different generic, so
|
|
// manually ensure the new value gets added to the eval block.
|
|
witness_value =
|
|
GetOrAddInstWithSpecificConstantValue(context, decl_id);
|
|
break;
|
|
} else {
|
|
CARBON_DIAGNOSTIC(
|
|
ImplMissingFunction, Error,
|
|
"missing implementation of {0} in impl of interface {1}",
|
|
SemIR::NameId, SemIR::NameId);
|
|
auto builder =
|
|
context.emitter().Build(impl.definition_id, ImplMissingFunction,
|
|
fn.name_id, interface.name_id);
|
|
NoteAssociatedFunction(context, builder, fn_type->function_id);
|
|
builder.Emit();
|
|
|
|
witness_value = SemIR::ErrorInst::InstId;
|
|
}
|
|
break;
|
|
}
|
|
case SemIR::AssociatedConstantDecl::Kind: {
|
|
// These are set to their final values already.
|
|
break;
|
|
}
|
|
default:
|
|
CARBON_CHECK(decl_id == SemIR::ErrorInst::InstId,
|
|
"Unexpected kind of associated entity {0}", decl);
|
|
witness_value = SemIR::ErrorInst::InstId;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// TODO: Diagnose if any declarations in the impl are not in used_decl_ids.
|
|
}
|
|
|
|
auto CheckRequireDeclsSatisfied(Context& context, SemIR::LocId loc_id,
|
|
SemIR::Impl& impl,
|
|
SemIR::TypeInstId full_constraint_id,
|
|
IdentifiedFacetTypeId identified_id) -> void {
|
|
if (impl.witness_id == SemIR::ErrorInst::InstId) {
|
|
return;
|
|
}
|
|
|
|
// The IdentifiedFacetType canonicalizes the self facets, so we do the same
|
|
// for comparing with it.
|
|
auto self_const_id =
|
|
GetCanonicalFacet(context, context.constant_values().Get(impl.self_id));
|
|
|
|
const auto& identified = context.identified_facet_types().Get(identified_id);
|
|
for (auto req : identified.required_impls()) {
|
|
// This replaces both IdentifiedWitness and ImplSelfWitness referring to
|
|
// this impl with the impl's ImplWitness. That causes any access through the
|
|
// witness to resolve to the value from the impl's witness table.
|
|
SubstWithImplWitness callbacks(&context, &impl, impl.witness_id);
|
|
req = SubstIdentifiedWitnesses(context, loc_id, req, callbacks);
|
|
|
|
if (req.self_facet_value == self_const_id &&
|
|
req.specific_interface == impl.interface) {
|
|
// This is what the impl is implementing, so it's not already satisfied.
|
|
continue;
|
|
}
|
|
|
|
auto result = LookupImplWitness(
|
|
context, loc_id, req.self_facet_value,
|
|
GetInterfaceType(context, req.specific_interface.interface_id,
|
|
req.specific_interface.specific_id)
|
|
.AsConstantId());
|
|
if (!result.has_value()) {
|
|
CARBON_DIAGNOSTIC(IdentifiedRequireImplsNotImplemented, Error,
|
|
"constraint {0} being implemented requires that {1} "
|
|
"implements `{2}`",
|
|
SemIR::DeclaredFacetTypeId, InstIdAsConstant,
|
|
SemIR::SpecificInterface);
|
|
auto facet_type = context.insts().GetAs<SemIR::FacetType>(
|
|
context.constant_values().GetConstantInstId(full_constraint_id));
|
|
context.emitter().Emit(
|
|
loc_id, IdentifiedRequireImplsNotImplemented,
|
|
facet_type.declared_facet_type_id,
|
|
context.constant_values().GetInstId(req.self_facet_value),
|
|
req.specific_interface);
|
|
}
|
|
if (!result.has_value() || result.has_error_value()) {
|
|
FillImplWitnessWithErrors(context, impl);
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (auto equiv : identified.equivalents()) {
|
|
// This replaces both IdentifiedWitness and ImplSelfWitness referring to
|
|
// this impl with the impl's ImplWitness. That causes any access through the
|
|
// witness to resolve to the value from the impl's witness table.
|
|
SubstWithImplWitness callbacks(&context, &impl, impl.witness_id);
|
|
auto subst_equiv =
|
|
SubstIdentifiedWitnesses(context, loc_id, equiv, callbacks);
|
|
|
|
if (subst_equiv.lhs != subst_equiv.rhs) {
|
|
CARBON_DIAGNOSTIC(IdentifiedRequireImplsNotEquivalent, Error,
|
|
"constraint {0} being implemented requires that {1} "
|
|
"equals {2}; found {3} and {4}",
|
|
SemIR::DeclaredFacetTypeId, InstIdAsConstant,
|
|
InstIdAsConstant, InstIdAsConstant, InstIdAsConstant);
|
|
auto facet_type = context.insts().GetAs<SemIR::FacetType>(
|
|
context.constant_values().GetConstantInstId(full_constraint_id));
|
|
context.emitter().Emit(
|
|
loc_id, IdentifiedRequireImplsNotEquivalent,
|
|
facet_type.declared_facet_type_id,
|
|
context.constant_values().GetInstId(equiv.lhs),
|
|
context.constant_values().GetInstId(equiv.rhs),
|
|
context.constant_values().GetInstId(subst_equiv.lhs),
|
|
context.constant_values().GetInstId(subst_equiv.rhs));
|
|
}
|
|
}
|
|
|
|
const auto& interface = context.interfaces().Get(impl.interface.interface_id);
|
|
|
|
auto require_ids =
|
|
context.require_impls_blocks().Get(interface.require_impls_block_id);
|
|
if (require_ids.empty()) {
|
|
return;
|
|
}
|
|
|
|
auto interface_with_self_specific_id = MakeSpecificWithInnerSelf(
|
|
context, loc_id, interface.generic_id, interface.generic_with_self_id,
|
|
impl.interface.specific_id, context.constant_values().Get(impl.self_id));
|
|
|
|
for (auto require_id : require_ids) {
|
|
const auto& require = context.require_impls().Get(require_id);
|
|
|
|
// Each require is in its own generic, with no additional bindings and no
|
|
// definition, so that they can have their specifics independently
|
|
// instantiated.
|
|
auto require_specific_id = CopySpecificToGeneric(
|
|
context, SemIR::LocId(require.decl_id), interface_with_self_specific_id,
|
|
require.generic_id);
|
|
auto req_self_const_id = GetConstantValueInSpecific(
|
|
context.sem_ir(), require_specific_id, require.self_id);
|
|
auto req_facet_type_const_id = GetConstantValueInSpecific(
|
|
context.sem_ir(), require_specific_id, require.facet_type_inst_id);
|
|
if (req_self_const_id == SemIR::ErrorInst::ConstantId ||
|
|
req_facet_type_const_id == SemIR::ErrorInst::ConstantId) {
|
|
FillImplWitnessWithErrors(context, impl);
|
|
return;
|
|
}
|
|
|
|
auto result = LookupImplWitness(context, loc_id, req_self_const_id,
|
|
req_facet_type_const_id);
|
|
if (!result.has_value()) {
|
|
CARBON_DIAGNOSTIC(InterfaceRequireImplsNotImplemented, Error,
|
|
"interface `{0}` being implemented requires that {1} "
|
|
"implements {2}",
|
|
SemIR::SpecificInterface, SemIR::TypeId,
|
|
SemIR::DeclaredFacetTypeId);
|
|
context.emitter().Emit(
|
|
loc_id, InterfaceRequireImplsNotImplemented, impl.interface,
|
|
context.types().GetTypeIdForTypeConstantId(req_self_const_id),
|
|
context.constant_values()
|
|
.GetInstAs<SemIR::FacetType>(req_facet_type_const_id)
|
|
.declared_facet_type_id);
|
|
}
|
|
if (!result.has_value() || result.has_error_value()) {
|
|
FillImplWitnessWithErrors(context, impl);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
auto FillImplWitnessWithErrors(Context& context, SemIR::Impl& impl) -> void {
|
|
if (impl.witness_id == SemIR::ErrorInst::InstId) {
|
|
return;
|
|
}
|
|
auto witness = context.insts().GetAs<SemIR::ImplWitness>(impl.witness_id);
|
|
auto witness_table =
|
|
context.insts().GetAs<SemIR::ImplWitnessTable>(witness.witness_table_id);
|
|
auto witness_block =
|
|
context.inst_blocks().GetMutable(witness_table.elements_id);
|
|
for (auto& elem : witness_block) {
|
|
if (elem == SemIR::InstId::ImplWitnessTablePlaceholder) {
|
|
elem = SemIR::ErrorInst::InstId;
|
|
}
|
|
}
|
|
impl.witness_id = SemIR::ErrorInst::InstId;
|
|
}
|
|
|
|
auto IsImplEffectivelyFinal(Context& context, const SemIR::Impl& impl) -> bool {
|
|
return impl.is_final ||
|
|
(context.constant_values().Get(impl.self_id).is_concrete() &&
|
|
context.constant_values().Get(impl.constraint_id).is_concrete());
|
|
}
|
|
|
|
auto CheckConstraintIsFacetType(Context& context, SemIR::LocId loc_id,
|
|
SemIR::TypeInstId constraint_id) -> bool {
|
|
auto facet_type_const_inst_id =
|
|
context.constant_values().GetConstantInstId(constraint_id);
|
|
auto facet_type =
|
|
context.insts().TryGetAs<SemIR::FacetType>(facet_type_const_inst_id);
|
|
if (!facet_type && facet_type_const_inst_id != SemIR::ErrorInst::InstId) {
|
|
CARBON_DIAGNOSTIC(ImplAsNonFacetType, Error, "impl as non-facet type {0}",
|
|
InstIdAsType);
|
|
context.emitter().Emit(loc_id, ImplAsNonFacetType, constraint_id);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
auto CheckConstraintIsInterface(Context& context, SemIR::LocId loc_id,
|
|
SemIR::InstId self_id,
|
|
SemIR::TypeInstId full_constraint_id)
|
|
-> IdentifiedFacetTypeId {
|
|
auto identified_id = RequireIdentifiedFacetType(
|
|
context, SemIR::LocId(full_constraint_id),
|
|
context.constant_values().Get(self_id),
|
|
context.types().GetTypeIdForTypeInstId(full_constraint_id),
|
|
[&](auto& builder) {
|
|
CARBON_DIAGNOSTIC_CONTEXT(
|
|
ImplOfUnidentifiedFacetType,
|
|
"facet type {0} cannot be identified in `impl as`", InstIdAsType);
|
|
builder.Attach(loc_id, ImplOfUnidentifiedFacetType, full_constraint_id);
|
|
});
|
|
if (!identified_id.has_value()) {
|
|
return IdentifiedFacetTypeId::None;
|
|
}
|
|
const auto& identified = context.identified_facet_types().Get(identified_id);
|
|
if (!identified.is_valid_impl_as_target()) {
|
|
CARBON_DIAGNOSTIC(ImplOfNotOneInterface, Error,
|
|
"impl as {0} interfaces, expected 1", int);
|
|
context.emitter().Emit(loc_id, ImplOfNotOneInterface,
|
|
identified.num_interfaces_to_impl());
|
|
return IdentifiedFacetTypeId::None;
|
|
}
|
|
return identified_id;
|
|
}
|
|
|
|
auto GetImplInterfaceInSpecific(Context& context, const SemIR::Impl& impl,
|
|
SemIR::SpecificId specific_id)
|
|
-> SemIR::SpecificInterface {
|
|
if (!specific_id.has_value()) {
|
|
CARBON_CHECK(!impl.generic_id.has_value());
|
|
// The impl is not generic, the specific interface will be concrete.
|
|
return impl.interface;
|
|
}
|
|
|
|
CARBON_CHECK(context.specifics().Get(specific_id).generic_id ==
|
|
impl.generic_id);
|
|
// The `interface_inst_id` is an instruction that contains the impl's target
|
|
// specific interface. We get its constant value to apply the impl's specific
|
|
// to that target interface.
|
|
auto interface_in_specific = SemIR::GetConstantValueInSpecific(
|
|
context.sem_ir(), specific_id, impl.interface_inst_id);
|
|
if (interface_in_specific == SemIR::ErrorInst::ConstantId) {
|
|
return SemIR::SpecificInterface::None;
|
|
}
|
|
// The `interface_inst_id` is always an `ImplSelfWitness` at this time.
|
|
auto inst = context.constant_values().GetInstAs<SemIR::ImplSelfWitness>(
|
|
interface_in_specific);
|
|
return context.specific_interfaces().Get(inst.specific_interface_id);
|
|
}
|
|
|
|
} // namespace Carbon::Check
|