Skip to content

Commit 32d2651

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 b0d3234 commit 32d2651

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
@@ -2250,6 +2250,10 @@ class ConstraintSystem {
22502250
llvm::SmallMapVector<ConstraintLocator *, ArrayRef<OpenedType>, 4>
22512251
OpenedTypes;
22522252

2253+
/// A dictionary of all conformances that have been looked up by the solver.
2254+
llvm::DenseMap<std::pair<TypeBase *, ProtocolDecl *>, ProtocolConformanceRef>
2255+
Conformances;
2256+
22532257
/// The list of all generic requirements fixed along the current
22542258
/// solver path.
22552259
using FixedRequirement =
@@ -3946,7 +3950,7 @@ class ConstraintSystem {
39463950
///
39473951
/// \param wantRValue Whether this routine should look through
39483952
/// lvalues at each step.
3949-
Type getFixedTypeRecursive(Type type, bool wantRValue) const {
3953+
Type getFixedTypeRecursive(Type type, bool wantRValue) {
39503954
TypeMatchOptions flags = llvm::None;
39513955
return getFixedTypeRecursive(type, flags, wantRValue);
39523956
}
@@ -3964,7 +3968,7 @@ class ConstraintSystem {
39643968
/// \param wantRValue Whether this routine should look through
39653969
/// lvalues at each step.
39663970
Type getFixedTypeRecursive(Type type, TypeMatchOptions &flags,
3967-
bool wantRValue) const;
3971+
bool wantRValue);
39683972

39693973
/// Determine whether the given type variable occurs within the given type.
39703974
///
@@ -4198,6 +4202,10 @@ class ConstraintSystem {
41984202
ConstraintLocatorBuilder locator,
41994203
const OpenedTypeMap &replacements);
42004204

4205+
/// Check whether the given type conforms to the given protocol and if
4206+
/// so return a valid conformance reference.
4207+
ProtocolConformanceRef lookupConformance(Type type, ProtocolDecl *P);
4208+
42014209
/// Wrapper over swift::adjustFunctionTypeForConcurrency that passes along
42024210
/// the appropriate closure-type and opening extraction functions.
42034211
AnyFunctionType *adjustFunctionTypeForConcurrency(
@@ -4711,7 +4719,7 @@ class ConstraintSystem {
47114719
///
47124720
/// The resulting types can be compared canonically, so long as additional
47134721
/// type equivalence requirements aren't introduced between comparisons.
4714-
Type simplifyType(Type type) const;
4722+
Type simplifyType(Type type);
47154723

47164724
/// Simplify a type, by replacing type variables with either their
47174725
/// fixed types (if available) or their representatives.
@@ -4791,8 +4799,9 @@ class ConstraintSystem {
47914799

47924800
/// Simplifies a type by replacing type variables with the result of
47934801
/// \c getFixedTypeFn and performing lookup on dependent member types.
4794-
Type simplifyTypeImpl(Type type,
4795-
llvm::function_ref<Type(TypeVariableType *)> getFixedTypeFn) const;
4802+
Type
4803+
simplifyTypeImpl(Type type,
4804+
llvm::function_ref<Type(TypeVariableType *)> getFixedTypeFn);
47964805

47974806
/// Attempt to simplify the given construction constraint.
47984807
///

lib/Sema/CSBindings.cpp

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

671671
std::tie(isCovered, adjustedTy) =
672-
literal.isCoveredBy(*binding, allowsNil, CS.DC);
672+
literal.isCoveredBy(*binding, allowsNil, CS);
673673

674674
if (!isCovered)
675675
continue;
@@ -872,7 +872,7 @@ void PotentialBindings::addDefault(Constraint *constraint) {
872872
Defaults.insert(constraint);
873873
}
874874

875-
bool LiteralRequirement::isCoveredBy(Type type, DeclContext *useDC) const {
875+
bool LiteralRequirement::isCoveredBy(Type type, ConstraintSystem &CS) const {
876876
auto coversDefaultType = [](Type type, Type defaultType) -> bool {
877877
if (!defaultType->hasUnboundGenericType())
878878
return type->isEqual(defaultType);
@@ -892,14 +892,12 @@ bool LiteralRequirement::isCoveredBy(Type type, DeclContext *useDC) const {
892892
if (hasDefaultType() && coversDefaultType(type, getDefaultType()))
893893
return true;
894894

895-
return (bool)TypeChecker::conformsToProtocol(type, getProtocol(),
896-
useDC->getParentModule());
895+
return bool(CS.lookupConformance(type, getProtocol()));
897896
}
898897

899898
std::pair<bool, Type>
900-
LiteralRequirement::isCoveredBy(const PotentialBinding &binding,
901-
bool canBeNil,
902-
DeclContext *useDC) const {
899+
LiteralRequirement::isCoveredBy(const PotentialBinding &binding, bool canBeNil,
900+
ConstraintSystem &CS) const {
903901
auto type = binding.BindingType;
904902
switch (binding.Kind) {
905903
case AllowedBindingKind::Exact:
@@ -919,7 +917,7 @@ LiteralRequirement::isCoveredBy(const PotentialBinding &binding,
919917
if (type->isTypeVariableOrMember() || type->isPlaceholder())
920918
return std::make_pair(false, Type());
921919

922-
if (isCoveredBy(type, useDC)) {
920+
if (isCoveredBy(type, CS)) {
923921
return std::make_pair(true, requiresUnwrap ? type : binding.BindingType);
924922
}
925923

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
@@ -8425,8 +8425,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
84258425
}
84268426

84278427
// Check whether this type conforms to the protocol.
8428-
auto conformance = DC->getParentModule()->lookupConformance(
8429-
type, protocol, /*allowMissing=*/true);
8428+
auto conformance = lookupConformance(type, protocol);
84308429
if (conformance) {
84318430
return recordConformance(conformance);
84328431
}
@@ -8546,8 +8545,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
85468545

85478546
if (auto rawValue = isRawRepresentable(*this, type)) {
85488547
if (!rawValue->isTypeVariableOrMember() &&
8549-
TypeChecker::conformsToProtocol(rawValue, protocol,
8550-
DC->getParentModule())) {
8548+
lookupConformance(rawValue, protocol)) {
85518549
auto *fix = UseRawValue::create(*this, type, protocolTy, loc);
85528550
// Since this is a conformance requirement failure (where the
85538551
// source is most likely an argument), let's increase its impact
@@ -8706,11 +8704,9 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyTransitivelyConformsTo(
87068704

87078705
auto *protocol = protocolTy->castTo<ProtocolType>()->getDecl();
87088706

8709-
auto *M = DC->getParentModule();
8710-
87118707
// First, let's check whether the type itself conforms,
87128708
// if it does - we are done.
8713-
if (M->lookupConformance(resolvedTy, protocol))
8709+
if (lookupConformance(resolvedTy, protocol))
87148710
return SolutionKind::Solved;
87158711

87168712
// If the type doesn't conform, let's check whether
@@ -8782,10 +8778,9 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyTransitivelyConformsTo(
87828778
}
87838779
}
87848780

8785-
return llvm::any_of(typesToCheck,
8786-
[&](Type type) {
8787-
return bool(M->lookupConformance(type, protocol));
8788-
})
8781+
return llvm::any_of(
8782+
typesToCheck,
8783+
[&](Type type) { return bool(lookupConformance(type, protocol)); })
87898784
? SolutionKind::Solved
87908785
: SolutionKind::Error;
87918786
}
@@ -9372,7 +9367,7 @@ static bool mayBeForKeyPathSubscriptWithoutLabel(ConstraintSystem &cs,
93729367
/// This is useful to figure out whether it makes sense to
93739368
/// perform dynamic member lookup or not.
93749369
static bool
9375-
allFromConditionalConformances(DeclContext *DC, Type baseTy,
9370+
allFromConditionalConformances(ConstraintSystem &cs, Type baseTy,
93769371
ArrayRef<OverloadChoice> candidates) {
93779372
auto *NTD = baseTy->getAnyNominal();
93789373
if (!NTD)
@@ -9391,8 +9386,7 @@ allFromConditionalConformances(DeclContext *DC, Type baseTy,
93919386
}
93929387

93939388
if (auto *protocol = candidateDC->getSelfProtocolDecl()) {
9394-
auto conformance = DC->getParentModule()->lookupConformance(
9395-
baseTy, protocol);
9389+
auto conformance = cs.lookupConformance(baseTy, protocol);
93969390
if (!conformance.isConcrete())
93979391
return false;
93989392

@@ -10130,7 +10124,7 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
1013010124
const auto &candidates = result.ViableCandidates;
1013110125

1013210126
if ((candidates.empty() ||
10133-
allFromConditionalConformances(DC, instanceTy, candidates)) &&
10127+
allFromConditionalConformances(*this, instanceTy, candidates)) &&
1013410128
!isSelfRecursiveKeyPathDynamicMemberLookup(*this, baseTy,
1013510129
memberLocator)) {
1013610130
auto &ctx = getASTContext();
@@ -10713,7 +10707,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
1071310707
// called within extensions to that type (usually adding 'clamp').
1071410708
bool treatAsViable =
1071510709
(member.isSimpleName("min") || member.isSimpleName("max")) &&
10716-
allFromConditionalConformances(DC, baseTy, result.ViableCandidates);
10710+
allFromConditionalConformances(*this, baseTy,
10711+
result.ViableCandidates);
1071710712

1071810713
generateConstraints(
1071910714
candidates, memberTy, outerAlternatives, useDC, locator, llvm::None,
@@ -11121,8 +11116,7 @@ ConstraintSystem::simplifyValueWitnessConstraint(
1112111116
// conformance already?
1112211117
auto proto = requirement->getDeclContext()->getSelfProtocolDecl();
1112311118
assert(proto && "Value witness constraint for a non-requirement");
11124-
auto conformance = useDC->getParentModule()->lookupConformance(
11125-
baseObjectType, proto);
11119+
auto conformance = lookupConformance(baseObjectType, proto);
1112611120
if (!conformance)
1112711121
return fail();
1112811122

lib/Sema/CSSolver.cpp

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

2178-
return (bool)TypeChecker::conformsToProtocol(nominal->getDeclaredType(), protocol,
2179-
CS.DC->getParentModule());
2178+
return bool(CS.lookupConformance(nominal->getDeclaredType(), protocol));
21802179
};
21812180

21822181
// 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
@@ -1196,9 +1196,8 @@ llvm::Optional<Type> ConstraintSystem::isSetType(Type type) {
11961196
return llvm::None;
11971197
}
11981198

1199-
Type ConstraintSystem::getFixedTypeRecursive(Type type,
1200-
TypeMatchOptions &flags,
1201-
bool wantRValue) const {
1199+
Type ConstraintSystem::getFixedTypeRecursive(Type type, TypeMatchOptions &flags,
1200+
bool wantRValue) {
12021201

12031202
if (wantRValue)
12041203
type = type->getRValueType();
@@ -3822,7 +3821,7 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
38223821
namespace {
38233822

38243823
struct TypeSimplifier {
3825-
const ConstraintSystem &CS;
3824+
ConstraintSystem &CS;
38263825
llvm::function_ref<Type(TypeVariableType *)> GetFixedTypeFn;
38273826

38283827
struct ActivePackExpansion {
@@ -3831,7 +3830,7 @@ struct TypeSimplifier {
38313830
};
38323831
SmallVector<ActivePackExpansion, 4> ActivePackExpansions;
38333832

3834-
TypeSimplifier(const ConstraintSystem &CS,
3833+
TypeSimplifier(ConstraintSystem &CS,
38353834
llvm::function_ref<Type(TypeVariableType *)> getFixedTypeFn)
38363835
: CS(CS), GetFixedTypeFn(getFixedTypeFn) {}
38373836

@@ -3979,8 +3978,7 @@ struct TypeSimplifier {
39793978
if (lookupBaseType->mayHaveMembers() ||
39803979
lookupBaseType->is<PackType>()) {
39813980
auto *proto = assocType->getProtocol();
3982-
auto conformance = CS.DC->getParentModule()->lookupConformance(
3983-
lookupBaseType, proto);
3981+
auto conformance = CS.lookupConformance(lookupBaseType, proto);
39843982
if (!conformance) {
39853983
// If the base type doesn't conform to the associatedtype's protocol,
39863984
// there will be a missing conformance fix applied in diagnostic mode,
@@ -4011,11 +4009,11 @@ struct TypeSimplifier {
40114009
} // end anonymous namespace
40124010

40134011
Type ConstraintSystem::simplifyTypeImpl(Type type,
4014-
llvm::function_ref<Type(TypeVariableType *)> getFixedTypeFn) const {
4012+
llvm::function_ref<Type(TypeVariableType *)> getFixedTypeFn) {
40154013
return type.transform(TypeSimplifier(*this, getFixedTypeFn));
40164014
}
40174015

4018-
Type ConstraintSystem::simplifyType(Type type) const {
4016+
Type ConstraintSystem::simplifyType(Type type) {
40194017
if (!type->hasTypeVariable())
40204018
return type;
40214019

@@ -6098,15 +6096,12 @@ bool constraints::hasAppliedSelf(const OverloadChoice &choice,
60986096
/// Check whether given type conforms to `RawRepresentable` protocol
60996097
/// and return the witness type.
61006098
Type constraints::isRawRepresentable(ConstraintSystem &cs, Type type) {
6101-
auto *DC = cs.DC;
6102-
61036099
auto rawReprType = TypeChecker::getProtocol(
61046100
cs.getASTContext(), SourceLoc(), KnownProtocolKind::RawRepresentable);
61056101
if (!rawReprType)
61066102
return Type();
61076103

6108-
auto conformance = TypeChecker::conformsToProtocol(type, rawReprType,
6109-
DC->getParentModule());
6104+
auto conformance = cs.lookupConformance(type, rawReprType);
61106105
if (conformance.isInvalid())
61116106
return Type();
61126107

@@ -7342,6 +7337,21 @@ bool ConstraintSystem::participatesInInference(ClosureExpr *closure) const {
73427337
return true;
73437338
}
73447339

7340+
ProtocolConformanceRef
7341+
ConstraintSystem::lookupConformance(Type type, ProtocolDecl *protocol) {
7342+
auto cacheKey = std::make_pair(type.getPointer(), protocol);
7343+
7344+
auto cachedConformance = Conformances.find(cacheKey);
7345+
if (cachedConformance != Conformances.end())
7346+
return cachedConformance->second;
7347+
7348+
auto conformance =
7349+
DC->getParentModule()->lookupConformance(type, protocol,
7350+
/*allowMissing=*/true);
7351+
Conformances[cacheKey] = conformance;
7352+
return conformance;
7353+
}
7354+
73457355
TypeVarBindingProducer::TypeVarBindingProducer(BindingSet &bindings)
73467356
: BindingProducer(bindings.getConstraintSystem(),
73477357
bindings.getTypeVariable()->getImpl().getLocator()),
@@ -7474,7 +7484,7 @@ bool TypeVarBindingProducer::requiresOptionalAdjustment(
74747484
auto *proto = CS.getASTContext().getProtocol(
74757485
KnownProtocolKind::ExpressibleByNilLiteral);
74767486

7477-
return !proto->getParentModule()->lookupConformance(type, proto);
7487+
return !CS.lookupConformance(type, proto);
74787488
} else if (binding.isDefaultableBinding() && binding.BindingType->isAny()) {
74797489
return true;
74807490
}

test/Constraints/array_literal.swift

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

360360
extension PArray : ExpressibleByArrayLiteral where T: P {
361+
// expected-note@-1 {{requirement from conditional conformance of 'PArray<String>' to 'ExpressibleByArrayLiteral'}}
361362
typealias ArrayLiteralElement = T
362363

363364
init(arrayLiteral elements: T...) { }
@@ -367,7 +368,7 @@ extension Int: P { }
367368

368369
func testConditional(i: Int, s: String) {
369370
let _: PArray<Int> = [i, i, i]
370-
let _: PArray<String> = [s, s, s] // expected-error{{cannot convert value of type '[String]' to specified type 'PArray<String>'}}
371+
let _: PArray<String> = [s, s, s] // expected-error{{generic struct 'PArray' requires that 'String' conform to 'P'}}
371372
}
372373

373374

0 commit comments

Comments
 (0)