Use FacetAccessType when converting to a value of type FacetType (#4925)

When attempting to convert to a value of type FacetType, and the source
value is a FacetAccessType, then if the type of the underlying
FacetValue is the same as the target, we can use the FacetValue there as
the conversion output.

If the type of the FacetValue differs, then we still want to do impl
lookup with the FacetValue to see if it matches with the target
FacetType, but that is still a TODO.

This allows a generic function with a value whose type is constrained by
a FacetType (thus the value's type is a FacetAccessType), to call other
functions with the value as an argument when it is constrained by the
same FacetType:
```
fn F[T:! FacetType](x: T);
fn G[T:! FacetType](x: T) { F(x); }
```

It is also an optimization to avoid impl lookup where we've already done
it to produce the FacetAccessType.

Adds a bunch of new tests with values of types which are constrained by
a facet type (or "facet value value" for short), with some more tests
that currently fail and should be made to pass.
This commit is contained in:
Dana Jansens
2025-02-12 14:35:11 +00:00
committed by GitHub
parent 2a17465e06
commit 30b2b5ef81
8 changed files with 2340 additions and 91 deletions
+26 -4
View File
@@ -991,7 +991,24 @@ static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id,
}
if (sem_ir.types().Is<SemIR::FacetType>(target.type_id)) {
if (sem_ir.types().Is<SemIR::FacetType>(value_type_id)) {
auto lookup_inst_id = value_id;
// `FacetAccessType` wraps an instruction that evaluates to a facet value
// (of type `FacetType`). If the `FacetType` matches the target
// `FacetType` then we don't need to do impl lookup. Otherwise, we want to
// try convert the result of the instruction in the `FacetAccessType`.
if (auto facet_access_type_value =
context.insts().TryGetAs<SemIR::FacetAccessType>(lookup_inst_id)) {
auto facet_value_inst_id = facet_access_type_value->facet_value_inst_id;
if (context.insts().Get(facet_value_inst_id).type_id() ==
target.type_id) {
return facet_value_inst_id;
}
lookup_inst_id = facet_access_type_value->facet_value_inst_id;
}
if (sem_ir.types().Is<SemIR::FacetType>(
sem_ir.insts().Get(lookup_inst_id).type_id())) {
// Conversion from a facet value (which has type `FacetType`) to a
// different facet value (which has type `FacetType`), if the value's
// `FacetType` satisfies the requirements of the target `FacetType`. The
@@ -1004,23 +1021,28 @@ static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id,
// so using that here would be like an implicit cast back to the concrete
// type.
context.TODO(loc_id, "Facet value converting to facet value");
} else if (sem_ir.types().Is<SemIR::TypeType>(value_type_id)) {
return value_id;
}
if (sem_ir.types().Is<SemIR::TypeType>(
sem_ir.insts().Get(lookup_inst_id).type_id())) {
// Conversion from a type value (which has type `type`) to a facet value
// (which has type `FacetType`), if the type satisfies the requirements of
// the target `FacetType`, as determined by finding an impl witness. This
// binds the value to the `FacetType` with a `FacetValue`.
auto witness_inst_id = LookupImplWitness(
context, loc_id,
// The value instruction evaluates to a type value (which has type
// `type`). This gets that type value if it's available at compile
// time, as a constant value.
context.constant_values().Get(value_id),
context.constant_values().Get(lookup_inst_id),
context.types().GetConstantId(target.type_id));
if (witness_inst_id != SemIR::InstId::None) {
return context.AddInst<SemIR::FacetValue>(
loc_id, {
.type_id = target.type_id,
.type_inst_id = value_id,
.type_inst_id = lookup_inst_id,
.witness_inst_id = witness_inst_id,
});
}