Skip to content

Commit e12d1b6

Browse files
authored
Handle a specific providing ImplWitnessAccess for a symbolic binding used as a type (#6201)
SymbolicBindingType evaluates to the type component of a symbolic facet value (a type/witnesses pair), and that symbolic facet value has its constant value replaced by a specific. That specific can provide a FacetValue, in which case it just evaluates to that FacetValue's type component. It can provide a BindSymbolicName of another binding, in which case it points to that entity instead and awaits a further specific. Currently the code only handles these two cases, and they match the behaviour of the evaluation of FacetAccessType itself. However FacetAccessType evaluation also handles cases beyond these, as there are other instructions that occur as facet values, such as ImplWitnessAccess, when accessing an associated constant of an interface that has a facet type as its type. Currently eval then crashes in this scenario. Instead of furthering to reproduce the contents of FacetAccessType's evaluation, defer to calling the `EvalConstantInst()` overload for it when evaluating SymbolicBindingType against a new value from a specific. This means SymbolicBindingType can evaluate back into a FacetAccessType, when it was originally a FacetAccessType(BindSymbolicName) and becomes FacetAccessType(ImplWitnessAccess) through a specific. This comes with a test that crashed in eval before this change.
1 parent b2c2bdd commit e12d1b6

File tree

2 files changed

+54
-47
lines changed

2 files changed

+54
-47
lines changed

toolchain/check/eval.cpp

Lines changed: 27 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2178,75 +2178,55 @@ auto TryEvalTypedInst<SemIR::SymbolicBindingType>(EvalContext& eval_context,
21782178
SemIR::InstId inst_id,
21792179
SemIR::Inst inst)
21802180
-> SemIR::ConstantId {
2181-
auto bind = inst.As<SemIR::SymbolicBindingType>();
2182-
2183-
Phase phase = Phase::Concrete;
2184-
bool updated_constants = false;
2185-
2186-
// If we know which specific we're evaluating within and this is the type
2187-
// component of a facet parameter of the generic, its constant value refers to
2188-
// the type component of the corresponding argument value of the specific.
2189-
const auto& bind_name = eval_context.entity_names().Get(bind.entity_name_id);
2181+
// If a specific provides a new value for the binding with `entity_name_id`,
2182+
// the SymbolicBindingType is evaluated for that new value.
2183+
const auto& bind_name = eval_context.entity_names().Get(
2184+
inst.As<SemIR::SymbolicBindingType>().entity_name_id);
21902185
if (bind_name.bind_index().has_value()) {
2191-
// SymbolicBindingType comes from the evaluation of FacetAccessType when the
2192-
// facet value is symbolic. This block is effectively the deferred
2193-
// evaluation of that FacetAccessType now that a new value for the symbolic
2194-
// facet value has become known. The result is equivalent to creating a new
2195-
// FacetAccessType here with the `value_inst_id` and evaluating it.
21962186
if (auto value =
21972187
eval_context.GetCompileTimeBindValue(bind_name.bind_index());
21982188
value.has_value()) {
21992189
auto value_inst_id = eval_context.constant_values().GetInstId(value);
2200-
if (auto facet =
2201-
eval_context.insts().TryGetAs<SemIR::FacetValue>(value_inst_id)) {
2202-
return eval_context.constant_values().Get(facet->type_inst_id);
2203-
}
2204-
2205-
// Replace the fields with constant values as usual, except we get the
2206-
// EntityNameId from the BindSymbolicName in the specific, which
2207-
// ReplaceFieldWithConstantValue doesn't know how to do.
2208-
if (!ReplaceTypeWithConstantValue(eval_context, inst_id, &bind, &phase) ||
2209-
!ReplaceFieldWithConstantValue(
2210-
eval_context, &bind,
2211-
&SemIR::SymbolicBindingType::facet_value_inst_id, &phase)) {
2212-
return SemIR::ConstantId::NotConstant;
2213-
}
2214-
2215-
if (value_inst_id == SemIR::ErrorInst::InstId) {
2216-
phase = Phase::UnknownDueToError;
2217-
} else {
2218-
auto value_bind =
2219-
eval_context.insts().GetAs<SemIR::BindSymbolicName>(value_inst_id);
2220-
bind.entity_name_id =
2221-
GetConstantValue(eval_context, value_bind.entity_name_id, &phase);
2222-
}
22232190

2224-
updated_constants = true;
2191+
// A SymbolicBindingType can evaluate to a FacetAccessType if the new
2192+
// value of the entity is a facet value that that does not have a concrete
2193+
// type (a FacetType) and does not have a new EntityName to point to (a
2194+
// BindSymbolicName).
2195+
auto access = SemIR::FacetAccessType{
2196+
.type_id = SemIR::TypeType::TypeId,
2197+
.facet_value_inst_id = value_inst_id,
2198+
};
2199+
return ConvertEvalResultToConstantId(
2200+
eval_context.context(),
2201+
EvalConstantInst(eval_context.context(), access),
2202+
ComputeInstPhase(eval_context.context(), access));
22252203
}
22262204
}
22272205

2228-
if (!updated_constants) {
2229-
if (!ReplaceTypeWithConstantValue(eval_context, inst_id, &inst, &phase) ||
2230-
!ReplaceAllFieldsWithConstantValues(eval_context, &inst, &phase)) {
2231-
return SemIR::ConstantId::NotConstant;
2232-
}
2233-
// Copy the updated constant field values into `bind`.
2234-
bind = inst.As<SemIR::SymbolicBindingType>();
2206+
Phase phase = Phase::Concrete;
2207+
if (!ReplaceTypeWithConstantValue(eval_context, inst_id, &inst, &phase) ||
2208+
!ReplaceAllFieldsWithConstantValues(eval_context, &inst, &phase)) {
2209+
return SemIR::ConstantId::NotConstant;
22352210
}
22362211
// Propagate error phase after getting the constant value for all fields.
22372212
if (phase == Phase::UnknownDueToError) {
22382213
return SemIR::ErrorInst::ConstantId;
22392214
}
22402215

2216+
// Evaluation of SymbolicBindingType.
2217+
//
2218+
// Like FacetAccessType, a SymbolicBindingType of a FacetValue just evaluates
2219+
// to the type inside.
2220+
//
22412221
// TODO: Look in ScopeStack with the entity_name_id to find the facet value
22422222
// and get its constant value in the current specific context. The
22432223
// facet_value_inst_id will go away.
22442224
if (auto facet_value = eval_context.insts().TryGetAs<SemIR::FacetValue>(
2245-
bind.facet_value_inst_id)) {
2225+
inst.As<SemIR::SymbolicBindingType>().facet_value_inst_id)) {
22462226
return eval_context.constant_values().Get(facet_value->type_inst_id);
22472227
}
22482228

2249-
return MakeConstantResult(eval_context.context(), bind, phase);
2229+
return MakeConstantResult(eval_context.context(), inst, phase);
22502230
}
22512231

22522232
// Returns whether `const_id` is the same constant facet value as

toolchain/check/testdata/facet/access.carbon

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,33 @@ fn G(AB:! A & B where .X = () and .Y = {}) -> AB.Y {
383383
}
384384
//@dump-sem-ir-end
385385

386+
// --- symbolic_binding_type_of_impl_witness_access.carbon
387+
388+
interface Y {}
389+
impl () as Y {}
390+
391+
interface Z {
392+
let Y1:! Y;
393+
fn G() -> Y1*;
394+
}
395+
396+
// The type of `T` matches exactly the type of `Z.Y1`, which prevents the
397+
// specific in the call from F2 from deducing a `FacetValue`. Instead it just
398+
// passes in the `ImplWitnessAccess` instruction as is.
399+
//
400+
// The `t: T` creates a `T as type`, or a `SymbolicBindingType` that gets
401+
// evaluated against the specific. The facet value that it evaluates against is
402+
// the `ImplWitnessAccess` from the call in F2.
403+
//
404+
// This requires that `SymbolicBindingType` evaluation correctly handles
405+
// arbitrary facet value instructions. Not just the common case of `FacetValue`
406+
// or `BindSymbolicName`.
407+
fn F1(T:! Y, t: T*) {}
408+
409+
fn F2(U:! Z) {
410+
F1(U.Y1, U.G());
411+
}
412+
386413
// CHECK:STDOUT: --- access_assoc_fn.carbon
387414
// CHECK:STDOUT:
388415
// CHECK:STDOUT: constants {

0 commit comments

Comments
 (0)