Replace .Self in the self type of T(.Self) impls NamedConstraint when identifying a facet type (#7204)

Given this example function:

```carbon
fn F(T:! Z where .Self imps Y(.Self) and E(.Self) impls X(.Self)) {
  T as Y(T);
  E(T) as X(T);
}
```

When we identify the facet type of `T`, we replace `.Self` with `T`.
However in the initial loop, the self _is_ `T` so we don't want to
substitute `.Self` instances inside `T` with itself, as that creates
cycles.

When we see a `type impls...` constraint such as `E(.Self)` we now have
a different self-type so we can, and want to, substitute the `.Self` in
it with `T`. This was already being done, unless the RHS of the `impls`
was a named constraint. In that case we forgot that we were in a `type
impls...` constraint, and avoided replacing `.Self`. Now the algorithm
remembers and correctly replaces `.Self` for `type impls named
constraint` constraints in a facet type.
This commit is contained in:
Dana Jansens
2026-05-13 18:49:02 +00:00
committed by GitHub
parent 2047e08635
commit 5706601096
2 changed files with 60 additions and 10 deletions
@@ -452,3 +452,41 @@ fn F(T:! Z) {
T.Z1 as Y;
C(T.Z1) as Y;
}
// --- period_self_in_type_impls_named_constraint.carbon
library "[[@TEST_NAME]]";
interface Z {
let Z1:! type;
}
interface Y {}
constraint GivesY {
require impls Y;
}
class C(T:! type);
fn F(T:! Z where C(.Self) impls GivesY) {
C(T) as Y;
}
// --- impl_witness_access_impls_named_constraint.carbon
library "[[@TEST_NAME]]";
interface Z {
let Z1:! type;
}
interface Y {}
constraint GivesY {
require impls Y;
}
// This type places a `.Self` (in the .Z1) into the identified facet type as
// `.Self impls Y` through the named constraint. It ensures that the `.Self` is
// correctly replaced by `T` in order to compare equal with `T.Z1` in impl
// lookup.
fn F(T:! Z where .Z1 impls GivesY) {
T.Z1 as Y;
}
+22 -10
View File
@@ -983,14 +983,21 @@ static auto IdentifyFacetType(Context& context, SemIR::LocId loc_id,
}
struct SelfImplsFacetType {
// Whether the impling of facet type should be considered as extending in
// the resulting IdentifiedFacetType.
bool extend;
// Whether we should replace `.Self` in the self type. This is false for the
// top-level self, since we'd be replacing parts of it with itself, which is
// cyclical. It becomes true when recursing into a `T impls ...` constraint
// where the self type is now something else.
bool subst_self;
SemIR::ConstantId self;
SemIR::FacetTypeId facet_type;
};
// Work queue.
llvm::SmallVector<SelfImplsFacetType> work = {
{true, initial_self_const_id, facet_type.facet_type_id}};
{true, false, initial_self_const_id, facet_type.facet_type_id}};
// Outputs for the IdentifiedFacetType.
bool partially_identified = false;
@@ -1003,17 +1010,20 @@ static auto IdentifyFacetType(Context& context, SemIR::LocId loc_id,
while (!work.empty()) {
SelfImplsFacetType next_impls = work.pop_back_val();
bool facet_type_extends = next_impls.extend;
auto subst_period_self_in_self = next_impls.subst_self;
auto self_const_id = GetCanonicalFacetOrTypeValue(context, next_impls.self);
const auto& facet_type_info =
context.facet_types().Get(next_impls.facet_type);
auto self_and_interface = [&](SemIR::SpecificInterface interface)
auto self_and_interface = [&](SemIR::SpecificInterface impls_interface)
-> SemIR::IdentifiedFacetType::RequiredImpl {
// Note that we subst `.Self` in the interface, but we do not in the
// self type here, as that would be cyclical, replacing part of the self
// type with itself.
return {self_const_id, SubstPeriodSelf(context, loc_id, interface,
period_self_replacement_id)};
auto self = subst_period_self_in_self
? SubstPeriodSelf(context, loc_id, self_const_id,
period_self_replacement_id)
: self_const_id;
auto interface = SubstPeriodSelf(context, loc_id, impls_interface,
period_self_replacement_id);
return {self, interface};
};
auto type_and_interface =
[&](SemIR::FacetTypeInfo::TypeImplsInterface impls)
@@ -1108,7 +1118,8 @@ static auto IdentifyFacetType(Context& context, SemIR::LocId loc_id,
.GetInstAs<SemIR::FacetType>(require_facet_type)
.facet_type_id;
bool extend = facet_type_extends && require.extend_self;
work.push_back({extend, require_self, facet_type_id});
work.push_back(
{extend, subst_period_self_in_self, require_self, facet_type_id});
}
}
@@ -1164,7 +1175,8 @@ static auto IdentifyFacetType(Context& context, SemIR::LocId loc_id,
context.constant_values()
.GetInstAs<SemIR::FacetType>(require_facet_type)
.facet_type_id;
work.push_back({false, require_self, facet_type_id});
work.push_back(
{false, subst_period_self_in_self, require_self, facet_type_id});
}
}
@@ -1225,7 +1237,7 @@ static auto IdentifyFacetType(Context& context, SemIR::LocId loc_id,
context.constant_values()
.GetInstAs<SemIR::FacetType>(require_facet_type)
.facet_type_id;
work.push_back({false, require_self, facet_type_id});
work.push_back({false, true, require_self, facet_type_id});
}
}
}