Skip to content

Commit 69315fb

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 5a3220e commit 69315fb

File tree

8 files changed

+62
-53
lines changed

8 files changed

+62
-53
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
@@ -3053,6 +3053,10 @@ class ConstraintSystem {
30533053
llvm::SmallMapVector<ConstraintLocator *, ArrayRef<OpenedType>, 4>
30543054
OpenedTypes;
30553055

3056+
/// A dictionary of all conformances that have been looked up by the solver.
3057+
llvm::DenseMap<std::pair<TypeBase *, ProtocolDecl *>, ProtocolConformanceRef>
3058+
Conformances;
3059+
30563060
/// The list of all generic requirements fixed along the current
30573061
/// solver path.
30583062
using FixedRequirement =
@@ -4670,7 +4674,7 @@ class ConstraintSystem {
46704674
///
46714675
/// \param wantRValue Whether this routine should look through
46724676
/// lvalues at each step.
4673-
Type getFixedTypeRecursive(Type type, bool wantRValue) const {
4677+
Type getFixedTypeRecursive(Type type, bool wantRValue) {
46744678
TypeMatchOptions flags = None;
46754679
return getFixedTypeRecursive(type, flags, wantRValue);
46764680
}
@@ -4688,7 +4692,7 @@ class ConstraintSystem {
46884692
/// \param wantRValue Whether this routine should look through
46894693
/// lvalues at each step.
46904694
Type getFixedTypeRecursive(Type type, TypeMatchOptions &flags,
4691-
bool wantRValue) const;
4695+
bool wantRValue);
46924696

46934697
/// Determine whether the given type variable occurs within the given type.
46944698
///
@@ -4880,6 +4884,10 @@ class ConstraintSystem {
48804884
ConstraintLocatorBuilder locator,
48814885
const OpenedTypeMap &replacements);
48824886

4887+
/// Check whether the given type conforms to the given protocol and if
4888+
/// so return a valid conformance reference.
4889+
ProtocolConformanceRef lookupConformance(Type type, ProtocolDecl *P);
4890+
48834891
/// Wrapper over swift::adjustFunctionTypeForConcurrency that passes along
48844892
/// the appropriate closure-type and opening extraction functions.
48854893
AnyFunctionType *adjustFunctionTypeForConcurrency(
@@ -5388,7 +5396,7 @@ class ConstraintSystem {
53885396
///
53895397
/// The resulting types can be compared canonically, so long as additional
53905398
/// type equivalence requirements aren't introduced between comparisons.
5391-
Type simplifyType(Type type) const;
5399+
Type simplifyType(Type type);
53925400

53935401
/// Simplify a type, by replacing type variables with either their
53945402
/// fixed types (if available) or their representatives.
@@ -5468,8 +5476,9 @@ class ConstraintSystem {
54685476

54695477
/// Simplifies a type by replacing type variables with the result of
54705478
/// \c getFixedTypeFn and performing lookup on dependent member types.
5471-
Type simplifyTypeImpl(Type type,
5472-
llvm::function_ref<Type(TypeVariableType *)> getFixedTypeFn) const;
5479+
Type
5480+
simplifyTypeImpl(Type type,
5481+
llvm::function_ref<Type(TypeVariableType *)> getFixedTypeFn);
54735482

54745483
/// Attempt to simplify the given construction constraint.
54755484
///

lib/Sema/CSBindings.cpp

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

653653
std::tie(isCovered, adjustedTy) =
654-
literal.isCoveredBy(*binding, allowsNil, CS.DC);
654+
literal.isCoveredBy(*binding, allowsNil, CS);
655655

656656
if (!isCovered)
657657
continue;
@@ -854,7 +854,7 @@ void PotentialBindings::addDefault(Constraint *constraint) {
854854
Defaults.insert(constraint);
855855
}
856856

857-
bool LiteralRequirement::isCoveredBy(Type type, DeclContext *useDC) const {
857+
bool LiteralRequirement::isCoveredBy(Type type, ConstraintSystem &CS) const {
858858
auto coversDefaultType = [](Type type, Type defaultType) -> bool {
859859
if (!defaultType->hasUnboundGenericType())
860860
return type->isEqual(defaultType);
@@ -874,14 +874,12 @@ bool LiteralRequirement::isCoveredBy(Type type, DeclContext *useDC) const {
874874
if (hasDefaultType() && coversDefaultType(type, getDefaultType()))
875875
return true;
876876

877-
return (bool)TypeChecker::conformsToProtocol(type, getProtocol(),
878-
useDC->getParentModule());
877+
return bool(CS.lookupConformance(type, getProtocol()));
879878
}
880879

881880
std::pair<bool, Type>
882-
LiteralRequirement::isCoveredBy(const PotentialBinding &binding,
883-
bool canBeNil,
884-
DeclContext *useDC) const {
881+
LiteralRequirement::isCoveredBy(const PotentialBinding &binding, bool canBeNil,
882+
ConstraintSystem &CS) const {
885883
auto type = binding.BindingType;
886884
switch (binding.Kind) {
887885
case AllowedBindingKind::Exact:
@@ -901,7 +899,7 @@ LiteralRequirement::isCoveredBy(const PotentialBinding &binding,
901899
if (type->isTypeVariableOrMember() || type->isPlaceholder())
902900
return std::make_pair(false, Type());
903901

904-
if (isCoveredBy(type, useDC)) {
902+
if (isCoveredBy(type, CS)) {
905903
return std::make_pair(true, requiresUnwrap ? type : binding.BindingType);
906904
}
907905

lib/Sema/CSGen.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -417,8 +417,7 @@ namespace {
417417
// the literal.
418418
if (otherArgTy && otherArgTy->getAnyNominal()) {
419419
if (otherArgTy->isEqual(paramTy) &&
420-
TypeChecker::conformsToProtocol(
421-
otherArgTy, literalProto, CS.DC->getParentModule())) {
420+
CS.lookupConformance(otherArgTy, literalProto)) {
422421
return true;
423422
}
424423
} else if (Type defaultType =

lib/Sema/CSSimplify.cpp

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

79657965
// Check whether this type conforms to the protocol.
7966-
auto conformance = DC->getParentModule()->lookupConformance(
7967-
type, protocol, /*allowMissing=*/true);
7966+
auto conformance = lookupConformance(type, protocol);
79687967
if (conformance) {
79697968
return recordConformance(conformance);
79707969
}
@@ -8079,8 +8078,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
80798078

80808079
if (auto rawValue = isRawRepresentable(*this, type)) {
80818080
if (!rawValue->isTypeVariableOrMember() &&
8082-
TypeChecker::conformsToProtocol(rawValue, protocol,
8083-
DC->getParentModule())) {
8081+
lookupConformance(rawValue, protocol)) {
80848082
auto *fix = UseRawValue::create(*this, type, protocolTy, loc);
80858083
// Since this is a conformance requirement failure (where the
80868084
// source is most likely an argument), let's increase its impact
@@ -8227,11 +8225,9 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyTransitivelyConformsTo(
82278225

82288226
auto *protocol = protocolTy->castTo<ProtocolType>()->getDecl();
82298227

8230-
auto *M = DC->getParentModule();
8231-
82328228
// First, let's check whether the type itself conforms,
82338229
// if it does - we are done.
8234-
if (M->lookupConformance(resolvedTy, protocol))
8230+
if (lookupConformance(resolvedTy, protocol))
82358231
return SolutionKind::Solved;
82368232

82378233
// If the type doesn't conform, let's check whether
@@ -8302,10 +8298,9 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyTransitivelyConformsTo(
83028298
}
83038299
}
83048300

8305-
return llvm::any_of(typesToCheck,
8306-
[&](Type type) {
8307-
return bool(M->lookupConformance(type, protocol));
8308-
})
8301+
return llvm::any_of(
8302+
typesToCheck,
8303+
[&](Type type) { return bool(lookupConformance(type, protocol)); })
83098304
? SolutionKind::Solved
83108305
: SolutionKind::Error;
83118306
}
@@ -8832,7 +8827,7 @@ static bool mayBeForKeyPathSubscriptWithoutLabel(ConstraintSystem &cs,
88328827
/// This is useful to figure out whether it makes sense to
88338828
/// perform dynamic member lookup or not.
88348829
static bool
8835-
allFromConditionalConformances(DeclContext *DC, Type baseTy,
8830+
allFromConditionalConformances(ConstraintSystem &cs, Type baseTy,
88368831
ArrayRef<OverloadChoice> candidates) {
88378832
auto *NTD = baseTy->getAnyNominal();
88388833
if (!NTD)
@@ -8851,8 +8846,7 @@ allFromConditionalConformances(DeclContext *DC, Type baseTy,
88518846
}
88528847

88538848
if (auto *protocol = candidateDC->getSelfProtocolDecl()) {
8854-
auto conformance = DC->getParentModule()->lookupConformance(
8855-
baseTy, protocol);
8849+
auto conformance = cs.lookupConformance(baseTy, protocol);
88568850
if (!conformance.isConcrete())
88578851
return false;
88588852

@@ -9520,7 +9514,7 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
95209514
const auto &candidates = result.ViableCandidates;
95219515

95229516
if ((candidates.empty() ||
9523-
allFromConditionalConformances(DC, instanceTy, candidates)) &&
9517+
allFromConditionalConformances(*this, instanceTy, candidates)) &&
95249518
!isSelfRecursiveKeyPathDynamicMemberLookup(*this, baseTy,
95259519
memberLocator)) {
95269520
auto &ctx = getASTContext();
@@ -10118,7 +10112,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
1011810112
// called within extensions to that type (usually adding 'clamp').
1011910113
bool treatAsViable =
1012010114
(member.isSimpleName("min") || member.isSimpleName("max")) &&
10121-
allFromConditionalConformances(DC, baseTy, result.ViableCandidates);
10115+
allFromConditionalConformances(*this, baseTy,
10116+
result.ViableCandidates);
1012210117

1012310118
generateConstraints(
1012410119
candidates, memberTy, outerAlternatives, useDC, locator, None,
@@ -10525,8 +10520,7 @@ ConstraintSystem::simplifyValueWitnessConstraint(
1052510520
// conformance already?
1052610521
auto proto = requirement->getDeclContext()->getSelfProtocolDecl();
1052710522
assert(proto && "Value witness constraint for a non-requirement");
10528-
auto conformance = useDC->getParentModule()->lookupConformance(
10529-
baseObjectType, proto);
10523+
auto conformance = lookupConformance(baseObjectType, proto);
1053010524
if (!conformance)
1053110525
return fail();
1053210526

lib/Sema/CSSolver.cpp

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

2108-
return (bool)TypeChecker::conformsToProtocol(nominal->getDeclaredType(), protocol,
2109-
CS.DC->getParentModule());
2108+
return bool(CS.lookupConformance(nominal->getDeclaredType(), protocol));
21102109
};
21112110

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

lib/Sema/ConstraintSystem.cpp

Lines changed: 23 additions & 13 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();
@@ -3674,8 +3673,8 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
36743673
}
36753674
}
36763675

3677-
Type ConstraintSystem::simplifyTypeImpl(Type type,
3678-
llvm::function_ref<Type(TypeVariableType *)> getFixedTypeFn) const {
3676+
Type ConstraintSystem::simplifyTypeImpl(
3677+
Type type, llvm::function_ref<Type(TypeVariableType *)> getFixedTypeFn) {
36793678
return type.transform([&](Type type) -> Type {
36803679
if (auto tvt = dyn_cast<TypeVariableType>(type.getPointer()))
36813680
return getFixedTypeFn(tvt);
@@ -3707,8 +3706,7 @@ Type ConstraintSystem::simplifyTypeImpl(Type type,
37073706
if (lookupBaseType->mayHaveMembers() ||
37083707
lookupBaseType->is<PackType>()) {
37093708
auto *proto = assocType->getProtocol();
3710-
auto conformance = DC->getParentModule()->lookupConformance(
3711-
lookupBaseType, proto);
3709+
auto conformance = lookupConformance(lookupBaseType, proto);
37123710
if (!conformance) {
37133711
// FIXME: This regresses diagnostics if removed, but really the
37143712
// handling of a missing conformance should be the same for
@@ -3742,7 +3740,7 @@ Type ConstraintSystem::simplifyTypeImpl(Type type,
37423740
});
37433741
}
37443742

3745-
Type ConstraintSystem::simplifyType(Type type) const {
3743+
Type ConstraintSystem::simplifyType(Type type) {
37463744
if (!type->hasTypeVariable())
37473745
return type;
37483746

@@ -5665,15 +5663,12 @@ bool constraints::hasAppliedSelf(const OverloadChoice &choice,
56655663
/// Check whether given type conforms to `RawRepresentable` protocol
56665664
/// and return the witness type.
56675665
Type constraints::isRawRepresentable(ConstraintSystem &cs, Type type) {
5668-
auto *DC = cs.DC;
5669-
56705666
auto rawReprType = TypeChecker::getProtocol(
56715667
cs.getASTContext(), SourceLoc(), KnownProtocolKind::RawRepresentable);
56725668
if (!rawReprType)
56735669
return Type();
56745670

5675-
auto conformance = TypeChecker::conformsToProtocol(type, rawReprType,
5676-
DC->getParentModule());
5671+
auto conformance = cs.lookupConformance(type, rawReprType);
56775672
if (conformance.isInvalid())
56785673
return Type();
56795674

@@ -7168,6 +7163,21 @@ bool ConstraintSystem::participatesInInference(ClosureExpr *closure) const {
71687163
}
71697164
}
71707165

7166+
ProtocolConformanceRef
7167+
ConstraintSystem::lookupConformance(Type type, ProtocolDecl *protocol) {
7168+
auto cacheKey = std::make_pair(type.getPointer(), protocol);
7169+
7170+
auto cachedConformance = Conformances.find(cacheKey);
7171+
if (cachedConformance != Conformances.end())
7172+
return cachedConformance->second;
7173+
7174+
auto conformance =
7175+
DC->getParentModule()->lookupConformance(type, protocol,
7176+
/*allowMissing=*/true);
7177+
Conformances[cacheKey] = conformance;
7178+
return conformance;
7179+
}
7180+
71717181
TypeVarBindingProducer::TypeVarBindingProducer(BindingSet &bindings)
71727182
: BindingProducer(bindings.getConstraintSystem(),
71737183
bindings.getTypeVariable()->getImpl().getLocator()),
@@ -7266,7 +7276,7 @@ bool TypeVarBindingProducer::requiresOptionalAdjustment(
72667276
auto *proto = CS.getASTContext().getProtocol(
72677277
KnownProtocolKind::ExpressibleByNilLiteral);
72687278

7269-
return !proto->getParentModule()->lookupConformance(type, proto);
7279+
return !CS.lookupConformance(type, proto);
72707280
} else if (binding.isDefaultableBinding() && binding.BindingType->isAny()) {
72717281
return true;
72727282
}

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)