mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 22:02:23 +01:00
Add a new instruction called ImplSymbolicWitness which represents a
search for an impl declaration given a self type and an interface to
find implemented for the self type. The self type is stored as a
constant instruction id, rather than as a ConstantId, as instructions
don't currently support holding ConstantId. The interface is stored as a
SpecificInterface but we can't fit all of it directly into the
instruction. So we add a new id to refer to the SpecificInterface as
follows.
Add a new SpecificInterfaceId which indexes into a canonical value store
on SemIR::File. This tracks all `SpecificInterface`s stored in an
instruction - specifically the ImplSymbolicWitness instruction.
The SpecificInterface on Impl is still stored there as a value, not as
an id, and no id is eagerly constructed for it. We wait until an id is
needed to make one. Since they are canonical, a new id is only create
when a new SpecificInterface value is seen.
When doing impl lookup, and the query is not concrete, and the impl is
not effectively final, the query needs to consider future impls that may
specialize either the self type or the constaint to make a more precise
match and replace the found impl declaration. Instead of returning the
ImplWitness instruction from the found impl, we generate a
ImplSymbolicWitness instruction, storing the query so that it can be
replayed later. This instruction is added to the generic eval block and
thus will be re-evaluated later with a SpecificId that may make the
query more concrete. When evaluating the instruction and replaying the
query, the lookup has the same conditions and if it does not decide to
use the found impl concretely, then the same instruction is returned
from eval, leaving it as symbolic.
--- Impl lookup changes ---
Impl lookup gets a little more interesting now. It continues to look in
the facet value for a witness if the self type is a facet value. Then
falls back to looking for an impl declaration. This step is no longer
done directly. Instead, we construct a ImplSymbolicWitness instruction
and evaluate it immediately for each interface that are in the query
facet type.
The ImplSymbolicWitness instruction, when evaluated, calls back to the
impl lookup code, with a query specific interface. There we resume back
into the same code path as from before, finding a witness in an impl
declaration. But we may return "found a non-final impl" instead of a
concrete witness. If eval receives this back, it evaluates to the
current ImplSymbolicWitness instruction as the resulting constant value.
To pass lookup failures back through eval, a result of InstId::None from
the second step of impl lookup will result in a non-constant value,
which is used as a signal back up the stack to the original impl lookup
function that the lookup failed. Using a non-constant value here would
break evaluation of the generic eval block if impl lookup could fail
there, however we know it will not since we only leave behind an
ImplSymbolicWitness instruction in the eval block if we found at least
one matching impl already, and we just want to look for a better match
with a more specific query.
We must take care to not store a reference into any value store across
computation in impl lookup, since impl lookup can recurse into itself
invalidate those stores. That includes the SpecificInterface obtained
from a SpecificInterfaceId, which impl lookup also inserts into the
store.
--- The long tail ---
Adding a new instruction and a new id type requires a myriad of changes
to support them:
We add Dump() support for SpecificInterfaceId. And fix a crash in Dump
for SpecificId::None. We also add MakeSpecificInterfaceId() for dumping
arbitrary ids.
The type of ImplSymbolicWitness is a new singleton builtin type
instruction called WitnessSymbolicType (like WitnessType is the type for
an ImplWitness).
Both ImplSymbolicWitness and WitnessSymbolicType are given `Value` as
their expression category as they are builtin constant values. And
BuildInfo() in TypeCompleter is taught about them both, returning a
`ValueRepr::Copy`.
WitnessSymbolicType is added to the set of SingletonInstKinds, so that
it can have a singleton instrution id as a static member.
Lower's BuildTypeForInst() is taught to make an empty struct for
WitnessSymbolicType, similar to WitnessType.
Instruction formatter (FormatterImpl) grows support for printing a
SpecificInterfaceId so that it can print both arguments of
ImplSymbolicWitness on the RHS when printing the SemIR instruction. To
print a SpecificInterfaceId, it prints both the interface id and the
specific id (if there is one). For example, for a query on a generic
interface `Z` with one parameter, the RHS includes the query, interface,
and specific:
```
%Z.impl_symbolic_witness: <symbolic witness> = impl_symbolic_witness %U, @Z, @Z(%U.as_type) [symbolic]
```
IdKind is extended to include SpecificInterfaceId.
InstFingerprinter is taught to look through SpecificInterfaceId and use
the interface and specific ids in the fingerprint.
InstNamer is taught about SpecificInterfaceId, counting the interfaces
when building an index. It is also tought about ImplSymbolicWitness,
using the name of the interface within and the `.impl_symbolic_witness`
suffix. For example, here the LHS is named after the interface in the
query:
```
%Z.impl_symbolic_witness: <symbolic witness> = impl_symbolic_witness %U, @Z, @Z(%U.as_type) [symbolic]
```
StringifyTypeExpr is taught about WitnessSymbolicType, which uses its IR
name since it's a singleton. And about ImplSymbolicWitness which uses
its constant value. The handling of ImplWitnessAccess also needed to be
adjusted, since it assumed that ImplWitnessAccess::witness_id would
always be a FacetAccessWitness, but it can now also be an
ImplSymbolicWitness. (It seems that the witness_id is also assigned
ImplWitness instructions, but those ImplWitnessAccess instructions don't
ever seem to get stringified in a diagnostic at this time.) At the
moment the ImplWitnessAccess with a symbolic witness is just stringified
as "<symbolic>", such as in:
```
x.carbon:1:2: error: cannot implicitly convert value of type `()` to `<symbolic>` [ConversionFailure]
let a: C(D).(Z.X) = ();
^~
```
There is a TODO left behind to include more information there.
The TypeStructure builder is made to handle WitnessSymbolicType and
WitnessType. These come up now in deduce where a generic impl will have
a ImplSymbolicWitness in a FacetValue for a generic self type. The query
may have a concrete ImplWitness in the same position. Since deduce tries
to deduce through the FacetValue, it tries to convert ImplWitness to
ImplSymbolicWitness, tries to do an impl lookup for `impl ImplWitness as
ImplicitAs(ImplSymbolicWitness)` and causes us to build type structures
with each of these.
Subst is updated to handle pushing and popping SpecificInterfaceId.
Without this, when finishing a generic's eval block, we would walk into
the ImplSymbolicWitness instruction, and its arguments, and fail to
recurse down into the SpecificInterfaceId. Then any specifics inside
would be left as "orphaned" without any generic id attached to them, and
we would never update the instructions in the SpecificInterface's
instructions (inside its own SpecificId) with new constant values when
evaluating the generic eval block against a specific. To do this we push
the specific_id inside the SpecificInterface, and when popping we pop
the specific_id then construct a new canonical SpecificInterface with it
and return that id.
We add support for importing ImplSymbolicWitness by importing its self
constant instruction and specific interface id. However we also had to
add import support for SpecificImplFunction, which can now appear in the
generic eval block for a generic impl declaration, and thus must be
imported with the declaration. This is done very similarly to
SpecificFunction, except the `type_id` is a singleton value.
---------
Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
614 lines
25 KiB
C++
614 lines
25 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_lookup.h"
|
|
|
|
#include <algorithm>
|
|
#include <variant>
|
|
|
|
#include "toolchain/base/kind_switch.h"
|
|
#include "toolchain/check/deduce.h"
|
|
#include "toolchain/check/diagnostic_helpers.h"
|
|
#include "toolchain/check/eval.h"
|
|
#include "toolchain/check/generic.h"
|
|
#include "toolchain/check/import_ref.h"
|
|
#include "toolchain/check/inst.h"
|
|
#include "toolchain/check/type.h"
|
|
#include "toolchain/check/type_completion.h"
|
|
#include "toolchain/check/type_structure.h"
|
|
#include "toolchain/sem_ir/facet_type_info.h"
|
|
#include "toolchain/sem_ir/ids.h"
|
|
#include "toolchain/sem_ir/impl.h"
|
|
#include "toolchain/sem_ir/inst.h"
|
|
#include "toolchain/sem_ir/typed_insts.h"
|
|
|
|
namespace Carbon::Check {
|
|
|
|
static auto FindAssociatedImportIRs(Context& context,
|
|
SemIR::ConstantId query_self_const_id,
|
|
SemIR::ConstantId query_facet_type_const_id)
|
|
-> llvm::SmallVector<SemIR::ImportIRId> {
|
|
llvm::SmallVector<SemIR::ImportIRId> result;
|
|
|
|
// Add an entity to our result.
|
|
auto add_entity = [&](const SemIR::EntityWithParamsBase& entity) {
|
|
// We will look for impls in the import IR associated with the first owning
|
|
// declaration.
|
|
auto decl_id = entity.first_owning_decl_id;
|
|
if (!decl_id.has_value()) {
|
|
return;
|
|
}
|
|
if (auto ir_id = GetCanonicalImportIRInst(context, decl_id).ir_id;
|
|
ir_id.has_value()) {
|
|
result.push_back(ir_id);
|
|
}
|
|
};
|
|
|
|
llvm::SmallVector<SemIR::InstId> worklist;
|
|
worklist.push_back(context.constant_values().GetInstId(query_self_const_id));
|
|
if (query_facet_type_const_id.has_value()) {
|
|
worklist.push_back(
|
|
context.constant_values().GetInstId(query_facet_type_const_id));
|
|
}
|
|
|
|
// Push the contents of an instruction block onto our worklist.
|
|
auto push_block = [&](SemIR::InstBlockId block_id) {
|
|
if (block_id.has_value()) {
|
|
llvm::append_range(worklist, context.inst_blocks().Get(block_id));
|
|
}
|
|
};
|
|
|
|
// Add the arguments of a specific to the worklist.
|
|
auto push_args = [&](SemIR::SpecificId specific_id) {
|
|
if (specific_id.has_value()) {
|
|
push_block(context.specifics().Get(specific_id).args_id);
|
|
}
|
|
};
|
|
|
|
while (!worklist.empty()) {
|
|
auto inst_id = worklist.pop_back_val();
|
|
|
|
// Visit the operands of the constant.
|
|
auto inst = context.insts().Get(inst_id);
|
|
auto [arg0_kind, arg1_kind] = inst.ArgKinds();
|
|
for (auto [arg, kind] :
|
|
{std::pair{inst.arg0(), arg0_kind}, {inst.arg1(), arg1_kind}}) {
|
|
switch (kind) {
|
|
case SemIR::IdKind::For<SemIR::InstId>: {
|
|
if (auto id = SemIR::InstId(arg); id.has_value()) {
|
|
worklist.push_back(id);
|
|
}
|
|
break;
|
|
}
|
|
case SemIR::IdKind::For<SemIR::InstBlockId>: {
|
|
push_block(SemIR::InstBlockId(arg));
|
|
break;
|
|
}
|
|
case SemIR::IdKind::For<SemIR::ClassId>: {
|
|
add_entity(context.classes().Get(SemIR::ClassId(arg)));
|
|
break;
|
|
}
|
|
case SemIR::IdKind::For<SemIR::InterfaceId>: {
|
|
add_entity(context.interfaces().Get(SemIR::InterfaceId(arg)));
|
|
break;
|
|
}
|
|
case SemIR::IdKind::For<SemIR::FacetTypeId>: {
|
|
const auto& facet_type_info =
|
|
context.facet_types().Get(SemIR::FacetTypeId(arg));
|
|
for (const auto& impl : facet_type_info.impls_constraints) {
|
|
add_entity(context.interfaces().Get(impl.interface_id));
|
|
push_args(impl.specific_id);
|
|
}
|
|
break;
|
|
}
|
|
case SemIR::IdKind::For<SemIR::FunctionId>: {
|
|
add_entity(context.functions().Get(SemIR::FunctionId(arg)));
|
|
break;
|
|
}
|
|
case SemIR::IdKind::For<SemIR::SpecificId>: {
|
|
push_args(SemIR::SpecificId(arg));
|
|
break;
|
|
}
|
|
default: {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Deduplicate.
|
|
llvm::sort(result, [](SemIR::ImportIRId a, SemIR::ImportIRId b) {
|
|
return a.index < b.index;
|
|
});
|
|
result.erase(llvm::unique(result), result.end());
|
|
|
|
return result;
|
|
}
|
|
|
|
// Returns true if a cycle was found and diagnosed.
|
|
static auto FindAndDiagnoseImplLookupCycle(
|
|
Context& context,
|
|
const llvm::SmallVector<Context::ImplLookupStackEntry>& stack,
|
|
SemIR::LocId loc_id, SemIR::ConstantId query_self_const_id,
|
|
SemIR::ConstantId query_facet_type_const_id) -> bool {
|
|
// Deduction of the interface parameters can do further impl lookups, and we
|
|
// need to ensure we terminate.
|
|
//
|
|
// https://docs.carbon-lang.dev/docs/design/generics/details.html#acyclic-rule
|
|
// - We look for violations of the acyclic rule by seeing if a previous lookup
|
|
// had all the same type inputs.
|
|
// - The `query_facet_type_const_id` encodes the entire facet type being
|
|
// looked up, including any specific parameters for a generic interface.
|
|
//
|
|
// TODO: Implement the termination rule, which requires looking at the
|
|
// complexity of the types on the top of (or throughout?) the stack:
|
|
// https://docs.carbon-lang.dev/docs/design/generics/details.html#termination-rule
|
|
for (auto [i, entry] : llvm::enumerate(stack)) {
|
|
if (entry.query_self_const_id == query_self_const_id &&
|
|
entry.query_facet_type_const_id == query_facet_type_const_id) {
|
|
auto facet_type_type_id =
|
|
context.types().GetTypeIdForTypeConstantId(query_facet_type_const_id);
|
|
CARBON_DIAGNOSTIC(ImplLookupCycle, Error,
|
|
"cycle found in search for impl of {0} for type {1}",
|
|
SemIR::TypeId, SemIR::TypeId);
|
|
auto builder = context.emitter().Build(
|
|
loc_id, ImplLookupCycle, facet_type_type_id,
|
|
context.types().GetTypeIdForTypeConstantId(query_self_const_id));
|
|
for (const auto& active_entry : llvm::drop_begin(stack, i)) {
|
|
if (active_entry.impl_loc.has_value()) {
|
|
CARBON_DIAGNOSTIC(ImplLookupCycleNote, Note,
|
|
"determining if this impl clause matches", );
|
|
builder.Note(active_entry.impl_loc, ImplLookupCycleNote);
|
|
}
|
|
}
|
|
builder.Emit();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Gets the set of `SpecificInterface`s that are required by a facet type
|
|
// (as a constant value).
|
|
static auto GetInterfacesFromConstantId(
|
|
Context& context, SemIR::ConstantId query_facet_type_const_id,
|
|
bool& has_other_requirements)
|
|
-> llvm::SmallVector<SemIR::SpecificInterface> {
|
|
auto facet_type_inst_id =
|
|
context.constant_values().GetInstId(query_facet_type_const_id);
|
|
auto facet_type_inst =
|
|
context.insts().GetAs<SemIR::FacetType>(facet_type_inst_id);
|
|
const auto& facet_type_info =
|
|
context.facet_types().Get(facet_type_inst.facet_type_id);
|
|
has_other_requirements = facet_type_info.other_requirements;
|
|
// TODO: This needs to match the order of witnesses for the facet type, which
|
|
// will need to be maintained once we add support for named constraints.
|
|
return facet_type_info.impls_constraints;
|
|
}
|
|
|
|
static auto GetWitnessIdForImpl(Context& context, SemIR::LocId loc_id,
|
|
bool query_is_concrete,
|
|
SemIR::ConstantId query_self_const_id,
|
|
const SemIR::SpecificInterface& interface,
|
|
SemIR::ImplId impl_id) -> EvalImplLookupResult {
|
|
// The impl may have generic arguments, in which case we need to deduce them
|
|
// to find what they are given the specific type and interface query. We use
|
|
// that specific to map values in the impl to the deduced values.
|
|
auto specific_id = SemIR::SpecificId::None;
|
|
{
|
|
// DeduceImplArguments can import new impls which can invalidate any
|
|
// pointers into `context.impls()`.
|
|
const SemIR::Impl& impl = context.impls().Get(impl_id);
|
|
if (impl.generic_id.has_value()) {
|
|
specific_id =
|
|
DeduceImplArguments(context, loc_id,
|
|
{.self_id = impl.self_id,
|
|
.generic_id = impl.generic_id,
|
|
.specific_id = impl.interface.specific_id},
|
|
query_self_const_id, interface.specific_id);
|
|
if (!specific_id.has_value()) {
|
|
return EvalImplLookupResult::MakeNone();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get a pointer again after DeduceImplArguments() is complete.
|
|
const SemIR::Impl& impl = context.impls().Get(impl_id);
|
|
|
|
// The self type of the impl must match the type in the query, or this is an
|
|
// `impl T as ...` for some other type `T` and should not be considered.
|
|
auto deduced_self_const_id = SemIR::GetConstantValueInSpecific(
|
|
context.sem_ir(), specific_id, impl.self_id);
|
|
// In a generic `impl forall` the self type can be a FacetAccessType, which
|
|
// will not be the same constant value as a query facet value. We move through
|
|
// to the facet value here, and if the query was a FacetAccessType we did the
|
|
// same there so they still match.
|
|
if (auto access = context.insts().TryGetAs<SemIR::FacetAccessType>(
|
|
context.constant_values().GetInstId(deduced_self_const_id))) {
|
|
deduced_self_const_id =
|
|
context.constant_values().Get(access->facet_value_inst_id);
|
|
}
|
|
if (query_self_const_id != deduced_self_const_id) {
|
|
return EvalImplLookupResult::MakeNone();
|
|
}
|
|
|
|
// The impl's constraint is a facet type which it is implementing for the self
|
|
// type: the `I` in `impl ... as I`. The deduction step may be unable to be
|
|
// fully applied to the types in the constraint and result in an error here,
|
|
// in which case it does not match the query.
|
|
auto deduced_constraint_id =
|
|
context.constant_values().GetInstId(SemIR::GetConstantValueInSpecific(
|
|
context.sem_ir(), specific_id, impl.constraint_id));
|
|
if (deduced_constraint_id == SemIR::ErrorInst::SingletonInstId) {
|
|
return EvalImplLookupResult::MakeNone();
|
|
}
|
|
|
|
auto deduced_constraint_facet_type_id =
|
|
context.insts()
|
|
.GetAs<SemIR::FacetType>(deduced_constraint_id)
|
|
.facet_type_id;
|
|
const auto& deduced_constraint_facet_type_info =
|
|
context.facet_types().Get(deduced_constraint_facet_type_id);
|
|
CARBON_CHECK(deduced_constraint_facet_type_info.impls_constraints.size() ==
|
|
1);
|
|
|
|
if (deduced_constraint_facet_type_info.other_requirements) {
|
|
// TODO: Remove this when other requirements goes away.
|
|
return EvalImplLookupResult::MakeNone();
|
|
}
|
|
|
|
// The specifics in the queried interface must match the deduced specifics in
|
|
// the impl's constraint facet type.
|
|
auto impl_interface_specific_id =
|
|
deduced_constraint_facet_type_info.impls_constraints[0].specific_id;
|
|
auto query_interface_specific_id = interface.specific_id;
|
|
if (impl_interface_specific_id != query_interface_specific_id) {
|
|
return EvalImplLookupResult::MakeNone();
|
|
}
|
|
|
|
LoadImportRef(context, impl.witness_id);
|
|
if (specific_id.has_value()) {
|
|
// We need a definition of the specific `impl` so we can access its
|
|
// witness.
|
|
ResolveSpecificDefinition(context, loc_id, specific_id);
|
|
}
|
|
|
|
bool impl_is_effectively_final =
|
|
// TODO: impl.is_final ||
|
|
(context.constant_values().Get(impl.self_id).is_concrete() &&
|
|
context.constant_values().Get(impl.constraint_id).is_concrete());
|
|
if (query_is_concrete || impl_is_effectively_final) {
|
|
return EvalImplLookupResult::MakeFinal(
|
|
context.constant_values().GetInstId(SemIR::GetConstantValueInSpecific(
|
|
context.sem_ir(), specific_id, impl.witness_id)));
|
|
} else {
|
|
return EvalImplLookupResult::MakeNonFinal();
|
|
}
|
|
}
|
|
|
|
// In the case where `facet_const_id` is a facet, see if its facet type requires
|
|
// that `specific_interface` is implemented. If so, return the witness from the
|
|
// facet.
|
|
static auto FindWitnessInFacet(
|
|
Context& context, SemIR::LocId loc_id, SemIR::ConstantId facet_const_id,
|
|
const SemIR::SpecificInterface& specific_interface) -> SemIR::InstId {
|
|
SemIR::InstId facet_inst_id =
|
|
context.constant_values().GetInstId(facet_const_id);
|
|
SemIR::TypeId facet_type_id = context.insts().Get(facet_inst_id).type_id();
|
|
if (auto facet_type_inst =
|
|
context.types().TryGetAs<SemIR::FacetType>(facet_type_id)) {
|
|
const auto& facet_type_info =
|
|
context.facet_types().Get(facet_type_inst->facet_type_id);
|
|
// TODO: This depends on the index into `impls_constraints` matching
|
|
// the index into the facet type witness. This will have to be maintained
|
|
// even for facet types that include named constraints, once that is
|
|
// supported.
|
|
for (auto [index, interface] :
|
|
llvm::enumerate(facet_type_info.impls_constraints)) {
|
|
if (interface == specific_interface) {
|
|
auto witness_id =
|
|
GetOrAddInst(context, loc_id,
|
|
SemIR::FacetAccessWitness{
|
|
.type_id = GetSingletonType(
|
|
context, SemIR::WitnessType::SingletonInstId),
|
|
.facet_value_inst_id = facet_inst_id,
|
|
.index = SemIR::ElementIndex(index)});
|
|
return witness_id;
|
|
}
|
|
}
|
|
}
|
|
return SemIR::InstId::None;
|
|
}
|
|
|
|
// Begin a search for an impl declaration matching the query. We do this by
|
|
// creating an ImplSymbolicWitness instruction and evaluating. If it's able to
|
|
// find a final concrete impl, then it will evaluate to that `ImplWitness` but
|
|
// if not, it will evaluate to itself as a symbolic witness to be further
|
|
// evaluated with a more specific query when building a specific for the generic
|
|
// context the query came from.
|
|
static auto FindWitnessInImpls(Context& context, SemIR::LocId loc_id,
|
|
SemIR::ConstantId query_self_const_id,
|
|
SemIR::SpecificInterface interface)
|
|
-> SemIR::InstId {
|
|
auto witness_const_id = TryEvalInst(
|
|
context, loc_id, SemIR::InstId::None,
|
|
SemIR::ImplSymbolicWitness{
|
|
.type_id =
|
|
GetSingletonType(context, SemIR::WitnessType::SingletonInstId),
|
|
.query_self_inst_id =
|
|
context.constant_values().GetInstId(query_self_const_id),
|
|
.query_specific_interface_id =
|
|
context.specific_interfaces().Add(interface),
|
|
});
|
|
// We use a NotConstant result from eval to communicate back an impl
|
|
// lookup failure. See `EvalConstantInst()` for `ImplSymbolicWitness`.
|
|
if (!witness_const_id.is_constant()) {
|
|
return SemIR::InstId::None;
|
|
}
|
|
return context.constant_values().GetInstId(witness_const_id);
|
|
}
|
|
|
|
auto LookupImplWitness(Context& context, SemIR::LocId loc_id,
|
|
SemIR::ConstantId query_self_const_id,
|
|
SemIR::ConstantId query_facet_type_const_id)
|
|
-> SemIR::InstBlockIdOrError {
|
|
if (query_self_const_id == SemIR::ErrorInst::SingletonConstantId ||
|
|
query_facet_type_const_id == SemIR::ErrorInst::SingletonConstantId) {
|
|
return SemIR::InstBlockIdOrError::MakeError();
|
|
}
|
|
|
|
{
|
|
// The query self value is a type value or a facet value.
|
|
auto query_self_type_id =
|
|
context.insts()
|
|
.Get(context.constant_values().GetInstId(query_self_const_id))
|
|
.type_id();
|
|
CARBON_CHECK(context.types().Is<SemIR::TypeType>(query_self_type_id) ||
|
|
context.types().Is<SemIR::FacetType>(query_self_type_id));
|
|
// The query facet type value is indeed a facet type.
|
|
CARBON_CHECK(context.insts().Is<SemIR::FacetType>(
|
|
context.constant_values().GetInstId(query_facet_type_const_id)));
|
|
}
|
|
|
|
auto import_irs = FindAssociatedImportIRs(context, query_self_const_id,
|
|
query_facet_type_const_id);
|
|
for (auto import_ir : import_irs) {
|
|
// TODO: Instead of importing all impls, only import ones that are in some
|
|
// way connected to this query.
|
|
for (auto impl_index : llvm::seq(
|
|
context.import_irs().Get(import_ir).sem_ir->impls().size())) {
|
|
// TODO: Track the relevant impls and only consider those ones and any
|
|
// local impls, rather than looping over all impls below.
|
|
ImportImpl(context, import_ir, SemIR::ImplId(impl_index));
|
|
}
|
|
}
|
|
|
|
// If the self type is a FacetAccessType, work with the facet value directly,
|
|
// which gives us the potential witnesses to avoid looking for impl
|
|
// declarations. We will do the same for the impl declarations we try to match
|
|
// so that we can compare the self constant values.
|
|
if (auto access = context.insts().TryGetAs<SemIR::FacetAccessType>(
|
|
context.constant_values().GetInstId(query_self_const_id))) {
|
|
query_self_const_id =
|
|
context.constant_values().Get(access->facet_value_inst_id);
|
|
}
|
|
|
|
if (FindAndDiagnoseImplLookupCycle(context, context.impl_lookup_stack(),
|
|
loc_id, query_self_const_id,
|
|
query_facet_type_const_id)) {
|
|
return SemIR::InstBlockIdOrError::MakeError();
|
|
}
|
|
|
|
bool has_other_requirements = false;
|
|
auto interfaces = GetInterfacesFromConstantId(
|
|
context, query_facet_type_const_id, has_other_requirements);
|
|
if (has_other_requirements) {
|
|
// TODO: Remove this when other requirements go away.
|
|
return SemIR::InstBlockId::None;
|
|
}
|
|
if (interfaces.empty()) {
|
|
return SemIR::InstBlockId::Empty;
|
|
}
|
|
|
|
auto& stack = context.impl_lookup_stack();
|
|
stack.push_back({
|
|
.query_self_const_id = query_self_const_id,
|
|
.query_facet_type_const_id = query_facet_type_const_id,
|
|
});
|
|
// We need to find a witness for each interface in `interfaces`. Every
|
|
// consumer of a facet type needs to agree on the order of interfaces used for
|
|
// its witnesses.
|
|
llvm::SmallVector<SemIR::InstId> result_witness_ids;
|
|
for (const auto& interface : interfaces) {
|
|
// TODO: Since both `interfaces` and `query_self_const_id` are sorted lists,
|
|
// do an O(N+M) merge instead of O(N*M) nested loops.
|
|
auto result_witness_id =
|
|
FindWitnessInFacet(context, loc_id, query_self_const_id, interface);
|
|
if (!result_witness_id.has_value()) {
|
|
result_witness_id =
|
|
FindWitnessInImpls(context, loc_id, query_self_const_id, interface);
|
|
}
|
|
if (result_witness_id.has_value()) {
|
|
result_witness_ids.push_back(result_witness_id);
|
|
} else {
|
|
// At least one queried interface in the facet type has no witness for the
|
|
// given type, we can stop looking for more.
|
|
break;
|
|
}
|
|
}
|
|
stack.pop_back();
|
|
// TODO: Validate that the witness satisfies the other requirements in
|
|
// `interface_const_id`.
|
|
|
|
// All interfaces in the query facet type must have been found to be available
|
|
// through some impl, or directly on the value's facet type if
|
|
// `query_self_const_id` is a facet value.
|
|
if (result_witness_ids.size() != interfaces.size()) {
|
|
return SemIR::InstBlockId::None;
|
|
}
|
|
|
|
return context.inst_blocks().AddCanonical(result_witness_ids);
|
|
}
|
|
|
|
// Returns whether the query is concrete, it is false if the self type or
|
|
// interface specifics have a symbolic dependency.
|
|
static auto QueryIsConcrete(Context& context, SemIR::ConstantId self_const_id,
|
|
SemIR::SpecificInterface& specific_interface)
|
|
-> bool {
|
|
if (!self_const_id.is_concrete()) {
|
|
return false;
|
|
}
|
|
if (!specific_interface.specific_id.has_value()) {
|
|
return true;
|
|
}
|
|
auto args_id =
|
|
context.specifics().Get(specific_interface.specific_id).args_id;
|
|
for (auto inst_id : context.inst_blocks().Get(args_id)) {
|
|
if (!context.constant_values().Get(inst_id).is_concrete()) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
struct CandidateImpl {
|
|
SemIR::ImplId impl_id;
|
|
SemIR::InstId loc_inst_id;
|
|
|
|
// Used for sorting the candidates to find the most-specialized match.
|
|
TypeStructure type_structure;
|
|
};
|
|
|
|
// Returns the list of candidates impls for lookup to select from.
|
|
static auto CollectCandidateImplsForQuery(
|
|
Context& context, const TypeStructure& query_type_structure,
|
|
SemIR::SpecificInterface& query_specific_interface)
|
|
-> llvm::SmallVector<CandidateImpl> {
|
|
llvm::SmallVector<CandidateImpl> candidate_impls;
|
|
for (auto [id, impl] : context.impls().enumerate()) {
|
|
// If the impl's interface_id differs from the query, then this impl can
|
|
// not possibly provide the queried interface.
|
|
if (impl.interface.interface_id != query_specific_interface.interface_id) {
|
|
continue;
|
|
}
|
|
|
|
// When the impl's interface_id matches, but the interface is generic, the
|
|
// impl may or may not match based on restrictions in the generic
|
|
// parameters of the impl.
|
|
//
|
|
// As a shortcut, if the impl's constraint is not symbolic (does not
|
|
// depend on any generic parameters), then we can determine whether we match
|
|
// by looking if the specific ids match exactly.
|
|
auto impl_interface_const_id =
|
|
context.constant_values().Get(impl.constraint_id);
|
|
if (!impl_interface_const_id.is_symbolic() &&
|
|
impl.interface.specific_id != query_specific_interface.specific_id) {
|
|
continue;
|
|
}
|
|
|
|
// This check comes first to avoid deduction with an invalid impl. We use
|
|
// an error value to indicate an error during creation of the impl, such
|
|
// as a recursive impl which will cause deduction to recurse infinitely.
|
|
if (impl.witness_id == SemIR::ErrorInst::SingletonInstId) {
|
|
continue;
|
|
}
|
|
CARBON_CHECK(impl.witness_id.has_value());
|
|
|
|
// Build the type structure used for choosing the best the candidate.
|
|
auto type_structure =
|
|
BuildTypeStructure(context, impl.self_id, impl.interface);
|
|
// TODO: We can skip the comparison here if the `impl_interface_const_id` is
|
|
// not symbolic, since when the interface and specific ids match, and they
|
|
// aren't symbolic, the structure will be identical.
|
|
if (!query_type_structure.IsCompatibleWith(type_structure)) {
|
|
continue;
|
|
}
|
|
|
|
candidate_impls.push_back(
|
|
{id, impl.definition_id, std::move(type_structure)});
|
|
}
|
|
|
|
auto compare = [](auto& lhs, auto& rhs) -> bool {
|
|
// TODO: Allow Carbon code to provide a priority ordering explicitly. For
|
|
// now they have all the same priority, so the priority is the order in
|
|
// which they are found in code.
|
|
|
|
// Sort by their type structures. Higher value in type structure comes
|
|
// first, so we use `>` comparison.
|
|
return lhs.type_structure > rhs.type_structure;
|
|
};
|
|
// Stable sort is used so that impls that are seen first are preferred when
|
|
// they have an equal priority ordering.
|
|
llvm::stable_sort(candidate_impls, compare);
|
|
|
|
return candidate_impls;
|
|
}
|
|
|
|
auto EvalLookupSingleImplWitness(Context& context, SemIR::LocId loc_id,
|
|
SemIR::ImplSymbolicWitness eval_query)
|
|
-> EvalImplLookupResult {
|
|
SemIR::ConstantId query_self_const_id =
|
|
context.constant_values().Get(eval_query.query_self_inst_id);
|
|
SemIR::SpecificInterfaceId query_specific_interface_id =
|
|
eval_query.query_specific_interface_id;
|
|
|
|
// NOTE: Do not retain this reference to the SpecificInterface obtained from a
|
|
// value store by SpecificInterfaceId. Doing impl lookup does deduce which can
|
|
// do more impl lookups, and impl lookup can add a new SpecificInterface to
|
|
// the store which can reallocate and invalidate any references held here into
|
|
// the store.
|
|
auto query_specific_interface =
|
|
context.specific_interfaces().Get(query_specific_interface_id);
|
|
|
|
// When the query is a concrete FacetValue, we want to look through it at the
|
|
// underlying type to find all interfaces it implements. This supports
|
|
// conversion from a FacetValue to any other possible FacetValue, since
|
|
// conversion depends on impl lookup to verify it is a valid type change. See
|
|
// https://github.com/carbon-language/carbon-lang/issues/5137. We can't do
|
|
// this step earlier than inside impl lookup since:
|
|
// - We want the converted facet value to be preserved in
|
|
// `FindWitnessInFacet()` to avoid looking for impl declarations.
|
|
// - The constant self value may be modified during constant evaluation as a
|
|
// more specific value is found.
|
|
if (auto facet_value = context.insts().TryGetAs<SemIR::FacetValue>(
|
|
context.constant_values().GetInstId(query_self_const_id))) {
|
|
query_self_const_id =
|
|
context.constant_values().Get(facet_value->type_inst_id);
|
|
}
|
|
|
|
auto query_type_structure = BuildTypeStructure(
|
|
context, context.constant_values().GetInstId(query_self_const_id),
|
|
query_specific_interface);
|
|
bool query_is_concrete =
|
|
QueryIsConcrete(context, query_self_const_id, query_specific_interface);
|
|
|
|
auto candidate_impls = CollectCandidateImplsForQuery(
|
|
context, query_type_structure, query_specific_interface);
|
|
|
|
for (const auto& candidate : candidate_impls) {
|
|
// In deferred lookup for a symbolic impl witness, while building a
|
|
// specific, there may be no stack yet as this may be the first lookup. If
|
|
// further lookups are started as a result in deduce, they will build the
|
|
// stack.
|
|
//
|
|
// NOTE: Don't retain a reference into the stack, it may be invalidated if
|
|
// we do further impl lookups when GetWitnessIdForImpl() does deduction.
|
|
if (!context.impl_lookup_stack().empty()) {
|
|
context.impl_lookup_stack().back().impl_loc = candidate.loc_inst_id;
|
|
}
|
|
|
|
// NOTE: GetWitnessIdForImpl() does deduction, which can cause new impls
|
|
// to be imported, invalidating any pointer into `context.impls()`.
|
|
auto result = GetWitnessIdForImpl(
|
|
context, loc_id, query_is_concrete, query_self_const_id,
|
|
query_specific_interface, candidate.impl_id);
|
|
if (result.has_value()) {
|
|
return result;
|
|
}
|
|
}
|
|
return EvalImplLookupResult::MakeNone();
|
|
}
|
|
|
|
} // namespace Carbon::Check
|