Skip to content

Commit 2f1b464

Browse files
authored
Cut down conformance checking time by reusing RequirementEnvironments (#18598)
Trade an extra copy for saving the cost of reinitialization when there are a lot of witnesses to check. A small but measurable win when type-checking the standard library. Also add a test to make sure we still get the requirement environment right for covariant 'Self'.
1 parent c275826 commit 2f1b464

File tree

3 files changed

+72
-26
lines changed

3 files changed

+72
-26
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -646,12 +646,32 @@ swift::matchWitness(
646646
return finalize(anyRenaming, optionalAdjustments);
647647
}
648648

649-
RequirementMatch swift::matchWitness(TypeChecker &tc,
650-
ProtocolDecl *proto,
651-
ProtocolConformance *conformance,
652-
DeclContext *dc,
653-
ValueDecl *req,
654-
ValueDecl *witness) {
649+
/// Checks \p reqEnvCache for a requirement environment appropriate for
650+
/// \p reqSig and \p covariantSelf. If one isn't there, it gets created from
651+
/// the rest of the parameters.
652+
///
653+
/// Note that this means RequirementEnvironmentCaches must not be shared across
654+
/// multiple protocols or conformances.
655+
static const RequirementEnvironment &getOrCreateRequirementEnvironment(
656+
WitnessChecker::RequirementEnvironmentCache &reqEnvCache,
657+
DeclContext *dc, GenericSignature *reqSig, ProtocolDecl *proto,
658+
ClassDecl *covariantSelf, ProtocolConformance *conformance) {
659+
WitnessChecker::RequirementEnvironmentCacheKey cacheKey(reqSig,
660+
covariantSelf);
661+
auto cacheIter = reqEnvCache.find(cacheKey);
662+
if (cacheIter == reqEnvCache.end()) {
663+
RequirementEnvironment reqEnv(dc, reqSig, proto, covariantSelf,
664+
conformance);
665+
cacheIter = reqEnvCache.insert({cacheKey, std::move(reqEnv)}).first;
666+
}
667+
return cacheIter->getSecond();
668+
}
669+
670+
RequirementMatch
671+
swift::matchWitness(TypeChecker &tc,
672+
WitnessChecker::RequirementEnvironmentCache &reqEnvCache,
673+
ProtocolDecl *proto, ProtocolConformance *conformance,
674+
DeclContext *dc, ValueDecl *req, ValueDecl *witness) {
655675
using namespace constraints;
656676

657677
// Initialized by the setup operation.
@@ -719,17 +739,18 @@ RequirementMatch swift::matchWitness(TypeChecker &tc,
719739
}
720740
}
721741

722-
Optional<RequirementEnvironment> reqEnvironment(
723-
RequirementEnvironment(dc, reqSig, proto, covariantSelf, conformance));
742+
const RequirementEnvironment &reqEnvironment =
743+
getOrCreateRequirementEnvironment(reqEnvCache, dc, reqSig, proto,
744+
covariantSelf, conformance);
724745

725746
// Set up the constraint system for matching.
726747
auto setup = [&]() -> std::tuple<Optional<RequirementMatch>, Type, Type> {
727748
// Construct a constraint system to use to solve the equality between
728749
// the required type and the witness type.
729750
cs.emplace(tc, dc, ConstraintSystemOptions());
730751

731-
auto reqGenericEnv = reqEnvironment->getSyntheticEnvironment();
732-
auto reqSubMap = reqEnvironment->getRequirementToSyntheticMap();
752+
auto reqGenericEnv = reqEnvironment.getSyntheticEnvironment();
753+
auto reqSubMap = reqEnvironment.getRequirementToSyntheticMap();
733754

734755
Type selfTy = proto->getSelfInterfaceType().subst(reqSubMap);
735756
if (reqGenericEnv)
@@ -820,7 +841,7 @@ RequirementMatch swift::matchWitness(TypeChecker &tc,
820841
: anyRenaming ? MatchKind::RenamedMatch
821842
: MatchKind::ExactMatch,
822843
witnessType,
823-
std::move(reqEnvironment),
844+
reqEnvironment,
824845
optionalAdjustments);
825846

826847
// Compute the set of substitutions we'll need for the witness.
@@ -1020,7 +1041,7 @@ bool WitnessChecker::findBestWitness(
10201041
}
10211042
}
10221043

1023-
auto match = matchWitness(TC, Proto, conformance, DC,
1044+
auto match = matchWitness(TC, ReqEnvironmentCache, Proto, conformance, DC,
10241045
requirement, witness);
10251046
if (match.isViable()) {
10261047
++numViable;
@@ -3038,7 +3059,8 @@ ResolveWitnessResult ConformanceChecker::resolveWitnessViaDerivation(
30383059
return ResolveWitnessResult::ExplicitFailed;
30393060

30403061
// Try to match the derived requirement.
3041-
auto match = matchWitness(TC, Proto, Conformance, DC, requirement, derived);
3062+
auto match = matchWitness(TC, ReqEnvironmentCache, Proto, Conformance, DC,
3063+
requirement, derived);
30423064
if (match.isViable()) {
30433065
recordWitness(requirement, match);
30443066
return ResolveWitnessResult::Success;
@@ -4445,9 +4467,10 @@ static void diagnosePotentialWitness(TypeChecker &tc,
44454467
proto->getFullName());
44464468

44474469
// Describe why the witness didn't satisfy the requirement.
4470+
WitnessChecker::RequirementEnvironmentCache oneUseCache;
44484471
auto dc = conformance->getDeclContext();
4449-
auto match = matchWitness(tc, conformance->getProtocol(), conformance,
4450-
dc, req, witness);
4472+
auto match = matchWitness(tc, oneUseCache, conformance->getProtocol(),
4473+
conformance, dc, req, witness);
44514474
if (match.Kind == MatchKind::ExactMatch &&
44524475
req->isObjC() && !witness->isObjC()) {
44534476
// Special case: note to add @objc.
@@ -4921,6 +4944,7 @@ swift::findWitnessedObjCRequirements(const ValueDecl *witness,
49214944
}
49224945
}
49234946

4947+
WitnessChecker::RequirementEnvironmentCache reqEnvCache;
49244948
ASTContext &ctx = nominal->getASTContext();
49254949
for (auto proto : nominal->getAllProtocols()) {
49264950
// We only care about Objective-C protocols.
@@ -4976,7 +5000,7 @@ swift::findWitnessedObjCRequirements(const ValueDecl *witness,
49765000
auto lazyResolver = ctx.getLazyResolver();
49775001
assert(lazyResolver && "Need a type checker to match witnesses");
49785002
auto &tc = *static_cast<TypeChecker *>(lazyResolver);
4979-
if (matchWitness(tc, proto, *conformance,
5003+
if (matchWitness(tc, reqEnvCache, proto, *conformance,
49805004
witnessToMatch->getDeclContext(), req,
49815005
const_cast<ValueDecl *>(witnessToMatch))
49825006
.Kind == MatchKind::ExactMatch) {
@@ -5214,8 +5238,9 @@ void TypeChecker::recordKnownWitness(NormalProtocolConformance *conformance,
52145238
// (because property behaviors rely on renaming).
52155239
validateDecl(witness);
52165240
auto dc = conformance->getDeclContext();
5217-
auto match = matchWitness(*this, conformance->getProtocol(), conformance,
5218-
dc, req, witness);
5241+
WitnessChecker::RequirementEnvironmentCache oneUseCache;
5242+
auto match = matchWitness(*this, oneUseCache, conformance->getProtocol(),
5243+
conformance, dc, req, witness);
52195244
if (match.Kind != MatchKind::ExactMatch &&
52205245
match.Kind != MatchKind::RenamedMatch) {
52215246
diagnose(witness, diag::property_behavior_conformance_broken,

lib/Sema/TypeCheckProtocol.h

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -333,14 +333,14 @@ class OptionalAdjustment {
333333
/// \brief Describes a match between a requirement and a witness.
334334
struct RequirementMatch {
335335
RequirementMatch(ValueDecl *witness, MatchKind kind,
336-
Optional<RequirementEnvironment> &&env = None)
336+
Optional<RequirementEnvironment> env = None)
337337
: Witness(witness), Kind(kind), WitnessType(), ReqEnv(std::move(env)) {
338338
assert(!hasWitnessType() && "Should have witness type");
339339
}
340340

341341
RequirementMatch(ValueDecl *witness, MatchKind kind,
342342
Type witnessType,
343-
Optional<RequirementEnvironment> &&env = None,
343+
Optional<RequirementEnvironment> env = None,
344344
ArrayRef<OptionalAdjustment> optionalAdjustments = {})
345345
: Witness(witness), Kind(kind), WitnessType(witnessType),
346346
ReqEnv(std::move(env)),
@@ -430,6 +430,12 @@ struct RequirementMatch {
430430
struct RequirementCheck;
431431

432432
class WitnessChecker {
433+
public:
434+
using RequirementEnvironmentCacheKey =
435+
std::pair<const GenericSignature *, const ClassDecl *>;
436+
using RequirementEnvironmentCache =
437+
llvm::DenseMap<RequirementEnvironmentCacheKey, RequirementEnvironment>;
438+
433439
protected:
434440
TypeChecker &TC;
435441
ProtocolDecl *Proto;
@@ -441,6 +447,8 @@ class WitnessChecker {
441447
// @_implements(Protocol, DeclName)
442448
llvm::DenseMap<DeclName, llvm::TinyPtrVector<ValueDecl *>> ImplementsTable;
443449

450+
RequirementEnvironmentCache ReqEnvironmentCache;
451+
444452
WitnessChecker(TypeChecker &tc, ProtocolDecl *proto,
445453
Type adoptee, DeclContext *dc);
446454

@@ -839,12 +847,11 @@ RequirementMatch matchWitness(
839847
RequirementMatch(bool, ArrayRef<OptionalAdjustment>)
840848
> finalize);
841849

842-
RequirementMatch matchWitness(TypeChecker &tc,
843-
ProtocolDecl *proto,
844-
ProtocolConformance *conformance,
845-
DeclContext *dc,
846-
ValueDecl *req,
847-
ValueDecl *witness);
850+
RequirementMatch
851+
matchWitness(TypeChecker &tc,
852+
WitnessChecker::RequirementEnvironmentCache &reqEnvCache,
853+
ProtocolDecl *proto, ProtocolConformance *conformance,
854+
DeclContext *dc, ValueDecl *req, ValueDecl *witness);
848855

849856
/// If the given type is a direct reference to an associated type of
850857
/// the given protocol, return the referenced associated type.

test/SILGen/witnesses_class.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,17 @@ class UsesDefaults<X : Barable> : HasDefaults {}
102102
// CHECK: [[FN:%.*]] = function_ref @$S15witnesses_class11HasDefaultsPAAE23hasDefaultGenericTakesTyy1TQz_qd__tAA7FooableRd__lF : $@convention(method) <τ_0_0 where τ_0_0 : HasDefaults><τ_1_0 where τ_1_0 : Fooable> (@in_guaranteed τ_0_0.T, @guaranteed τ_1_0, @in_guaranteed τ_0_0) -> ()
103103
// CHECK: apply [[FN]]<UsesDefaults<τ_0_0>, τ_1_0>(
104104
// CHECK: return
105+
106+
protocol ReturnsCovariantSelf {
107+
func returnsCovariantSelf() -> Self
108+
}
109+
extension ReturnsCovariantSelf {
110+
func returnsCovariantSelf() -> Self { return self }
111+
}
112+
113+
class NonMatchingMember: ReturnsCovariantSelf {
114+
func returnsCovariantSelf() {}
115+
}
116+
117+
// CHECK-LABEL: sil private [transparent] [thunk] @$S15witnesses_class17NonMatchingMemberCAA20ReturnsCovariantSelfA2aDP07returnsgH0xyFTW : $@convention(witness_method: ReturnsCovariantSelf) <τ_0_0 where τ_0_0 : NonMatchingMember> (@in_guaranteed τ_0_0) -> @out τ_0_0 {
118+

0 commit comments

Comments
 (0)