Skip to content

Preserve specialized conformance better #17993

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1784,24 +1784,26 @@ ASTContext::getConformance(Type conformingType,
/// that instead.
static ProtocolConformance *collapseSpecializedConformance(
Type type,
ProtocolConformance *conformance) {
ProtocolConformance *conformance,
SubstitutionMap substitutions) {
while (true) {
// If the conformance matches, return it.
if (conformance->getType()->isEqual(type))
return conformance;

switch (conformance->getKind()) {
case ProtocolConformanceKind::Inherited:
conformance = cast<InheritedProtocolConformance>(conformance)
->getInheritedConformance();
break;

case ProtocolConformanceKind::Specialized:
conformance = cast<SpecializedProtocolConformance>(conformance)
->getGenericConformance();
break;

case ProtocolConformanceKind::Normal:
case ProtocolConformanceKind::Inherited:
// If the conformance matches, return it.
if (conformance->getType()->isEqual(type)) {
for (auto subConformance : substitutions.getConformances())
if (!subConformance.isAbstract())
return nullptr;

return conformance;
}

return nullptr;
}
}
Expand All @@ -1814,7 +1816,8 @@ ASTContext::getSpecializedConformance(Type type,
// If we are performing a substitution that would get us back to the
// a prior conformance (e.g., mapping into and then out of a conformance),
// return the existing conformance.
if (auto existing = collapseSpecializedConformance(type, generic)) {
if (auto existing = collapseSpecializedConformance(type, generic,
substitutions)) {
++NumCollapsedSpecializedProtocolConformances;
return existing;
}
Expand Down
3 changes: 2 additions & 1 deletion lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2942,7 +2942,8 @@ Optional<ProtocolConformanceRef>
MakeAbstractConformanceForGenericType::operator()(CanType dependentType,
Type conformingReplacementType,
ProtocolDecl *conformedProtocol) const {
assert((conformingReplacementType->is<SubstitutableType>()
assert((conformingReplacementType->is<ErrorType>()
|| conformingReplacementType->is<SubstitutableType>()
|| conformingReplacementType->is<DependentMemberType>())
&& "replacement requires looking up a concrete conformance");
return ProtocolConformanceRef(conformedProtocol);
Expand Down
47 changes: 13 additions & 34 deletions lib/Sema/TypeCheckProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3318,57 +3318,36 @@ void ConformanceChecker::ensureRequirementsAreSatisfied(
= Conformance->populateSignatureConformances();

class GatherConformancesListener : public GenericRequirementsCheckListener {
TypeChecker &tc;
NormalProtocolConformance *conformance;
std::function<void(ProtocolConformanceRef)> &writer;
public:
GatherConformancesListener(
TypeChecker &tc,
NormalProtocolConformance *conformance,
std::function<void(ProtocolConformanceRef)> &writer)
: tc(tc), conformance(conformance), writer(writer) { }
NormalProtocolConformance *conformance,
std::function<void(ProtocolConformanceRef)> &writer)
: conformance(conformance), writer(writer) { }

void satisfiedConformance(Type depTy, Type replacementTy,
ProtocolConformanceRef conformance) override {
// The conformance will use contextual types, but we want the
// interface type equivalent.

// If we have an inherited conformance for an archetype, dig out the
// superclass conformance to translate.
Type inheritedInterfaceType;
if (conformance.isConcrete() &&
conformance.getConcrete()->getType()->hasArchetype()) {
auto concreteConformance = conformance.getConcrete();
if (concreteConformance->getKind()
== ProtocolConformanceKind::Inherited &&
conformance.getConcrete()->getType()->is<ArchetypeType>()) {
inheritedInterfaceType =
concreteConformance->getType()->mapTypeOutOfContext();
concreteConformance =
cast<InheritedProtocolConformance>(concreteConformance)
->getInheritedConformance();
}

// Map the conformance.
// FIXME: It would be so much easier and efficient if we had
// ProtocolConformance::mapTypesOutOfContext().
auto interfaceType =
concreteConformance->getType()->mapTypeOutOfContext();

conformance = *tc.conformsToProtocol(
interfaceType,
conformance.getRequirement(),
this->conformance->getDeclContext(),
(ConformanceCheckFlags::SuppressDependencyTracking|
ConformanceCheckFlags::SkipConditionalRequirements));
concreteConformance = concreteConformance->subst(
interfaceType,
[](SubstitutableType *type) -> Type {
if (auto *archetypeType = type->getAs<ArchetypeType>())
return archetypeType->getInterfaceType();
return type;
},
MakeAbstractConformanceForGenericType());

// Reinstate inherited conformance.
if (inheritedInterfaceType) {
conformance =
ProtocolConformanceRef(
tc.Context.getInheritedConformance(inheritedInterfaceType,
conformance.getConcrete()));
}
conformance = ProtocolConformanceRef(concreteConformance);
}

writer(conformance);
Expand All @@ -3384,7 +3363,7 @@ void ConformanceChecker::ensureRequirementsAreSatisfied(

return false;
}
} listener(TC, Conformance, writer);
} listener(Conformance, writer);

auto result = TC.checkGenericArguments(
DC, Loc, Loc,
Expand Down
44 changes: 44 additions & 0 deletions validation-test/compiler_crashers_2_fixed/0168-rdar40164371.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// RUN: %target-swift-frontend -emit-sil %s

protocol X1 {
associatedtype X3 : X4
}

protocol X4 {
associatedtype X15
}

protocol X7 { }

protocol X9 : X7 {
associatedtype X10 : X7
}

struct X12 : X9 {
typealias X10 = X12
}

struct X13<I1 : X7> : X9 {
typealias X10 = X13<I1>
}

struct X14<G : X4> : X4 where G.X15 : X9 {
typealias X15 = X13<G.X15.X10>
}

struct X17<A : X4> : X1 where A.X15 == X12 {
typealias X3 = X14<A>
}

struct X18 : X4 {
typealias X15 = X12
}

@_transparent
func callee<T>(_: T) where T : X1 {
let _: T.X3.X15? = nil
}

func caller(b: X17<X18>) {
callee(b)
}