Skip to content

Commit 9bb6673

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 e3d8e35 commit 9bb6673

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
@@ -340,7 +340,6 @@ struct OuterSubstitutions {
340340
SubstitutionMap subs;
341341
unsigned depth;
342342

343-
bool isUnsubstitutedTypeParameter(Type type) const;
344343
Type operator()(SubstitutableType *type) const;
345344
ProtocolConformanceRef operator()(CanType dependentType,
346345
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
@@ -669,21 +669,8 @@ SubstitutionMap SubstitutionMap::mapIntoTypeExpansionContext(
669669
SubstFlags::PreservePackExpansionLevel);
670670
}
671671

672-
bool OuterSubstitutions::isUnsubstitutedTypeParameter(Type type) const {
673-
if (!type->isTypeParameter())
674-
return false;
675-
676-
if (auto depMemTy = type->getAs<DependentMemberType>())
677-
return isUnsubstitutedTypeParameter(depMemTy->getBase());
678-
679-
if (auto genericParam = type->getAs<GenericTypeParamType>())
680-
return genericParam->getDepth() >= depth;
681-
682-
return false;
683-
}
684-
685672
Type OuterSubstitutions::operator()(SubstitutableType *type) const {
686-
if (isUnsubstitutedTypeParameter(type))
673+
if (cast<GenericTypeParamType>(type)->getDepth() >= depth)
687674
return Type(type);
688675

689676
return QuerySubstitutionMap{subs}(type);
@@ -693,9 +680,23 @@ ProtocolConformanceRef OuterSubstitutions::operator()(
693680
CanType dependentType,
694681
Type conformingReplacementType,
695682
ProtocolDecl *conformedProtocol) const {
696-
if (isUnsubstitutedTypeParameter(dependentType))
697-
return ProtocolConformanceRef::forAbstract(
683+
auto sig = subs.getGenericSignature();
684+
if (!sig->isValidTypeParameter(dependentType) ||
685+
!sig->requiresProtocol(dependentType, conformedProtocol)) {
686+
// FIXME: We need the isValidTypeParameter() check instead of just looking
687+
// at the root generic parameter because in the case of an existential
688+
// environment, the reduced type of a member type of Self might be an outer
689+
// type parameter that is not formed from the outer generic signature's
690+
// conformance requirements. Ideally, we'd either add these supplementary
691+
// conformance requirements to the generalization signature, or we would
692+
// store the supplementary conformances directly in the generic environment
693+
// somehow.
694+
//
695+
// Once we check for that and handle it properly, the lookupConformance()
696+
// can become a forAbstract().
697+
return swift::lookupConformance(
698698
conformingReplacementType, conformedProtocol);
699+
}
699700

700701
return LookUpConformanceInSubstitutionMap(subs)(
701702
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)