|
|
@@ -158,10 +158,9 @@ static auto FindAssociatedImportIRs(
|
|
|
|
|
|
// Returns true if a cycle was found and diagnosed.
|
|
|
static auto FindAndDiagnoseImplLookupCycle(
|
|
|
- Context& context,
|
|
|
- const llvm::SmallVector<Context::ImplLookupStackEntry>& stack,
|
|
|
+ Context& context, llvm::SmallVector<Context::ImplLookupStackEntry>& stack,
|
|
|
SemIR::LocId loc_id, SemIR::ConstantId query_self_const_id,
|
|
|
- SemIR::ConstantId query_facet_type_const_id) -> bool {
|
|
|
+ SemIR::ConstantId query_facet_type_const_id, bool diagnose) -> bool {
|
|
|
// Deduction of the interface parameters can do further impl lookups, and we
|
|
|
// need to ensure we terminate.
|
|
|
//
|
|
|
@@ -177,22 +176,26 @@ static auto FindAndDiagnoseImplLookupCycle(
|
|
|
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);
|
|
|
+ if (diagnose && !stack.back().diagnosed_cycle) {
|
|
|
+ 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();
|
|
|
}
|
|
|
- builder.Emit();
|
|
|
+
|
|
|
+ stack.back().diagnosed_cycle = true;
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
@@ -209,7 +212,7 @@ struct RequiredImplsFromConstraint {
|
|
|
static auto GetRequiredImplsFromConstraint(
|
|
|
Context& context, SemIR::LocId loc_id,
|
|
|
SemIR::ConstantId query_self_const_id,
|
|
|
- SemIR::ConstantId query_facet_type_const_id)
|
|
|
+ SemIR::ConstantId query_facet_type_const_id, bool diagnose)
|
|
|
-> std::optional<RequiredImplsFromConstraint> {
|
|
|
auto facet_type_inst_id =
|
|
|
context.constant_values().GetInstId(query_facet_type_const_id);
|
|
|
@@ -225,7 +228,8 @@ static auto GetRequiredImplsFromConstraint(
|
|
|
"facet type {0} can not be identified", InstIdAsType);
|
|
|
builder.Context(loc_id, ImplLookupInUnidentifiedFacetType,
|
|
|
facet_type_inst_id);
|
|
|
- });
|
|
|
+ },
|
|
|
+ diagnose);
|
|
|
if (!identified_id.has_value()) {
|
|
|
return std::nullopt;
|
|
|
}
|
|
|
@@ -235,12 +239,24 @@ static auto GetRequiredImplsFromConstraint(
|
|
|
.other_requirements = facet_type_info.other_requirements}};
|
|
|
}
|
|
|
|
|
|
-static auto GetWitnessIdForImpl(Context& context, SemIR::LocId loc_id,
|
|
|
- bool query_is_concrete,
|
|
|
- SemIR::ConstantId query_self_const_id,
|
|
|
- const SemIR::SpecificInterface& interface,
|
|
|
- const SemIR::Impl& impl)
|
|
|
- -> EvalImplLookupResult {
|
|
|
+static auto TreatImplAsFinal(Context& context, const SemIR::Impl& impl)
|
|
|
+ -> bool {
|
|
|
+ // Lookups for the impl inside its own definition treat the impl as final.
|
|
|
+ // Nothing can specialize those lookups further, and it resolves any accesses
|
|
|
+ // of associated constants to their concrete values.
|
|
|
+ return IsImplEffectivelyFinal(context, impl) || impl.is_being_defined();
|
|
|
+}
|
|
|
+
|
|
|
+// Given a (possibly generic) `impl`, deduce a specific `impl` from the query
|
|
|
+// self and specific for the interface. Return the witness ID of the `impl` of
|
|
|
+// the resulting specific `impl`, if its specific interface matches the query.
|
|
|
+//
|
|
|
+// Note the witness also has the specific for the `impl` applied to it.
|
|
|
+static auto TryGetSpecificWitnessIdForImpl(
|
|
|
+ Context& context, SemIR::LocId loc_id,
|
|
|
+ SemIR::ConstantId query_self_const_id,
|
|
|
+ const SemIR::SpecificInterface& interface, const SemIR::Impl& impl)
|
|
|
+ -> SemIR::ConstantId {
|
|
|
// 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.
|
|
|
@@ -249,7 +265,7 @@ static auto GetWitnessIdForImpl(Context& context, SemIR::LocId loc_id,
|
|
|
specific_id = DeduceImplArguments(
|
|
|
context, loc_id, impl, query_self_const_id, interface.specific_id);
|
|
|
if (!specific_id.has_value()) {
|
|
|
- return EvalImplLookupResult::MakeNone();
|
|
|
+ return SemIR::ConstantId::None;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -265,7 +281,7 @@ static auto GetWitnessIdForImpl(Context& context, SemIR::LocId loc_id,
|
|
|
auto deduced_self_const_id =
|
|
|
GetCanonicalFacetOrTypeValue(context, noncanonical_deduced_self_const_id);
|
|
|
if (query_self_const_id != deduced_self_const_id) {
|
|
|
- return EvalImplLookupResult::MakeNone();
|
|
|
+ return SemIR::ConstantId::None;
|
|
|
}
|
|
|
|
|
|
// The impl's constraint is a facet type which it is implementing for the self
|
|
|
@@ -276,7 +292,7 @@ static auto GetWitnessIdForImpl(Context& context, SemIR::LocId loc_id,
|
|
|
context.constant_values().GetInstId(SemIR::GetConstantValueInSpecific(
|
|
|
context.sem_ir(), specific_id, impl.constraint_id));
|
|
|
if (deduced_constraint_id == SemIR::ErrorInst::InstId) {
|
|
|
- return EvalImplLookupResult::MakeNone();
|
|
|
+ return SemIR::ConstantId::None;
|
|
|
}
|
|
|
|
|
|
auto deduced_constraint_facet_type_id =
|
|
|
@@ -289,7 +305,7 @@ static auto GetWitnessIdForImpl(Context& context, SemIR::LocId loc_id,
|
|
|
1);
|
|
|
|
|
|
if (deduced_constraint_facet_type_info.other_requirements) {
|
|
|
- return EvalImplLookupResult::MakeNone();
|
|
|
+ return SemIR::ConstantId::None;
|
|
|
}
|
|
|
|
|
|
// The specifics in the queried interface must match the deduced specifics in
|
|
|
@@ -298,15 +314,19 @@ static auto GetWitnessIdForImpl(Context& context, SemIR::LocId loc_id,
|
|
|
deduced_constraint_facet_type_info.extend_constraints[0].specific_id;
|
|
|
auto query_interface_specific_id = interface.specific_id;
|
|
|
if (impl_interface_specific_id != query_interface_specific_id) {
|
|
|
- return EvalImplLookupResult::MakeNone();
|
|
|
+ return SemIR::ConstantId::None;
|
|
|
}
|
|
|
|
|
|
LoadImportRef(context, impl.witness_id);
|
|
|
- if (specific_id.has_value()) {
|
|
|
+ if (!impl.is_being_defined() && specific_id.has_value()) {
|
|
|
// If the impl definition can be resolved, eval will do it immediately;
|
|
|
// otherwise, it can be resolved by further specialization. This is used to
|
|
|
// resolve dependency chains when `MakeFinal` is returned without a concrete
|
|
|
// definition; particularly final impls with symbolic constants.
|
|
|
+ //
|
|
|
+ // Note we do not do this for lookups _inside_ the definition of the impl,
|
|
|
+ // as that creates a cycle where resolving the definition must resolve the
|
|
|
+ // definition.
|
|
|
AddInstInNoBlock(
|
|
|
context, loc_id,
|
|
|
SemIR::RequireSpecificDefinition{
|
|
|
@@ -315,77 +335,27 @@ static auto GetWitnessIdForImpl(Context& context, SemIR::LocId loc_id,
|
|
|
.specific_id = specific_id});
|
|
|
}
|
|
|
|
|
|
- if (query_is_concrete || impl.is_final) {
|
|
|
- // TODO: These final results should be cached somehow. Positive (non-None)
|
|
|
- // results could be cached globally, as they can not change. But
|
|
|
- // negative results can change after a final impl is written, so
|
|
|
- // they can only be cached in a limited way, or the cache needs to
|
|
|
- // be invalidated by writing a final impl that would match.
|
|
|
- return EvalImplLookupResult::MakeFinal(
|
|
|
- context.constant_values().GetInstId(SemIR::GetConstantValueInSpecific(
|
|
|
- context.sem_ir(), specific_id, impl.witness_id)));
|
|
|
- } else {
|
|
|
- return EvalImplLookupResult::MakeNonFinal();
|
|
|
- }
|
|
|
+ return SemIR::GetConstantValueInSpecific(context.sem_ir(), specific_id,
|
|
|
+ impl.witness_id);
|
|
|
}
|
|
|
|
|
|
-// Finds a lookup result from `query_self_inst_id` if it is a facet value that
|
|
|
-// names the query interface in its facet type. Note that `query_self_inst_id`
|
|
|
-// is allowed to be a non-canonical facet value in order to find a concrete
|
|
|
-// witness, so it's not referenced as a constant value.
|
|
|
-static auto LookupImplWitnessInSelfFacetValue(
|
|
|
- Context& context, SemIR::LocId loc_id,
|
|
|
- SemIR::InstId self_facet_value_inst_id,
|
|
|
- SemIR::SpecificInterface query_specific_interface) -> EvalImplLookupResult {
|
|
|
+// Identify the facet type of the query self. It is allowed to be partially
|
|
|
+// identified.
|
|
|
+static auto IdentifyQuerySelfFacetType(Context& context, SemIR::LocId loc_id,
|
|
|
+ SemIR::ConstantId query_self_const_id)
|
|
|
+ -> SemIR::IdentifiedFacetTypeId {
|
|
|
+ auto query_self_inst_id =
|
|
|
+ context.constant_values().GetInstId(query_self_const_id);
|
|
|
+
|
|
|
auto facet_type = context.types().TryGetAs<SemIR::FacetType>(
|
|
|
- context.insts().Get(self_facet_value_inst_id).type_id());
|
|
|
+ context.insts().Get(query_self_inst_id).type_id());
|
|
|
if (!facet_type) {
|
|
|
- return EvalImplLookupResult::MakeNone();
|
|
|
- }
|
|
|
-
|
|
|
- auto self_facet_value_const_id =
|
|
|
- context.constant_values().Get(self_facet_value_inst_id);
|
|
|
-
|
|
|
- // The position of the interface in `required_impls()` is also the
|
|
|
- // position of the witness for that interface in `FacetValue`. The
|
|
|
- // `FacetValue` witnesses are the output of an impl lookup, which finds and
|
|
|
- // returns witnesses in the same order.
|
|
|
- auto identified_id = RequireIdentifiedFacetType(
|
|
|
- context, loc_id, self_facet_value_const_id, *facet_type,
|
|
|
- [&](auto& builder) {
|
|
|
- CARBON_DIAGNOSTIC(ImplLookupInUnidentifiedFacetTypeOfQuerySelf, Context,
|
|
|
- "facet type of value {0} can not be identified",
|
|
|
- InstIdAsType);
|
|
|
- builder.Context(loc_id, ImplLookupInUnidentifiedFacetTypeOfQuerySelf,
|
|
|
- self_facet_value_inst_id);
|
|
|
- });
|
|
|
- if (!identified_id.has_value()) {
|
|
|
- return EvalImplLookupResult::MakeNone();
|
|
|
- }
|
|
|
- auto facet_type_req_impls = llvm::enumerate(
|
|
|
- context.identified_facet_types().Get(identified_id).required_impls());
|
|
|
- auto it = llvm::find_if(facet_type_req_impls, [&](auto e) {
|
|
|
- auto [req_self, req_specific_interface] = e.value();
|
|
|
- // The `self_facet_value_inst_id` in eval is a canonicalized facet value, as
|
|
|
- // is the self in the identified facet type.
|
|
|
- return req_self == self_facet_value_const_id &&
|
|
|
- req_specific_interface == query_specific_interface;
|
|
|
- });
|
|
|
- if (it == facet_type_req_impls.end()) {
|
|
|
- return EvalImplLookupResult::MakeNone();
|
|
|
+ return SemIR::IdentifiedFacetTypeId::None;
|
|
|
}
|
|
|
- auto index = (*it).index();
|
|
|
|
|
|
- if (auto facet_value = context.insts().TryGetAs<SemIR::FacetValue>(
|
|
|
- self_facet_value_inst_id)) {
|
|
|
- auto witness_id =
|
|
|
- context.inst_blocks().Get(facet_value->witnesses_block_id)[index];
|
|
|
- if (context.insts().IsOneOf<SemIR::ImplWitness, SemIR::CustomWitness>(
|
|
|
- witness_id)) {
|
|
|
- return EvalImplLookupResult::MakeFinal(witness_id);
|
|
|
- }
|
|
|
- }
|
|
|
- return EvalImplLookupResult::MakeNonFinal();
|
|
|
+ return TryToIdentifyFacetType(context, loc_id, query_self_const_id,
|
|
|
+ *facet_type,
|
|
|
+ /*allow_partially_identified=*/true);
|
|
|
}
|
|
|
|
|
|
// Substitutes witnesess in place of `LookupImplWitness` queries into `.Self`,
|
|
|
@@ -561,120 +531,6 @@ static auto VerifyQueryFacetTypeConstraints(
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-// Begin a search for an impl declaration matching the query. We do this by
|
|
|
-// creating an LookupImplWitness 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 GetOrAddLookupImplWitness(Context& context, SemIR::LocId loc_id,
|
|
|
- SemIR::ConstantId query_self_const_id,
|
|
|
- SemIR::SpecificInterface interface)
|
|
|
- -> SemIR::InstId {
|
|
|
- auto witness_const_id = EvalOrAddInst(
|
|
|
- context, context.insts().GetLocIdForDesugaring(loc_id),
|
|
|
- SemIR::LookupImplWitness{
|
|
|
- .type_id = GetSingletonType(context, SemIR::WitnessType::TypeInstId),
|
|
|
- .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 `LookupImplWitness`.
|
|
|
- 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::ConstantId ||
|
|
|
- query_facet_type_const_id == SemIR::ErrorInst::ConstantId) {
|
|
|
- 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().IsOneOf<SemIR::TypeType, 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 req_impls_from_constraint = GetRequiredImplsFromConstraint(
|
|
|
- context, loc_id, query_self_const_id, query_facet_type_const_id);
|
|
|
- if (!req_impls_from_constraint) {
|
|
|
- return SemIR::InstBlockIdOrError::MakeError();
|
|
|
- }
|
|
|
- auto [req_impls, other_requirements] = *req_impls_from_constraint;
|
|
|
- if (other_requirements) {
|
|
|
- // TODO: Remove this when other requirements go away.
|
|
|
- return SemIR::InstBlockId::None;
|
|
|
- }
|
|
|
- if (req_impls.empty()) {
|
|
|
- return SemIR::InstBlockId::Empty;
|
|
|
- }
|
|
|
-
|
|
|
- if (FindAndDiagnoseImplLookupCycle(context, context.impl_lookup_stack(),
|
|
|
- loc_id, query_self_const_id,
|
|
|
- query_facet_type_const_id)) {
|
|
|
- return SemIR::InstBlockIdOrError::MakeError();
|
|
|
- }
|
|
|
-
|
|
|
- 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 self+interface pair in `req_impls`.
|
|
|
- //
|
|
|
- // Every consumer of a facet type needs to agree on the order of interfaces
|
|
|
- // used for its witnesses, which is done by following the order in the
|
|
|
- // IdentifiedFacetType.
|
|
|
- llvm::SmallVector<SemIR::InstId> result_witness_ids;
|
|
|
- for (const auto& req_impl : req_impls) {
|
|
|
- // 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 =
|
|
|
- GetOrAddLookupImplWitness(context, loc_id, req_impl.self_facet_value,
|
|
|
- req_impl.specific_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();
|
|
|
-
|
|
|
- // 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() != req_impls.size()) {
|
|
|
- return SemIR::InstBlockId::None;
|
|
|
- }
|
|
|
-
|
|
|
- // Verify rewrite constraints in the query constraint are satisfied after
|
|
|
- // applying the rewrites from the found witnesses.
|
|
|
- if (!VerifyQueryFacetTypeConstraints(context, loc_id, query_self_const_id,
|
|
|
- query_facet_type_const_id, req_impls,
|
|
|
- result_witness_ids)) {
|
|
|
- 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,
|
|
|
@@ -815,7 +671,7 @@ static auto CollectCandidateImplsForQuery(
|
|
|
for (auto [id, impl] : context.impls().enumerate()) {
|
|
|
CARBON_CHECK(impl.witness_id.has_value());
|
|
|
|
|
|
- if (final_only && !IsImplEffectivelyFinal(context, impl)) {
|
|
|
+ if (final_only && !TreatImplAsFinal(context, impl)) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
@@ -870,36 +726,355 @@ static auto CollectCandidateImplsForQuery(
|
|
|
return candidates;
|
|
|
}
|
|
|
|
|
|
+class IndexInFacetValue {
|
|
|
+ public:
|
|
|
+ static const IndexInFacetValue None;
|
|
|
+ static const IndexInFacetValue Unstable;
|
|
|
+
|
|
|
+ explicit constexpr IndexInFacetValue(int32_t index) : index_(index) {}
|
|
|
+
|
|
|
+ // Returns whether the value represents a successful attempt to find the index
|
|
|
+ // of an interface in a FacetValue. Returns true regardless of whether the
|
|
|
+ // index is stable and able to be used or not.
|
|
|
+ auto WasFound() const -> bool { return index_ != None.index_; }
|
|
|
+
|
|
|
+ // Gets the stable index which can be used to index into the witness table in
|
|
|
+ // a FacetValue, if there is one. Otherwise, returns -1.
|
|
|
+ auto GetStableIndex() const -> int32_t {
|
|
|
+ if (index_ == Unstable.index_) {
|
|
|
+ return None.index_;
|
|
|
+ }
|
|
|
+ return index_;
|
|
|
+ }
|
|
|
+
|
|
|
+ private:
|
|
|
+ int32_t index_;
|
|
|
+};
|
|
|
+
|
|
|
+inline constexpr auto IndexInFacetValue::None = IndexInFacetValue(-1);
|
|
|
+inline constexpr auto IndexInFacetValue::Unstable = IndexInFacetValue(-2);
|
|
|
+
|
|
|
+// Looks in the facet type of the query self facet value and returns the index
|
|
|
+// of `query_specific_interface` in the defined interface order for that facet
|
|
|
+// type. The order comes from the `query_self_type_identified_id` which must be
|
|
|
+// the IdentifiedFacetType of the type of `query_self_const_id `.
|
|
|
+//
|
|
|
+// If the query self is not a facet value, the IdentifiedFacetType would be
|
|
|
+// None.
|
|
|
+//
|
|
|
+// The IdentifiedFacetType must not be partially identified in order to find an
|
|
|
+// index, as that implies the interface order is not yet stable. In that case,
|
|
|
+// no index will be found.
|
|
|
+//
|
|
|
+// If the `query_specific_interface` is not part of the facet type of the query
|
|
|
+// self, returns -1 to indicate it was not found.
|
|
|
+static auto IndexOfImplWitnessInSelfFacetValue(
|
|
|
+ Context& context, SemIR::ConstantId query_self_const_id,
|
|
|
+ SemIR::IdentifiedFacetTypeId query_self_type_identified_id,
|
|
|
+ SemIR::SpecificInterface query_specific_interface) -> IndexInFacetValue {
|
|
|
+ if (!query_self_type_identified_id.has_value()) {
|
|
|
+ return IndexInFacetValue::None;
|
|
|
+ }
|
|
|
+
|
|
|
+ // The self in the identified facet type is a canonicalized facet value, so we
|
|
|
+ // canonicalize the query for comparison.
|
|
|
+ auto canonical_query_self_const_id =
|
|
|
+ GetCanonicalFacetOrTypeValue(context, query_self_const_id);
|
|
|
+
|
|
|
+ const auto& identified =
|
|
|
+ context.identified_facet_types().Get(query_self_type_identified_id);
|
|
|
+ auto facet_type_req_impls = llvm::enumerate(identified.required_impls());
|
|
|
+ auto it = llvm::find_if(facet_type_req_impls, [&](auto e) {
|
|
|
+ auto [req_self, req_specific_interface] = e.value();
|
|
|
+ return req_self == canonical_query_self_const_id &&
|
|
|
+ req_specific_interface == query_specific_interface;
|
|
|
+ });
|
|
|
+ if (it == facet_type_req_impls.end()) {
|
|
|
+ return IndexInFacetValue::None;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (identified.partially_identified()) {
|
|
|
+ return IndexInFacetValue::Unstable;
|
|
|
+ }
|
|
|
+ return IndexInFacetValue(static_cast<int32_t>((*it).index()));
|
|
|
+}
|
|
|
+
|
|
|
+static auto FindFinalWitnessFromSelfFacetValue(
|
|
|
+ Context& context, SemIR::ConstantId query_self_const_id,
|
|
|
+ SemIR::IdentifiedFacetTypeId query_self_type_identified_id,
|
|
|
+ SemIR::SpecificInterface query_specific_interface) -> SemIR::InstId {
|
|
|
+ // TODO: Add and use constant_values().GetAs<SemIR::FacetType>().
|
|
|
+ auto query_self_inst_id =
|
|
|
+ context.constant_values().GetInstId(query_self_const_id);
|
|
|
+ auto facet_value =
|
|
|
+ context.insts().TryGetAs<SemIR::FacetValue>(query_self_inst_id);
|
|
|
+ if (!facet_value) {
|
|
|
+ return SemIR::InstId::None;
|
|
|
+ }
|
|
|
+
|
|
|
+ auto index_in_facet_value = IndexOfImplWitnessInSelfFacetValue(
|
|
|
+ context, query_self_const_id, query_self_type_identified_id,
|
|
|
+ query_specific_interface);
|
|
|
+ auto stable_index = index_in_facet_value.GetStableIndex();
|
|
|
+ if (stable_index < 0) {
|
|
|
+ return SemIR::InstId::None;
|
|
|
+ }
|
|
|
+
|
|
|
+ auto witness_id =
|
|
|
+ context.inst_blocks().Get(facet_value->witnesses_block_id)[stable_index];
|
|
|
+ if (context.insts().Is<SemIR::LookupImplWitness>(witness_id)) {
|
|
|
+ // Did not find a final witness.
|
|
|
+ return SemIR::InstId::None;
|
|
|
+ }
|
|
|
+
|
|
|
+ return witness_id;
|
|
|
+}
|
|
|
+
|
|
|
+static auto FindNonFinalWitness(
|
|
|
+ Context& context, SemIR::LocId loc_id,
|
|
|
+ SemIR::ConstantId query_self_const_id,
|
|
|
+ SemIR::IdentifiedFacetTypeId query_self_type_identified_id,
|
|
|
+ SemIR::SpecificInterface query_specific_interface) -> bool {
|
|
|
+ auto index = IndexOfImplWitnessInSelfFacetValue(context, query_self_const_id,
|
|
|
+ query_self_type_identified_id,
|
|
|
+ query_specific_interface);
|
|
|
+ if (index.WasFound()) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // TODO: Remove SpecificInterfaceId from LookupCustomWitness apis, switch to
|
|
|
+ // just SpecificInterface.
|
|
|
+ auto query_specific_interface_id =
|
|
|
+ context.specific_interfaces().Add(query_specific_interface);
|
|
|
+
|
|
|
+ // Consider a custom witness for core interfaces.
|
|
|
+ // TODO: This needs to expand to more interfaces, and we might want to have
|
|
|
+ // that dispatch in custom_witness.cpp instead of here.
|
|
|
+ auto core_interface =
|
|
|
+ GetCoreInterface(context, query_specific_interface.interface_id);
|
|
|
+ if (auto witness_id = LookupCustomWitness(
|
|
|
+ context, loc_id, core_interface, query_self_const_id,
|
|
|
+ query_specific_interface_id, false)) {
|
|
|
+ // If there's a final witness, we would have already found it via evaluating
|
|
|
+ // the LookupImplWitness instruction.
|
|
|
+ CARBON_CHECK(!witness_id->has_value());
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ auto query_type_structure = BuildTypeStructure(
|
|
|
+ context, context.constant_values().GetInstId(query_self_const_id),
|
|
|
+ query_specific_interface);
|
|
|
+ // We looked for errors in the query self and facet type already, and we're
|
|
|
+ // not dealing with monomorphizations here.
|
|
|
+ CARBON_CHECK(query_type_structure, "error in impl lookup query");
|
|
|
+
|
|
|
+ auto candidates = CollectCandidateImplsForQuery(
|
|
|
+ context, /*final_only=*/false, query_self_const_id, *query_type_structure,
|
|
|
+ query_specific_interface);
|
|
|
+
|
|
|
+ for (const auto& candidate : candidates.impls) {
|
|
|
+ const auto& impl = *candidate.impl;
|
|
|
+ context.impl_lookup_stack().back().impl_loc = impl.definition_id;
|
|
|
+
|
|
|
+ auto witness_id = TryGetSpecificWitnessIdForImpl(
|
|
|
+ context, loc_id, query_self_const_id, query_specific_interface, impl);
|
|
|
+ if (witness_id.has_value()) {
|
|
|
+ // We looked for errors in the query self and facet type already, and
|
|
|
+ // we're not dealing with monomorphizations here.
|
|
|
+ CARBON_CHECK(witness_id != SemIR::ErrorInst::ConstantId,
|
|
|
+ "error in impl lookup query");
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // C++ interop only provides final witnesses, so we don't look for a witness
|
|
|
+ // from C++ here. Those are found in eval of the `LookupImplWitness`
|
|
|
+ // instruction.
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+auto LookupImplWitness(Context& context, SemIR::LocId loc_id,
|
|
|
+ SemIR::ConstantId query_self_const_id,
|
|
|
+ SemIR::ConstantId query_facet_type_const_id,
|
|
|
+ bool diagnose) -> SemIR::InstBlockIdOrError {
|
|
|
+ if (query_self_const_id == SemIR::ErrorInst::ConstantId ||
|
|
|
+ query_facet_type_const_id == SemIR::ErrorInst::ConstantId) {
|
|
|
+ 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().IsOneOf<SemIR::TypeType, 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 req_impls_from_constraint =
|
|
|
+ GetRequiredImplsFromConstraint(context, loc_id, query_self_const_id,
|
|
|
+ query_facet_type_const_id, diagnose);
|
|
|
+ if (!req_impls_from_constraint) {
|
|
|
+ return SemIR::InstBlockIdOrError::MakeError();
|
|
|
+ }
|
|
|
+ auto [req_impls, other_requirements] = *req_impls_from_constraint;
|
|
|
+ if (other_requirements) {
|
|
|
+ // TODO: Remove this when other requirements go away.
|
|
|
+ return SemIR::InstBlockId::None;
|
|
|
+ }
|
|
|
+ if (req_impls.empty()) {
|
|
|
+ return SemIR::InstBlockId::Empty;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Cycles are diagnosed even if they are found when diagnostics are otherwise
|
|
|
+ // being suppressed (such as during deduce).
|
|
|
+ if (FindAndDiagnoseImplLookupCycle(context, context.impl_lookup_stack(),
|
|
|
+ loc_id, query_self_const_id,
|
|
|
+ query_facet_type_const_id, true)) {
|
|
|
+ return SemIR::InstBlockIdOrError::MakeError();
|
|
|
+ }
|
|
|
+
|
|
|
+ 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,
|
|
|
+ .diagnosed_cycle = stack.empty() ? false : stack.back().diagnosed_cycle,
|
|
|
+ });
|
|
|
+ // We need to find a witness for each self+interface pair in `req_impls`.
|
|
|
+ //
|
|
|
+ // Every consumer of a facet type needs to agree on the order of interfaces
|
|
|
+ // used for its witnesses, which is done by following the order in the
|
|
|
+ // IdentifiedFacetType of the query facet type, and this is represented in the
|
|
|
+ // order of the interfaces in `req_impls`.
|
|
|
+ llvm::SmallVector<SemIR::InstId> result_witness_ids;
|
|
|
+ for (const auto& req_impl : req_impls) {
|
|
|
+ // Identify the type of the requirement's self up front, if it's a facet, so
|
|
|
+ // we only have to do this once.
|
|
|
+ auto req_self_type_identified_id =
|
|
|
+ IdentifyQuerySelfFacetType(context, loc_id, req_impl.self_facet_value);
|
|
|
+
|
|
|
+ // If the self facet contains a final witness for the required interface, we
|
|
|
+ // use that and avoid any further work. This is strictly an optimization,
|
|
|
+ // since that same final witness should be found by evaluating a
|
|
|
+ // LookupImplWitness instruction for the required self+interface pair.
|
|
|
+ auto result_witness_id = FindFinalWitnessFromSelfFacetValue(
|
|
|
+ context, req_impl.self_facet_value, req_self_type_identified_id,
|
|
|
+ req_impl.specific_interface);
|
|
|
+ if (result_witness_id.has_value()) {
|
|
|
+ // Found a final witness, use it.
|
|
|
+ result_witness_ids.push_back(result_witness_id);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ auto witness_const_id = EvalOrAddInst<SemIR::LookupImplWitness>(
|
|
|
+ context, context.insts().GetLocIdForDesugaring(loc_id),
|
|
|
+ {.type_id = GetSingletonType(context, SemIR::WitnessType::TypeInstId),
|
|
|
+ .query_self_inst_id =
|
|
|
+ context.constant_values().GetInstId(req_impl.self_facet_value),
|
|
|
+ .query_specific_interface_id =
|
|
|
+ context.specific_interfaces().Add(req_impl.specific_interface)});
|
|
|
+ result_witness_id = context.constant_values().GetInstId(witness_const_id);
|
|
|
+ if (!context.insts().Is<SemIR::LookupImplWitness>(result_witness_id)) {
|
|
|
+ // Found a final witness, use it.
|
|
|
+ result_witness_ids.push_back(result_witness_id);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (QueryIsConcrete(context, req_impl.self_facet_value,
|
|
|
+ req_impl.specific_interface)) {
|
|
|
+ // Failed to find a final witness for a concrete query. There won't be a
|
|
|
+ // non-final witness, as any witness would have been treated as final.
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Did not find a final witness. If we find a non-final witness, then we use
|
|
|
+ // the `LookupImplWitness` as our witness so that monomorphization can
|
|
|
+ // produce a final witness later.
|
|
|
+ if (!FindNonFinalWitness(context, loc_id, req_impl.self_facet_value,
|
|
|
+ req_self_type_identified_id,
|
|
|
+ req_impl.specific_interface)) {
|
|
|
+ // At least one queried interface in the facet type has no witness for the
|
|
|
+ // given type, we can stop looking for more.
|
|
|
+ //
|
|
|
+ // TODO: The LookupImplWitness won't be used. We should find a way to
|
|
|
+ // discard it, which would remove it from the generic eval block if the
|
|
|
+ // lookup is within a generic context.
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Save the non-final witness, which will eventually resolve to a final
|
|
|
+ // witness as specifics are applied to make the query more concrete.
|
|
|
+ result_witness_ids.push_back(result_witness_id);
|
|
|
+ }
|
|
|
+ auto pop = stack.pop_back_val();
|
|
|
+ if (pop.diagnosed_cycle && !stack.empty()) {
|
|
|
+ stack.back().diagnosed_cycle = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 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() != req_impls.size()) {
|
|
|
+ return SemIR::InstBlockId::None;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Verify rewrite constraints in the query constraint are satisfied after
|
|
|
+ // applying the rewrites from the found witnesses.
|
|
|
+ if (!VerifyQueryFacetTypeConstraints(context, loc_id, query_self_const_id,
|
|
|
+ query_facet_type_const_id, req_impls,
|
|
|
+ result_witness_ids)) {
|
|
|
+ return SemIR::InstBlockId::None;
|
|
|
+ }
|
|
|
+
|
|
|
+ return context.inst_blocks().AddCanonical(result_witness_ids);
|
|
|
+}
|
|
|
+
|
|
|
// Record the query which found a final impl witness. It's illegal to
|
|
|
// write a final impl afterward that would match the same query.
|
|
|
static auto PoisonImplLookupQuery(Context& context, SemIR::LocId loc_id,
|
|
|
EvalImplLookupMode mode,
|
|
|
SemIR::LookupImplWitness eval_query,
|
|
|
- const EvalImplLookupResult& result,
|
|
|
+ SemIR::ConstantId witness_id,
|
|
|
const SemIR::Impl& impl) -> void {
|
|
|
if (mode == EvalImplLookupMode::RecheckPoisonedLookup) {
|
|
|
return;
|
|
|
}
|
|
|
- if (!result.has_final_value()) {
|
|
|
- return;
|
|
|
- }
|
|
|
// If the impl was effectively final, then we don't need to poison here. A
|
|
|
// change of query result will already be diagnosed at the point where the
|
|
|
// new impl decl was written that changes the result.
|
|
|
- if (IsImplEffectivelyFinal(context, impl)) {
|
|
|
+ if (TreatImplAsFinal(context, impl)) {
|
|
|
return;
|
|
|
}
|
|
|
context.poisoned_concrete_impl_lookup_queries().push_back(
|
|
|
- {.loc_id = loc_id,
|
|
|
- .query = eval_query,
|
|
|
- .impl_witness = result.final_witness()});
|
|
|
+ {.loc_id = loc_id, .query = eval_query, .witness_id = witness_id});
|
|
|
}
|
|
|
|
|
|
-auto EvalLookupSingleImplWitness(Context& context, SemIR::LocId loc_id,
|
|
|
- SemIR::LookupImplWitness eval_query,
|
|
|
- SemIR::InstId self_facet_value_inst_id,
|
|
|
- EvalImplLookupMode mode)
|
|
|
- -> EvalImplLookupResult {
|
|
|
+// Return whether the `FacetType` in `type_id` extends a single interface, and
|
|
|
+// that it matches `specific_interface`.
|
|
|
+static auto FacetTypeIsSingleInterface(
|
|
|
+ Context& context, SemIR::TypeId type_id,
|
|
|
+ SemIR::SpecificInterface specific_interface) -> bool {
|
|
|
+ auto facet_type = context.types().GetAs<SemIR::FacetType>(type_id);
|
|
|
+ const auto& facet_type_info =
|
|
|
+ context.facet_types().Get(facet_type.facet_type_id);
|
|
|
+ if (auto single = facet_type_info.TryAsSingleExtend()) {
|
|
|
+ if (auto* si = std::get_if<SemIR::SpecificInterface>(&*single)) {
|
|
|
+ return *si == specific_interface;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+auto EvalLookupSingleFinalWitness(Context& context, SemIR::LocId loc_id,
|
|
|
+ SemIR::LookupImplWitness eval_query,
|
|
|
+ SemIR::InstId self_facet_value_inst_id,
|
|
|
+ EvalImplLookupMode mode)
|
|
|
+ -> SemIR::ConstantId {
|
|
|
auto query_specific_interface =
|
|
|
context.specific_interfaces().Get(eval_query.query_specific_interface_id);
|
|
|
|
|
|
@@ -909,40 +1084,59 @@ auto EvalLookupSingleImplWitness(Context& context, SemIR::LocId loc_id,
|
|
|
SemIR::ConstantId query_self_const_id =
|
|
|
context.constant_values().Get(eval_query.query_self_inst_id);
|
|
|
|
|
|
- auto facet_lookup_result = LookupImplWitnessInSelfFacetValue(
|
|
|
- context, loc_id, self_facet_value_inst_id, query_specific_interface);
|
|
|
- if (facet_lookup_result.has_final_value()) {
|
|
|
- return facet_lookup_result;
|
|
|
- }
|
|
|
-
|
|
|
- // If the self type is a facet that provides a witness, then we are in an
|
|
|
- // `interface` or an `impl`. In both cases, we don't want to do any impl
|
|
|
- // lookups. The query will eventually resolve to a concrete witness when it
|
|
|
- // can get it from the self facet value, when it has a specific applied in the
|
|
|
- // future.
|
|
|
+ // If the query self is monomorphized as a FacetValue, we can't use its
|
|
|
+ // witnesses in general, since we are not allowed to identify facet types in
|
|
|
+ // monomorphization. And we need to identify it to know which witness is for
|
|
|
+ // which interface.
|
|
|
//
|
|
|
- // In particular, this avoids a LookupImplWitness instruction in the eval
|
|
|
- // block of an impl declaration from doing impl lookup. Specifically the
|
|
|
- // lookup of the implicit .Self in `impl ... where .X`. If it does impl lookup
|
|
|
- // when the eval block is run, it finds the same `impl`, tries to build a
|
|
|
- // specific from it, which runs the eval block, creating a recursive loop that
|
|
|
- // crashes.
|
|
|
- if (facet_lookup_result.has_value()) {
|
|
|
- if (auto bind = context.insts().TryGetAs<SemIR::SymbolicBinding>(
|
|
|
- eval_query.query_self_inst_id)) {
|
|
|
- const auto& entity = context.entity_names().Get(bind->entity_name_id);
|
|
|
- if (entity.name_id == SemIR::NameId::PeriodSelf ||
|
|
|
- entity.name_id == SemIR::NameId::SelfType) {
|
|
|
- return facet_lookup_result;
|
|
|
+ // However, if the facet type has only a single interface and it matches the
|
|
|
+ // query, then we can use the witness, since there is only one.
|
|
|
+ //
|
|
|
+ // This looks like an optimization, but it's done to prefer the FacetValue's
|
|
|
+ // witness over the cached value for monomorphizations of `Self` inside an
|
|
|
+ // `impl` definition. If a final witness was previously found for the same
|
|
|
+ // type as the monomorphized `Self`, the cache would reuse it. But associated
|
|
|
+ // constants may differ in that witness from the current `impl`'s witness
|
|
|
+ // which leads to inconsistency within the impl definition.
|
|
|
+ //
|
|
|
+ // By preferring the impl's FacetValue, the `impl` remains self-consistent
|
|
|
+ // even if it's ultimately not valid due to a conflict. When a conflict with
|
|
|
+ // another `impl` does exist, a poisoning error will occur showing the two
|
|
|
+ // `impl`s are in disagreement for a concrete value, as the poisoning lookup
|
|
|
+ // does not preserve the FacetValue.
|
|
|
+ if (auto facet_value = context.insts().TryGetAsIfValid<SemIR::FacetValue>(
|
|
|
+ self_facet_value_inst_id)) {
|
|
|
+ if (FacetTypeIsSingleInterface(context, facet_value->type_id,
|
|
|
+ query_specific_interface)) {
|
|
|
+ auto witnesses =
|
|
|
+ context.inst_blocks().Get(facet_value->witnesses_block_id);
|
|
|
+ CARBON_CHECK(witnesses.size() == 1);
|
|
|
+ auto witness_inst_id = witnesses.front();
|
|
|
+ // Only use the witness in monomoprhization if it's a final witness.
|
|
|
+ if (!context.insts().Is<SemIR::LookupImplWitness>(witness_inst_id)) {
|
|
|
+ return context.constant_values().Get(witness_inst_id);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- auto query_type_structure = BuildTypeStructure(
|
|
|
- context, context.constant_values().GetInstId(query_self_const_id),
|
|
|
- query_specific_interface);
|
|
|
- if (!query_type_structure) {
|
|
|
- return EvalImplLookupResult::MakeNone();
|
|
|
+ // If the query is on `.Self` and looking for the same interface as `.Self`
|
|
|
+ // provides, do not look for a witness in monomorphization - a non-final
|
|
|
+ // witness will be found from the facet type. This happens inside an `impl`
|
|
|
+ // declaration, and we must avoid finding that same `impl` and trying to
|
|
|
+ // deduce `.Self` for it, as that results in a specific declaration for the
|
|
|
+ // `impl` which evaluates this lookup again, producing a cycle.
|
|
|
+ //
|
|
|
+ // If the query is for `.Self` and for the facet type of `.Self`, then there
|
|
|
+ // is no final witness yet.
|
|
|
+ if (auto bind = context.insts().TryGetAs<SemIR::SymbolicBinding>(
|
|
|
+ eval_query.query_self_inst_id)) {
|
|
|
+ const auto& entity = context.entity_names().Get(bind->entity_name_id);
|
|
|
+ if (entity.name_id == SemIR::NameId::PeriodSelf) {
|
|
|
+ if (FacetTypeIsSingleInterface(context, bind->type_id,
|
|
|
+ query_specific_interface)) {
|
|
|
+ return SemIR::ConstantId::None;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// Check to see if this result is in the cache. But skip the cache if we're
|
|
|
@@ -952,37 +1146,37 @@ auto EvalLookupSingleImplWitness(Context& context, SemIR::LocId loc_id,
|
|
|
if (mode != EvalImplLookupMode::RecheckPoisonedLookup) {
|
|
|
if (auto result =
|
|
|
context.impl_lookup_cache().Lookup(impl_lookup_cache_key)) {
|
|
|
- return EvalImplLookupResult::MakeFinal(result.value());
|
|
|
+ return result.value();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // If the self value is a (symbolic) facet value that has a symbolic witness,
|
|
|
- // then we don't need to do impl lookup, except that we want to find any final
|
|
|
- // impls to return a concrete witness if possible. So we limit the query to
|
|
|
- // final impls only in that case. Note as in the CHECK above, the query can
|
|
|
- // not be concrete in this case, so only final impls can produce a concrete
|
|
|
- // witness for this query.
|
|
|
- auto candidates = CollectCandidateImplsForQuery(
|
|
|
- context, facet_lookup_result.has_value(), query_self_const_id,
|
|
|
- *query_type_structure, query_specific_interface);
|
|
|
-
|
|
|
bool query_is_concrete =
|
|
|
QueryIsConcrete(context, query_self_const_id, query_specific_interface);
|
|
|
- CARBON_CHECK(!query_is_concrete || !facet_lookup_result.has_value(),
|
|
|
- "Non-concrete facet lookup value for concrete query");
|
|
|
|
|
|
- // Perform a lookup for an `impl` that matches the query. If we don't find a
|
|
|
- // final impl, the self value may still have been a facet that provides a
|
|
|
- // symbolic witness in the `facet_lookup_result`, which we want to fall back
|
|
|
- // to. It records that an `impl` will exist for the query, but is yet unknown.
|
|
|
+ auto query_type_structure = BuildTypeStructure(
|
|
|
+ context, context.constant_values().GetInstId(query_self_const_id),
|
|
|
+ query_specific_interface);
|
|
|
+ if (!query_type_structure) {
|
|
|
+ // TODO: We should return an error here; an error was found in the type
|
|
|
+ // structure.
|
|
|
+ return SemIR::ConstantId::None;
|
|
|
+ }
|
|
|
+
|
|
|
+ // We only want to return final witneses in monomorphization. If the query is
|
|
|
+ // concrete, we can find all impls, otherwise we want only (effectively) final
|
|
|
+ // impls.
|
|
|
+ auto candidates = CollectCandidateImplsForQuery(
|
|
|
+ context, /*final_only=*/!query_is_concrete, query_self_const_id,
|
|
|
+ *query_type_structure, query_specific_interface);
|
|
|
|
|
|
struct LookupResult {
|
|
|
- EvalImplLookupResult result;
|
|
|
+ SemIR::ConstantId witness_id = SemIR::ConstantId::None;
|
|
|
+ // Holds a pointer into `candidates`.
|
|
|
const TypeStructure* impl_type_structure = nullptr;
|
|
|
SemIR::LocId impl_loc_id = SemIR::LocId::None;
|
|
|
};
|
|
|
|
|
|
- LookupResult lookup_result = {.result = facet_lookup_result};
|
|
|
+ LookupResult lookup_result;
|
|
|
|
|
|
auto core_interface =
|
|
|
GetCoreInterface(context, query_specific_interface.interface_id);
|
|
|
@@ -991,15 +1185,14 @@ auto EvalLookupSingleImplWitness(Context& context, SemIR::LocId loc_id,
|
|
|
// TODO: This needs to expand to more interfaces, and we might want to have
|
|
|
// that dispatch in custom_witness.cpp instead of here.
|
|
|
bool used_custom_witness = false;
|
|
|
- if (auto witness_id = LookupCustomWitness(
|
|
|
+ if (auto witness_inst_id = LookupCustomWitness(
|
|
|
context, loc_id, core_interface, query_self_const_id,
|
|
|
- eval_query.query_specific_interface_id)) {
|
|
|
- if (witness_id->has_value()) {
|
|
|
- lookup_result = {.result = EvalImplLookupResult::MakeFinal(*witness_id)};
|
|
|
- } else {
|
|
|
- lookup_result = {.result = EvalImplLookupResult::MakeNonFinal()};
|
|
|
+ eval_query.query_specific_interface_id, true)) {
|
|
|
+ if (witness_inst_id->has_value()) {
|
|
|
+ lookup_result = {.witness_id =
|
|
|
+ context.constant_values().Get(*witness_inst_id)};
|
|
|
+ used_custom_witness = true;
|
|
|
}
|
|
|
- used_custom_witness = true;
|
|
|
}
|
|
|
|
|
|
// Only consider candidates when a custom witness didn't apply.
|
|
|
@@ -1007,20 +1200,19 @@ auto EvalLookupSingleImplWitness(Context& context, SemIR::LocId loc_id,
|
|
|
for (const auto& candidate : candidates.impls) {
|
|
|
const auto& impl = *candidate.impl;
|
|
|
|
|
|
- // 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.
|
|
|
+ // In monomorphization, while resolving 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.
|
|
|
if (!context.impl_lookup_stack().empty()) {
|
|
|
context.impl_lookup_stack().back().impl_loc = impl.definition_id;
|
|
|
}
|
|
|
|
|
|
- auto result = GetWitnessIdForImpl(context, loc_id, query_is_concrete,
|
|
|
- query_self_const_id,
|
|
|
- query_specific_interface, impl);
|
|
|
- if (result.has_value()) {
|
|
|
- PoisonImplLookupQuery(context, loc_id, mode, eval_query, result, impl);
|
|
|
- lookup_result = {.result = result,
|
|
|
+ auto witness_id = TryGetSpecificWitnessIdForImpl(
|
|
|
+ context, loc_id, query_self_const_id, query_specific_interface, impl);
|
|
|
+ if (witness_id.has_value()) {
|
|
|
+ PoisonImplLookupQuery(context, loc_id, mode, eval_query, witness_id,
|
|
|
+ impl);
|
|
|
+ lookup_result = {.witness_id = witness_id,
|
|
|
.impl_type_structure = &candidate.type_structure,
|
|
|
.impl_loc_id = SemIR::LocId(impl.definition_id)};
|
|
|
break;
|
|
|
@@ -1037,17 +1229,17 @@ auto EvalLookupSingleImplWitness(Context& context, SemIR::LocId loc_id,
|
|
|
eval_query.query_specific_interface_id,
|
|
|
lookup_result.impl_type_structure, lookup_result.impl_loc_id);
|
|
|
if (cpp_witness_id.has_value()) {
|
|
|
- lookup_result = {.result =
|
|
|
- EvalImplLookupResult::MakeFinal(cpp_witness_id)};
|
|
|
+ lookup_result = {.witness_id =
|
|
|
+ context.constant_values().Get(cpp_witness_id)};
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (mode != EvalImplLookupMode::RecheckPoisonedLookup &&
|
|
|
- lookup_result.result.has_final_value()) {
|
|
|
+ lookup_result.witness_id.has_value()) {
|
|
|
context.impl_lookup_cache().Insert(impl_lookup_cache_key,
|
|
|
- lookup_result.result.final_witness());
|
|
|
+ lookup_result.witness_id);
|
|
|
}
|
|
|
- return lookup_result.result;
|
|
|
+ return lookup_result.witness_id;
|
|
|
}
|
|
|
|
|
|
auto LookupMatchesImpl(Context& context, SemIR::LocId loc_id,
|
|
|
@@ -1057,10 +1249,14 @@ auto LookupMatchesImpl(Context& context, SemIR::LocId loc_id,
|
|
|
if (query_self_const_id == SemIR::ErrorInst::ConstantId) {
|
|
|
return false;
|
|
|
}
|
|
|
- auto result = GetWitnessIdForImpl(
|
|
|
- context, loc_id, /*query_is_concrete=*/false, query_self_const_id,
|
|
|
- query_specific_interface, context.impls().Get(target_impl));
|
|
|
- return result.has_value();
|
|
|
+ auto witness_id = TryGetSpecificWitnessIdForImpl(
|
|
|
+ context, loc_id, query_self_const_id, query_specific_interface,
|
|
|
+ context.impls().Get(target_impl));
|
|
|
+ // TODO: If this fails, it would be because there is an error in the specific
|
|
|
+ // interface. Should we check for that and return false?
|
|
|
+ CARBON_CHECK(witness_id != SemIR::ErrorInst::ConstantId,
|
|
|
+ "error in lookup specific interface");
|
|
|
+ return witness_id.has_value();
|
|
|
}
|
|
|
|
|
|
} // namespace Carbon::Check
|