Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support specialization in impl lookup with a symbolic query/impl. #5169

Merged
merged 16 commits into from
Mar 26, 2025
1 change: 1 addition & 0 deletions toolchain/check/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ cc_library(
"//common:vlog",
"//toolchain/base:index_base",
"//toolchain/base:kind_switch",
"//toolchain/base:value_store",
"//toolchain/check:generic_region_stack",
"//toolchain/check:scope_stack",
"//toolchain/diagnostics:diagnostic_emitter",
Expand Down
5 changes: 5 additions & 0 deletions toolchain/check/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "common/map.h"
#include "common/ostream.h"
#include "llvm/ADT/SmallVector.h"
#include "toolchain/base/value_store.h"
#include "toolchain/check/decl_introducer_state.h"
#include "toolchain/check/decl_name_stack.h"
#include "toolchain/check/full_pattern_stack.h"
Expand Down Expand Up @@ -232,6 +233,10 @@ class Context {
return sem_ir().complete_facet_types();
}
auto impls() -> SemIR::ImplStore& { return sem_ir().impls(); }
auto specific_interfaces()
-> CanonicalValueStore<SemIR::SpecificInterfaceId>& {
return sem_ir().specific_interfaces();
}
auto generics() -> SemIR::GenericStore& { return sem_ir().generics(); }
auto specifics() -> SemIR::SpecificStore& { return sem_ir().specifics(); }
auto import_irs() -> ValueStore<SemIR::ImportIRId>& {
Expand Down
2 changes: 2 additions & 0 deletions toolchain/check/diagnostic_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class SemIRLoc {
private:
// Only allow member access for diagnostics.
friend class SemIRLocDiagnosticEmitter;
// And also for eval to unwrap a LocId for calling into the rest of Check.
friend class UnwrapSemIRLoc;

union {
SemIR::InstId inst_id_;
Expand Down
6 changes: 6 additions & 0 deletions toolchain/check/dump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,12 @@ LLVM_DUMP_METHOD static auto Dump(const Context& context,
SemIR::Dump(context.sem_ir(), specific_id);
}

LLVM_DUMP_METHOD static auto Dump(
const Context& context, SemIR::SpecificInterfaceId specific_interface_id)
-> void {
SemIR::Dump(context.sem_ir(), specific_interface_id);
}

LLVM_DUMP_METHOD static auto Dump(
const Context& context, SemIR::StructTypeFieldsId struct_type_fields_id)
-> void {
Expand Down
24 changes: 24 additions & 0 deletions toolchain/check/eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ class EvalContext {
auto interfaces() -> const ValueStore<SemIR::InterfaceId>& {
return sem_ir().interfaces();
}
auto specific_interfaces()
-> CanonicalValueStore<SemIR::SpecificInterfaceId>& {
return sem_ir().specific_interfaces();
}
auto facet_types() -> CanonicalValueStore<SemIR::FacetTypeId>& {
return sem_ir().facet_types();
}
Expand Down Expand Up @@ -553,6 +557,20 @@ static auto GetConstantValue(EvalContext& eval_context,
specific.generic_id, args_id);
}

static auto GetConstantValue(EvalContext& eval_context,
SemIR::SpecificInterfaceId specific_interface_id,
Phase* phase) -> SemIR::SpecificInterfaceId {
const auto& interface =
eval_context.specific_interfaces().Get(specific_interface_id);
if (!interface.specific_id.has_value()) {
return specific_interface_id;
}
return eval_context.specific_interfaces().Add(
{.interface_id = interface.interface_id,
.specific_id =
GetConstantValue(eval_context, interface.specific_id, phase)});
}

// Like `GetConstantValue` but does a `FacetTypeId` -> `FacetTypeInfo`
// conversion. Does not perform canonicalization.
static auto GetConstantFacetTypeInfo(EvalContext& eval_context,
Expand Down Expand Up @@ -1841,6 +1859,12 @@ static auto TryEvalInstInContext(EvalContext& eval_context,
inst_id, inst);
}

auto TryEvalInst(Context& context, SemIR::LocId loc_id, SemIR::InstId inst_id,
SemIR::Inst inst) -> SemIR::ConstantId {
EvalContext eval_context(context, loc_id);
return TryEvalInstInContext(eval_context, inst_id, inst);
}

auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst)
-> SemIR::ConstantId {
EvalContext eval_context(context, inst_id);
Expand Down
4 changes: 4 additions & 0 deletions toolchain/check/eval.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ namespace Carbon::Check {
// `SemIR::ConstantId::NotConstant`.
auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst)
-> SemIR::ConstantId;
// Like the above but specific a LocId instead of deriving it from the
// `inst_id`. This is most useful when passing `None` as the `inst_id`.
auto TryEvalInst(Context& context, SemIR::LocId loc_id, SemIR::InstId inst_id,
SemIR::Inst inst) -> SemIR::ConstantId;

// Evaluates the eval block for a region of a specific. Produces a block
// containing the evaluated constant values of the instructions in the eval
Expand Down
43 changes: 43 additions & 0 deletions toolchain/check/eval_inst.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,41 @@

#include "toolchain/check/eval_inst.h"

#include <variant>

#include "toolchain/check/action.h"
#include "toolchain/check/facet_type.h"
#include "toolchain/check/generic.h"
#include "toolchain/check/impl_lookup.h"
#include "toolchain/check/import_ref.h"
#include "toolchain/check/type.h"
#include "toolchain/check/type_completion.h"
#include "toolchain/sem_ir/ids.h"
#include "toolchain/sem_ir/typed_insts.h"

namespace Carbon::Check {

// When calling from eval to various Check functions, we need the actual LocId.
// This allows us to unwrap the SemIRLoc to do so.
//
// TODO: Decide whether to refactor calls everywhere to accept `SemIRLoc`, or
// fold `SemIRLoc` into `LocId`. Either way, we would like eval to call other
// code without unwrapping `SemIRLoc`.
class UnwrapSemIRLoc {
public:
auto operator()(Context& context, SemIRLoc loc) -> SemIR::LocId {
if (loc.is_inst_id_) {
if (loc.inst_id_.has_value()) {
return context.insts().GetLocId(loc.inst_id_);
} else {
return SemIR::LocId::None;
}
} else {
return loc.loc_id_;
}
}
};

// Performs an access into an aggregate, retrieving the specified element.
static auto PerformAggregateAccess(Context& context, SemIR::Inst inst)
-> ConstantEvalResult {
Expand Down Expand Up @@ -197,6 +222,24 @@ auto EvalConstantInst(Context& /*context*/, SemIRLoc /*loc*/,
.type_id = inst.type_id, .elements_id = SemIR::InstBlockId::Empty});
}

auto EvalConstantInst(Context& context, SemIRLoc loc,
SemIR::ImplSymbolicWitness inst) -> ConstantEvalResult {
auto result = EvalLookupSingleImplWitness(
context, UnwrapSemIRLoc()(context, loc), inst);
if (!result.has_value()) {
// We use NotConstant to communicate back to impl lookup that the lookup
// failed. This can not happen for a deferred symbolic lookup in a generic
// eval block, since we only add the deferred lookup instruction (being
// evaluated here) to the SemIR if the lookup succeeds.
return ConstantEvalResult::NotConstant;
}
if (!result.has_concrete_value()) {
return ConstantEvalResult::NewSamePhase(inst);
}
return ConstantEvalResult::Existing(
context.constant_values().Get(result.concrete_witness()));
}

auto EvalConstantInst(Context& context, SemIRLoc loc,
SemIR::ImplWitnessAccess inst) -> ConstantEvalResult {
// This is PerformAggregateAccess followed by GetConstantInSpecific.
Expand Down
Loading