Skip to content

Commit 0e8bf17

Browse files
committed
[ConstraintSystem] Add cache for conformance lookups
`lookupConformance` request is not cached and constraint solver performs a lot of them for the same type (i.e. during disjunction solving), let's try to cache previously performed requests to see whether additional memory use is worth the performance benefit.
1 parent 8fd028c commit 0e8bf17

File tree

8 files changed

+63
-54
lines changed

8 files changed

+63
-54
lines changed

include/swift/Sema/CSBindings.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,23 +195,22 @@ struct LiteralRequirement {
195195
/// \param canBeNil The flag that determines whether given type
196196
/// variable requires all of its bindings to be optional.
197197
///
198-
/// \param useDC The declaration context in which this literal
199-
/// requirement is used.
198+
/// \param CS The constraint system this literal requirement belongs to.
200199
///
201200
/// \returns a pair of bool and a type:
202201
/// - bool, true if binding covers given literal protocol;
203202
/// - type, non-null if binding type has to be adjusted
204203
/// to cover given literal protocol;
205204
std::pair<bool, Type> isCoveredBy(const PotentialBinding &binding,
206205
bool canBeNil,
207-
DeclContext *useDC) const;
206+
ConstraintSystem &CS) const;
208207

209208
/// Determines whether literal protocol associated with this
210209
/// meta-information is viable for inclusion as a defaultable binding.
211210
bool viableAsBinding() const { return !isCovered() && hasDefaultType(); }
212211

213212
private:
214-
bool isCoveredBy(Type type, DeclContext *useDC) const;
213+
bool isCoveredBy(Type type, ConstraintSystem &CS) const;
215214
};
216215

217216
struct PotentialBindings {

include/swift/Sema/ConstraintSystem.h

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2197,6 +2197,10 @@ class ConstraintSystem {
21972197
llvm::SmallMapVector<ConstraintLocator *, ArrayRef<OpenedType>, 4>
21982198
OpenedTypes;
21992199

2200+
/// A dictionary of all conformances that have been looked up by the solver.
2201+
llvm::DenseMap<std::pair<TypeBase *, ProtocolDecl *>, ProtocolConformanceRef>
2202+
Conformances;
2203+
22002204
/// The list of all generic requirements fixed along the current
22012205
/// solver path.
22022206
using FixedRequirement =
@@ -3820,7 +3824,7 @@ class ConstraintSystem {
38203824
///
38213825
/// \param wantRValue Whether this routine should look through
38223826
/// lvalues at each step.
3823-
Type getFixedTypeRecursive(Type type, bool wantRValue) const {
3827+
Type getFixedTypeRecursive(Type type, bool wantRValue) {
38243828
TypeMatchOptions flags = None;
38253829
return getFixedTypeRecursive(type, flags, wantRValue);
38263830
}
@@ -3838,7 +3842,7 @@ class ConstraintSystem {
38383842
/// \param wantRValue Whether this routine should look through
38393843
/// lvalues at each step.
38403844
Type getFixedTypeRecursive(Type type, TypeMatchOptions &flags,
3841-
bool wantRValue) const;
3845+
bool wantRValue);
38423846

38433847
/// Determine whether the given type variable occurs within the given type.
38443848
///
@@ -4026,6 +4030,10 @@ class ConstraintSystem {
40264030
ConstraintLocatorBuilder locator,
40274031
const OpenedTypeMap &replacements);
40284032

4033+
/// Check whether the given type conforms to the given protocol and if
4034+
/// so return a valid conformance reference.
4035+
ProtocolConformanceRef lookupConformance(Type type, ProtocolDecl *P);
4036+
40294037
/// Wrapper over swift::adjustFunctionTypeForConcurrency that passes along
40304038
/// the appropriate closure-type and opening extraction functions.
40314039
AnyFunctionType *adjustFunctionTypeForConcurrency(
@@ -4524,7 +4532,7 @@ class ConstraintSystem {
45244532
///
45254533
/// The resulting types can be compared canonically, so long as additional
45264534
/// type equivalence requirements aren't introduced between comparisons.
4527-
Type simplifyType(Type type) const;
4535+
Type simplifyType(Type type);
45284536

45294537
/// Simplify a type, by replacing type variables with either their
45304538
/// fixed types (if available) or their representatives.
@@ -4604,8 +4612,9 @@ class ConstraintSystem {
46044612

46054613
/// Simplifies a type by replacing type variables with the result of
46064614
/// \c getFixedTypeFn and performing lookup on dependent member types.
4607-
Type simplifyTypeImpl(Type type,
4608-
llvm::function_ref<Type(TypeVariableType *)> getFixedTypeFn) const;
4615+
Type
4616+
simplifyTypeImpl(Type type,
4617+
llvm::function_ref<Type(TypeVariableType *)> getFixedTypeFn);
46094618

46104619
/// Attempt to simplify the given construction constraint.
46114620
///

lib/Sema/CSBindings.cpp

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,7 @@ void BindingSet::determineLiteralCoverage() {
663663
Type adjustedTy;
664664

665665
std::tie(isCovered, adjustedTy) =
666-
literal.isCoveredBy(*binding, allowsNil, CS.DC);
666+
literal.isCoveredBy(*binding, allowsNil, CS);
667667

668668
if (!isCovered)
669669
continue;
@@ -866,7 +866,7 @@ void PotentialBindings::addDefault(Constraint *constraint) {
866866
Defaults.insert(constraint);
867867
}
868868

869-
bool LiteralRequirement::isCoveredBy(Type type, DeclContext *useDC) const {
869+
bool LiteralRequirement::isCoveredBy(Type type, ConstraintSystem &CS) const {
870870
auto coversDefaultType = [](Type type, Type defaultType) -> bool {
871871
if (!defaultType->hasUnboundGenericType())
872872
return type->isEqual(defaultType);
@@ -886,14 +886,12 @@ bool LiteralRequirement::isCoveredBy(Type type, DeclContext *useDC) const {
886886
if (hasDefaultType() && coversDefaultType(type, getDefaultType()))
887887
return true;
888888

889-
return (bool)TypeChecker::conformsToProtocol(type, getProtocol(),
890-
useDC->getParentModule());
889+
return bool(CS.lookupConformance(type, getProtocol()));
891890
}
892891

893892
std::pair<bool, Type>
894-
LiteralRequirement::isCoveredBy(const PotentialBinding &binding,
895-
bool canBeNil,
896-
DeclContext *useDC) const {
893+
LiteralRequirement::isCoveredBy(const PotentialBinding &binding, bool canBeNil,
894+
ConstraintSystem &CS) const {
897895
auto type = binding.BindingType;
898896
switch (binding.Kind) {
899897
case AllowedBindingKind::Exact:
@@ -913,7 +911,7 @@ LiteralRequirement::isCoveredBy(const PotentialBinding &binding,
913911
if (type->isTypeVariableOrMember() || type->isPlaceholder())
914912
return std::make_pair(false, Type());
915913

916-
if (isCoveredBy(type, useDC)) {
914+
if (isCoveredBy(type, CS)) {
917915
return std::make_pair(true, requiresUnwrap ? type : binding.BindingType);
918916
}
919917

lib/Sema/CSGen.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -425,8 +425,7 @@ namespace {
425425
// the literal.
426426
if (otherArgTy && otherArgTy->getAnyNominal()) {
427427
if (otherArgTy->isEqual(paramTy) &&
428-
TypeChecker::conformsToProtocol(
429-
otherArgTy, literalProto, CS.DC->getParentModule())) {
428+
CS.lookupConformance(otherArgTy, literalProto)) {
430429
return true;
431430
}
432431
} else if (Type defaultType =

lib/Sema/CSSimplify.cpp

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8107,8 +8107,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
81078107
}
81088108

81098109
// Check whether this type conforms to the protocol.
8110-
auto conformance = DC->getParentModule()->lookupConformance(
8111-
type, protocol, /*allowMissing=*/true);
8110+
auto conformance = lookupConformance(type, protocol);
81128111
if (conformance) {
81138112
return recordConformance(conformance);
81148113
}
@@ -8221,8 +8220,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
82218220

82228221
if (auto rawValue = isRawRepresentable(*this, type)) {
82238222
if (!rawValue->isTypeVariableOrMember() &&
8224-
TypeChecker::conformsToProtocol(rawValue, protocol,
8225-
DC->getParentModule())) {
8223+
lookupConformance(rawValue, protocol)) {
82268224
auto *fix = UseRawValue::create(*this, type, protocolTy, loc);
82278225
// Since this is a conformance requirement failure (where the
82288226
// source is most likely an argument), let's increase its impact
@@ -8369,11 +8367,9 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyTransitivelyConformsTo(
83698367

83708368
auto *protocol = protocolTy->castTo<ProtocolType>()->getDecl();
83718369

8372-
auto *M = DC->getParentModule();
8373-
83748370
// First, let's check whether the type itself conforms,
83758371
// if it does - we are done.
8376-
if (M->lookupConformance(resolvedTy, protocol))
8372+
if (lookupConformance(resolvedTy, protocol))
83778373
return SolutionKind::Solved;
83788374

83798375
// If the type doesn't conform, let's check whether
@@ -8444,10 +8440,9 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyTransitivelyConformsTo(
84448440
}
84458441
}
84468442

8447-
return llvm::any_of(typesToCheck,
8448-
[&](Type type) {
8449-
return bool(M->lookupConformance(type, protocol));
8450-
})
8443+
return llvm::any_of(
8444+
typesToCheck,
8445+
[&](Type type) { return bool(lookupConformance(type, protocol)); })
84518446
? SolutionKind::Solved
84528447
: SolutionKind::Error;
84538448
}
@@ -9006,7 +9001,7 @@ static bool mayBeForKeyPathSubscriptWithoutLabel(ConstraintSystem &cs,
90069001
/// This is useful to figure out whether it makes sense to
90079002
/// perform dynamic member lookup or not.
90089003
static bool
9009-
allFromConditionalConformances(DeclContext *DC, Type baseTy,
9004+
allFromConditionalConformances(ConstraintSystem &cs, Type baseTy,
90109005
ArrayRef<OverloadChoice> candidates) {
90119006
auto *NTD = baseTy->getAnyNominal();
90129007
if (!NTD)
@@ -9025,8 +9020,7 @@ allFromConditionalConformances(DeclContext *DC, Type baseTy,
90259020
}
90269021

90279022
if (auto *protocol = candidateDC->getSelfProtocolDecl()) {
9028-
auto conformance = DC->getParentModule()->lookupConformance(
9029-
baseTy, protocol);
9023+
auto conformance = cs.lookupConformance(baseTy, protocol);
90309024
if (!conformance.isConcrete())
90319025
return false;
90329026

@@ -9708,7 +9702,7 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
97089702
const auto &candidates = result.ViableCandidates;
97099703

97109704
if ((candidates.empty() ||
9711-
allFromConditionalConformances(DC, instanceTy, candidates)) &&
9705+
allFromConditionalConformances(*this, instanceTy, candidates)) &&
97129706
!isSelfRecursiveKeyPathDynamicMemberLookup(*this, baseTy,
97139707
memberLocator)) {
97149708
auto &ctx = getASTContext();
@@ -10285,7 +10279,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
1028510279
// called within extensions to that type (usually adding 'clamp').
1028610280
bool treatAsViable =
1028710281
(member.isSimpleName("min") || member.isSimpleName("max")) &&
10288-
allFromConditionalConformances(DC, baseTy, result.ViableCandidates);
10282+
allFromConditionalConformances(*this, baseTy,
10283+
result.ViableCandidates);
1028910284

1029010285
generateConstraints(
1029110286
candidates, memberTy, outerAlternatives, useDC, locator, None,
@@ -10694,8 +10689,7 @@ ConstraintSystem::simplifyValueWitnessConstraint(
1069410689
// conformance already?
1069510690
auto proto = requirement->getDeclContext()->getSelfProtocolDecl();
1069610691
assert(proto && "Value witness constraint for a non-requirement");
10697-
auto conformance = useDC->getParentModule()->lookupConformance(
10698-
baseObjectType, proto);
10692+
auto conformance = lookupConformance(baseObjectType, proto);
1069910693
if (!conformance)
1070010694
return fail();
1070110695

lib/Sema/CSSolver.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2113,8 +2113,7 @@ void DisjunctionChoiceProducer::partitionGenericOperators(
21132113
if (auto *refined = dyn_cast<ProtocolDecl>(nominal))
21142114
return refined->inheritsFrom(protocol);
21152115

2116-
return (bool)TypeChecker::conformsToProtocol(nominal->getDeclaredType(), protocol,
2117-
CS.DC->getParentModule());
2116+
return bool(CS.lookupConformance(nominal->getDeclaredType(), protocol));
21182117
};
21192118

21202119
// Gather Numeric and Sequence overloads into separate buckets.

lib/Sema/ConstraintSystem.cpp

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,9 +1094,8 @@ Optional<Type> ConstraintSystem::isSetType(Type type) {
10941094
return None;
10951095
}
10961096

1097-
Type ConstraintSystem::getFixedTypeRecursive(Type type,
1098-
TypeMatchOptions &flags,
1099-
bool wantRValue) const {
1097+
Type ConstraintSystem::getFixedTypeRecursive(Type type, TypeMatchOptions &flags,
1098+
bool wantRValue) {
11001099

11011100
if (wantRValue)
11021101
type = type->getRValueType();
@@ -3707,7 +3706,7 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
37073706
namespace {
37083707

37093708
struct TypeSimplifier {
3710-
const ConstraintSystem &CS;
3709+
ConstraintSystem &CS;
37113710
llvm::function_ref<Type(TypeVariableType *)> GetFixedTypeFn;
37123711

37133712
struct ActivePackExpansion {
@@ -3716,7 +3715,7 @@ struct TypeSimplifier {
37163715
};
37173716
SmallVector<ActivePackExpansion, 4> ActivePackExpansions;
37183717

3719-
TypeSimplifier(const ConstraintSystem &CS,
3718+
TypeSimplifier(ConstraintSystem &CS,
37203719
llvm::function_ref<Type(TypeVariableType *)> getFixedTypeFn)
37213720
: CS(CS), GetFixedTypeFn(getFixedTypeFn) {}
37223721

@@ -3824,8 +3823,7 @@ struct TypeSimplifier {
38243823
if (lookupBaseType->mayHaveMembers() ||
38253824
lookupBaseType->is<PackType>()) {
38263825
auto *proto = assocType->getProtocol();
3827-
auto conformance = CS.DC->getParentModule()->lookupConformance(
3828-
lookupBaseType, proto);
3826+
auto conformance = CS.lookupConformance(lookupBaseType, proto);
38293827
if (!conformance) {
38303828
// FIXME: This regresses diagnostics if removed, but really the
38313829
// handling of a missing conformance should be the same for
@@ -3862,11 +3860,11 @@ struct TypeSimplifier {
38623860
} // end anonymous namespace
38633861

38643862
Type ConstraintSystem::simplifyTypeImpl(Type type,
3865-
llvm::function_ref<Type(TypeVariableType *)> getFixedTypeFn) const {
3863+
llvm::function_ref<Type(TypeVariableType *)> getFixedTypeFn) {
38663864
return type.transform(TypeSimplifier(*this, getFixedTypeFn));
38673865
}
38683866

3869-
Type ConstraintSystem::simplifyType(Type type) const {
3867+
Type ConstraintSystem::simplifyType(Type type) {
38703868
if (!type->hasTypeVariable())
38713869
return type;
38723870

@@ -5867,15 +5865,12 @@ bool constraints::hasAppliedSelf(const OverloadChoice &choice,
58675865
/// Check whether given type conforms to `RawRepresentable` protocol
58685866
/// and return the witness type.
58695867
Type constraints::isRawRepresentable(ConstraintSystem &cs, Type type) {
5870-
auto *DC = cs.DC;
5871-
58725868
auto rawReprType = TypeChecker::getProtocol(
58735869
cs.getASTContext(), SourceLoc(), KnownProtocolKind::RawRepresentable);
58745870
if (!rawReprType)
58755871
return Type();
58765872

5877-
auto conformance = TypeChecker::conformsToProtocol(type, rawReprType,
5878-
DC->getParentModule());
5873+
auto conformance = cs.lookupConformance(type, rawReprType);
58795874
if (conformance.isInvalid())
58805875
return Type();
58815876

@@ -7115,6 +7110,21 @@ bool ConstraintSystem::participatesInInference(ClosureExpr *closure) const {
71157110
}
71167111
}
71177112

7113+
ProtocolConformanceRef
7114+
ConstraintSystem::lookupConformance(Type type, ProtocolDecl *protocol) {
7115+
auto cacheKey = std::make_pair(type.getPointer(), protocol);
7116+
7117+
auto cachedConformance = Conformances.find(cacheKey);
7118+
if (cachedConformance != Conformances.end())
7119+
return cachedConformance->second;
7120+
7121+
auto conformance =
7122+
DC->getParentModule()->lookupConformance(type, protocol,
7123+
/*allowMissing=*/true);
7124+
Conformances[cacheKey] = conformance;
7125+
return conformance;
7126+
}
7127+
71187128
TypeVarBindingProducer::TypeVarBindingProducer(BindingSet &bindings)
71197129
: BindingProducer(bindings.getConstraintSystem(),
71207130
bindings.getTypeVariable()->getImpl().getLocator()),
@@ -7213,7 +7223,7 @@ bool TypeVarBindingProducer::requiresOptionalAdjustment(
72137223
auto *proto = CS.getASTContext().getProtocol(
72147224
KnownProtocolKind::ExpressibleByNilLiteral);
72157225

7216-
return !proto->getParentModule()->lookupConformance(type, proto);
7226+
return !CS.lookupConformance(type, proto);
72177227
} else if (binding.isDefaultableBinding() && binding.BindingType->isAny()) {
72187228
return true;
72197229
}

test/Constraints/array_literal.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ protocol P { }
371371
struct PArray<T> { }
372372

373373
extension PArray : ExpressibleByArrayLiteral where T: P {
374+
// expected-note@-1 {{requirement from conditional conformance of 'PArray<String>' to 'ExpressibleByArrayLiteral'}}
374375
typealias ArrayLiteralElement = T
375376

376377
init(arrayLiteral elements: T...) { }
@@ -380,7 +381,7 @@ extension Int: P { }
380381

381382
func testConditional(i: Int, s: String) {
382383
let _: PArray<Int> = [i, i, i]
383-
let _: PArray<String> = [s, s, s] // expected-error{{cannot convert value of type '[String]' to specified type 'PArray<String>'}}
384+
let _: PArray<String> = [s, s, s] // expected-error{{generic struct 'PArray' requires that 'String' conform to 'P'}}
384385
}
385386

386387

0 commit comments

Comments
 (0)