Skip to content

Commit 18f216c

Browse files
committed
Sema: Fix re-entrant call to ensureRequirementsAreSatisfied() when resolving associated conformances
ConformanceChecker::ensureRequirementsAreSatisfied() modifies the conformance as it resolves each one of its associated conformances, so a re-entrant call can end up corrupting state by adding too many elements to the buffer, or adding elements at the wrong offsets. If TypeChecker::checkConformanceRequirements() was called before ConformanceChecker::resolveTypeWitnesses(), we could re-entrantly call ConformanceChecker::ensureRequirementsAreSatisfied(): TypeChecker::checkConformanceRequirements() => ConformanceChecker::ensureRequirementsAreSatisfied() => Type::subst(), etc => ConformanceChecker::resolveTypeWitnesses() => ConformanceChecker::ensureRequirementsAreSatisfied() The code in SubstitutionMap::lookupConformance() worked around this by checking the failure condition and calling resolveTypeWitness() first, before calling getAssociatedConformance(). Instead, remove this and call resolveTypeWitness() from inside NormalProtocolConformance::getAssociatedConformance().
1 parent c947eda commit 18f216c

File tree

6 files changed

+31
-19
lines changed

6 files changed

+31
-19
lines changed

include/swift/AST/LazyResolver.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,6 @@ class LazyResolver {
7878
/// Mark the given conformance as "used" from the given declaration context.
7979
virtual void markConformanceUsed(ProtocolConformanceRef conformance,
8080
DeclContext *dc) = 0;
81-
82-
/// Fill in the signature conformances of the given protocol conformance.
83-
virtual void checkConformanceRequirements(
84-
NormalProtocolConformance *conformance) = 0;
8581
};
8682

8783
class LazyMemberLoader;

lib/AST/ProtocolConformance.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -904,8 +904,9 @@ NormalProtocolConformance::getAssociatedConformance(Type assocType,
904904
// Fill in the signature conformances, if we haven't done so yet.
905905
if (getSignatureConformances().empty()) {
906906
assocType->getASTContext().getLazyResolver()
907-
->checkConformanceRequirements(
908-
const_cast<NormalProtocolConformance *>(this));
907+
->resolveTypeWitness(
908+
const_cast<NormalProtocolConformance *>(this),
909+
nullptr);
909910
}
910911

911912
assert(!getSignatureConformances().empty() &&

lib/AST/SubstitutionMap.cpp

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -416,17 +416,6 @@ SubstitutionMap::lookupConformance(CanType type, ProtocolDecl *proto) const {
416416
// to use that.
417417
if (normal->getState() == ProtocolConformanceState::CheckingTypeWitnesses)
418418
return None;
419-
420-
auto lazyResolver = type->getASTContext().getLazyResolver();
421-
if (lazyResolver == nullptr)
422-
return None;
423-
424-
lazyResolver->resolveTypeWitness(normal, nullptr);
425-
426-
// Error case: the conformance is broken, so we cannot handle this
427-
// substitution.
428-
if (normal->getSignatureConformances().empty())
429-
return None;
430419
}
431420

432421
// Get the associated conformance.

lib/Sema/TypeChecker.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1676,8 +1676,7 @@ class TypeChecker final : public LazyResolver {
16761676
void checkConformance(NormalProtocolConformance *conformance);
16771677

16781678
/// Check the requirement signature of the given conformance.
1679-
void checkConformanceRequirements(NormalProtocolConformance *conformance)
1680-
override ;
1679+
void checkConformanceRequirements(NormalProtocolConformance *conformance);
16811680

16821681
/// Check all of the conformances in the given context.
16831682
void checkConformancesInContext(DeclContext *dc,
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
protocol Q {
2+
associatedtype T
3+
}
4+
5+
protocol P : Q {
6+
associatedtype U
7+
}
8+
9+
struct Conforms<T, U> : P {}
10+
11+
func takesP<T : P>(_: T) {}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %target-swift-frontend -emit-ir -primary-file %s %S/Inputs/signature_conformances_other.swift | %FileCheck %s
2+
3+
// Make sure we correctly determine the witness table is dependent, even though
4+
// it was defined in a different file.
5+
6+
// CHECK-LABEL: define hidden swiftcc void @"$s32signature_conformances_multifile5passPyyF"()
7+
func passP() {
8+
// CHECK: call swiftcc void @"$s32signature_conformances_multifile8ConformsVACyxq_GycfC"(%swift.type* @"$sSiN", %swift.type* @"$sSSN")
9+
// CHECK: %0 = call swiftcc %swift.metadata_response @"$s32signature_conformances_multifile8ConformsVySiSSGMa"
10+
// CHECK: %1 = extractvalue %swift.metadata_response %0
11+
// CHECK: %2 = call i8** @"$s32signature_conformances_multifile8ConformsVySiSSGACyxq_GAA1PAAWl"()
12+
// CHECK: call swiftcc void @"$s32signature_conformances_multifile6takesPyyxAA1PRzlF"(%swift.opaque* noalias nocapture undef, %swift.type* %1, i8** %2)
13+
takesP(Conforms<Int, String>())
14+
15+
// CHECK: ret void
16+
}

0 commit comments

Comments
 (0)