Skip to content

Commit 1a29367

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 86c30d6 commit 1a29367

File tree

5 files changed

+56
-24
lines changed

5 files changed

+56
-24
lines changed

include/swift/AST/SubstitutionMap.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,6 @@ struct OuterSubstitutions {
344344
SubstitutionMap subs;
345345
unsigned depth;
346346

347-
bool isUnsubstitutedTypeParameter(Type type) const;
348347
Type operator()(SubstitutableType *type) const;
349348
ProtocolConformanceRef operator()(CanType dependentType,
350349
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: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -663,21 +663,8 @@ SubstitutionMap SubstitutionMap::mapIntoTypeExpansionContext(
663663
SubstFlags::PreservePackExpansionLevel);
664664
}
665665

666-
bool OuterSubstitutions::isUnsubstitutedTypeParameter(Type type) const {
667-
if (!type->isTypeParameter())
668-
return false;
669-
670-
if (auto depMemTy = type->getAs<DependentMemberType>())
671-
return isUnsubstitutedTypeParameter(depMemTy->getBase());
672-
673-
if (auto genericParam = type->getAs<GenericTypeParamType>())
674-
return genericParam->getDepth() >= depth;
675-
676-
return false;
677-
}
678-
679666
Type OuterSubstitutions::operator()(SubstitutableType *type) const {
680-
if (isUnsubstitutedTypeParameter(type))
667+
if (cast<GenericTypeParamType>(type)->getDepth() >= depth)
681668
return Type(type);
682669

683670
return QuerySubstitutionMap{subs}(type);
@@ -687,9 +674,21 @@ ProtocolConformanceRef OuterSubstitutions::operator()(
687674
CanType dependentType,
688675
Type conformingReplacementType,
689676
ProtocolDecl *conformedProtocol) const {
690-
if (isUnsubstitutedTypeParameter(dependentType))
691-
return ProtocolConformanceRef::forAbstract(
677+
if (!subs.getGenericSignature()->isValidTypeParameter(dependentType)) {
678+
// FIXME: We need the isValidTypeParameter() check instead of just looking
679+
// at the root generic parameter because in the case of an existential
680+
// environment, the reduced type of a member type of Self might be an outer
681+
// type parameter that is not formed from the outer generic signature's
682+
// conformance requirements. Ideally, we'd either add these supplementary
683+
// conformance requirements to the generalization signature, or we would
684+
// store the supplementary conformances directly in the generic environment
685+
// somehow.
686+
//
687+
// Once we check for that and handle it properly, the lookupConformance()
688+
// can become a forAbstract().
689+
return swift::lookupConformance(
692690
conformingReplacementType, conformedProtocol);
691+
}
693692

694693
return LookUpConformanceInSubstitutionMap(subs)(
695694
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: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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 f(_: A.A.A) -> B
12+
}
13+
14+
struct G<T>: N {
15+
typealias A = G<G<T>>
16+
}
17+
18+
struct S: P {
19+
typealias A = G<Int>
20+
func f(_: A.A.A) -> Int {
21+
return 0
22+
}
23+
}
24+
25+
func call() -> Any {
26+
let x: any P<G<Int>> = S()
27+
let y = x.f(G<G<G<Int>>>())
28+
return y
29+
}

0 commit comments

Comments
 (0)