Skip to content

Commit ca223d6

Browse files
committed
AST: Fix existential erasure of long member types
Suppose protocol P has a primary associated type A, and we have a `any P<S>` value. We form the generalization signature <T> with substitution map {T := S}, and the existential signature <T, Self where T == Self.A>. Now, if we call a protocol requirement that takes Self.A.A.A, we see this is fixed concrete type, because the reduced type of Self.A.A.A is T.A.A in the existential signature. However, this type parameter is not formed from the conformance requirements of the generalization signature (there aren't any), so we cannot directly apply the outer substitution map. Instead, change the outer substitution conformance lookup callback to check if the reduced type parameter is valid in the generalization signature, and not just rooted in a generic parameter of the generalization signature. If it isn't, fall back to global conformance lookup. A better fix would introduce new requirements into the generalization signature to handle this, or store them separately in the generic environment itself. But this is fine for now. - Fixes #79763. - Fixes rdar://problem/146111083.
1 parent 6b4710e commit ca223d6

File tree

5 files changed

+54
-24
lines changed

5 files changed

+54
-24
lines changed

include/swift/AST/SubstitutionMap.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,6 @@ struct OuterSubstitutions {
337337
SubstitutionMap subs;
338338
unsigned depth;
339339

340-
bool isUnsubstitutedTypeParameter(Type type) const;
341340
Type operator()(SubstitutableType *type) const;
342341
ProtocolConformanceRef operator()(CanType dependentType,
343342
Type conformingReplacementType,

lib/AST/GenericEnvironment.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -317,9 +317,13 @@ GenericEnvironment::maybeApplyOuterContextSubstitutions(Type type) const {
317317
case Kind::OpenedExistential:
318318
case Kind::OpenedElement:
319319
case Kind::Opaque: {
320-
OuterSubstitutions replacer{
321-
getOuterSubstitutions(), getGenericSignature()->getMaxDepth()};
322-
return type.subst(replacer, replacer);
320+
if (auto subs = getOuterSubstitutions()) {
321+
OuterSubstitutions replacer{subs,
322+
getGenericSignature()->getMaxDepth()};
323+
return type.subst(replacer, replacer);
324+
}
325+
326+
return type;
323327
}
324328
}
325329
}

lib/AST/SubstitutionMap.cpp

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -657,21 +657,8 @@ SubstitutionMap swift::substOpaqueTypesWithUnderlyingTypes(
657657
SubstFlags::PreservePackExpansionLevel);
658658
}
659659

660-
bool OuterSubstitutions::isUnsubstitutedTypeParameter(Type type) const {
661-
if (!type->isTypeParameter())
662-
return false;
663-
664-
if (auto depMemTy = type->getAs<DependentMemberType>())
665-
return isUnsubstitutedTypeParameter(depMemTy->getBase());
666-
667-
if (auto genericParam = type->getAs<GenericTypeParamType>())
668-
return genericParam->getDepth() >= depth;
669-
670-
return false;
671-
}
672-
673660
Type OuterSubstitutions::operator()(SubstitutableType *type) const {
674-
if (isUnsubstitutedTypeParameter(type))
661+
if (cast<GenericTypeParamType>(type)->getDepth() >= depth)
675662
return Type(type);
676663

677664
return QuerySubstitutionMap{subs}(type);
@@ -681,9 +668,23 @@ ProtocolConformanceRef OuterSubstitutions::operator()(
681668
CanType dependentType,
682669
Type conformingReplacementType,
683670
ProtocolDecl *conformedProtocol) const {
684-
if (isUnsubstitutedTypeParameter(dependentType))
685-
return ProtocolConformanceRef::forAbstract(
671+
auto sig = subs.getGenericSignature();
672+
if (!sig->isValidTypeParameter(dependentType) ||
673+
!sig->requiresProtocol(dependentType, conformedProtocol)) {
674+
// FIXME: We need the isValidTypeParameter() check instead of just looking
675+
// at the root generic parameter because in the case of an existential
676+
// environment, the reduced type of a member type of Self might be an outer
677+
// type parameter that is not formed from the outer generic signature's
678+
// conformance requirements. Ideally, we'd either add these supplementary
679+
// conformance requirements to the generalization signature, or we would
680+
// store the supplementary conformances directly in the generic environment
681+
// somehow.
682+
//
683+
// Once we check for that and handle it properly, the lookupConformance()
684+
// can become a forAbstract().
685+
return swift::lookupConformance(
686686
conformingReplacementType, conformedProtocol);
687+
}
687688

688689
return LookUpConformanceInSubstitutionMap(subs)(
689690
dependentType, conformingReplacementType, conformedProtocol);

lib/Sema/OpenedExistentials.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -823,10 +823,11 @@ Type swift::typeEraseOpenedExistentialReference(
823823

824824
auto applyOuterSubstitutions = [&](Type t) -> Type {
825825
if (t->hasTypeParameter()) {
826-
auto outerSubs = existentialSig.Generalization;
827-
unsigned depth = existentialSig.OpenedSig->getMaxDepth();
828-
OuterSubstitutions replacer{outerSubs, depth};
829-
return t.subst(replacer, replacer);
826+
if (auto outerSubs = existentialSig.Generalization) {
827+
unsigned depth = existentialSig.OpenedSig->getMaxDepth();
828+
OuterSubstitutions replacer{outerSubs, depth};
829+
return t.subst(replacer, replacer);
830+
}
830831
}
831832

832833
return t;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %target-swift-emit-silgen %s
2+
3+
protocol N {
4+
associatedtype A: N
5+
}
6+
7+
protocol P<A> {
8+
associatedtype A: N
9+
associatedtype B
10+
11+
func f0(_: A) -> B
12+
func f1(_: A.A) -> B
13+
func f2(_: A.A.A) -> B
14+
}
15+
16+
struct G<T>: N {
17+
typealias A = G<G<T>>
18+
}
19+
20+
func call(x: any P<G<Int>>) -> (Any, Any, Any) {
21+
let y0 = x.f0(G<Int>())
22+
let y1 = x.f1(G<G<Int>>())
23+
let y2 = x.f2(G<G<G<Int>>>())
24+
return (y0, y1, y2)
25+
}

0 commit comments

Comments
 (0)