mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
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 https://github.com/swiftlang/swift/issues/79763.
- Fixes rdar://problem/146111083.
This commit is contained in:
@@ -344,7 +344,6 @@ struct OuterSubstitutions {
|
||||
SubstitutionMap subs;
|
||||
unsigned depth;
|
||||
|
||||
bool isUnsubstitutedTypeParameter(Type type) const;
|
||||
Type operator()(SubstitutableType *type) const;
|
||||
ProtocolConformanceRef operator()(CanType dependentType,
|
||||
Type conformingReplacementType,
|
||||
|
||||
@@ -317,9 +317,13 @@ GenericEnvironment::maybeApplyOuterContextSubstitutions(Type type) const {
|
||||
case Kind::OpenedExistential:
|
||||
case Kind::OpenedElement:
|
||||
case Kind::Opaque: {
|
||||
OuterSubstitutions replacer{
|
||||
getOuterSubstitutions(), getGenericSignature()->getMaxDepth()};
|
||||
return type.subst(replacer, replacer);
|
||||
if (auto subs = getOuterSubstitutions()) {
|
||||
OuterSubstitutions replacer{subs,
|
||||
getGenericSignature()->getMaxDepth()};
|
||||
return type.subst(replacer, replacer);
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -663,21 +663,8 @@ SubstitutionMap SubstitutionMap::mapIntoTypeExpansionContext(
|
||||
SubstFlags::PreservePackExpansionLevel);
|
||||
}
|
||||
|
||||
bool OuterSubstitutions::isUnsubstitutedTypeParameter(Type type) const {
|
||||
if (!type->isTypeParameter())
|
||||
return false;
|
||||
|
||||
if (auto depMemTy = type->getAs<DependentMemberType>())
|
||||
return isUnsubstitutedTypeParameter(depMemTy->getBase());
|
||||
|
||||
if (auto genericParam = type->getAs<GenericTypeParamType>())
|
||||
return genericParam->getDepth() >= depth;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Type OuterSubstitutions::operator()(SubstitutableType *type) const {
|
||||
if (isUnsubstitutedTypeParameter(type))
|
||||
if (cast<GenericTypeParamType>(type)->getDepth() >= depth)
|
||||
return Type(type);
|
||||
|
||||
return QuerySubstitutionMap{subs}(type);
|
||||
@@ -687,9 +674,23 @@ ProtocolConformanceRef OuterSubstitutions::operator()(
|
||||
CanType dependentType,
|
||||
Type conformingReplacementType,
|
||||
ProtocolDecl *conformedProtocol) const {
|
||||
if (isUnsubstitutedTypeParameter(dependentType))
|
||||
return ProtocolConformanceRef::forAbstract(
|
||||
auto sig = subs.getGenericSignature();
|
||||
if (!sig->isValidTypeParameter(dependentType) ||
|
||||
!sig->requiresProtocol(dependentType, conformedProtocol)) {
|
||||
// FIXME: We need the isValidTypeParameter() check instead of just looking
|
||||
// at the root generic parameter because in the case of an existential
|
||||
// environment, the reduced type of a member type of Self might be an outer
|
||||
// type parameter that is not formed from the outer generic signature's
|
||||
// conformance requirements. Ideally, we'd either add these supplementary
|
||||
// conformance requirements to the generalization signature, or we would
|
||||
// store the supplementary conformances directly in the generic environment
|
||||
// somehow.
|
||||
//
|
||||
// Once we check for that and handle it properly, the lookupConformance()
|
||||
// can become a forAbstract().
|
||||
return swift::lookupConformance(
|
||||
conformingReplacementType, conformedProtocol);
|
||||
}
|
||||
|
||||
return LookUpConformanceInSubstitutionMap(subs)(
|
||||
dependentType, conformingReplacementType, conformedProtocol);
|
||||
|
||||
@@ -823,10 +823,11 @@ Type swift::typeEraseOpenedExistentialReference(
|
||||
|
||||
auto applyOuterSubstitutions = [&](Type t) -> Type {
|
||||
if (t->hasTypeParameter()) {
|
||||
auto outerSubs = existentialSig.Generalization;
|
||||
unsigned depth = existentialSig.OpenedSig->getMaxDepth();
|
||||
OuterSubstitutions replacer{outerSubs, depth};
|
||||
return t.subst(replacer, replacer);
|
||||
if (auto outerSubs = existentialSig.Generalization) {
|
||||
unsigned depth = existentialSig.OpenedSig->getMaxDepth();
|
||||
OuterSubstitutions replacer{outerSubs, depth};
|
||||
return t.subst(replacer, replacer);
|
||||
}
|
||||
}
|
||||
|
||||
return t;
|
||||
|
||||
25
test/SILGen/existential_erasure_length_2.swift
Normal file
25
test/SILGen/existential_erasure_length_2.swift
Normal file
@@ -0,0 +1,25 @@
|
||||
// RUN: %target-swift-emit-silgen %s
|
||||
|
||||
protocol N {
|
||||
associatedtype A: N
|
||||
}
|
||||
|
||||
protocol P<A> {
|
||||
associatedtype A: N
|
||||
associatedtype B
|
||||
|
||||
func f0(_: A) -> B
|
||||
func f1(_: A.A) -> B
|
||||
func f2(_: A.A.A) -> B
|
||||
}
|
||||
|
||||
struct G<T>: N {
|
||||
typealias A = G<G<T>>
|
||||
}
|
||||
|
||||
func call(x: any P<G<Int>>) -> (Any, Any, Any) {
|
||||
let y0 = x.f0(G<Int>())
|
||||
let y1 = x.f1(G<G<Int>>())
|
||||
let y2 = x.f2(G<G<G<Int>>>())
|
||||
return (y0, y1, y2)
|
||||
}
|
||||
Reference in New Issue
Block a user