Skip to content

Commit 239e77c

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 239e77c

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

700699
return LookUpConformanceInSubstitutionMap(subs)(
701700
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)