Skip to content

Commit c0303e0

Browse files
committed
Sema: Requestify Obj-C requirements map computation.
Avoids repeatedly rebuilding requirement maps during witness resolution.
1 parent 50e3143 commit c0303e0

File tree

6 files changed

+108
-67
lines changed

6 files changed

+108
-67
lines changed

include/swift/AST/Decl.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5074,6 +5074,35 @@ enum class KnownDerivableProtocolKind : uint8_t {
50745074

50755075
using PrimaryAssociatedTypeName = std::pair<Identifier, SourceLoc>;
50765076

5077+
/// A wrapper for a dictionary that maps Obj-C protocol requirement selectors to
5078+
/// a list of function decls.
5079+
class ObjCRequirementMap {
5080+
public:
5081+
using FunctionList = TinyPtrVector<AbstractFunctionDecl *>;
5082+
5083+
private:
5084+
using MethodKey = std::pair<ObjCSelector, char>;
5085+
llvm::SmallDenseMap<MethodKey, FunctionList, 4> storage;
5086+
5087+
static MethodKey getObjCMethodKey(AbstractFunctionDecl *func);
5088+
5089+
public:
5090+
void addRequirement(AbstractFunctionDecl *requirement) {
5091+
storage[getObjCMethodKey(requirement)].push_back(requirement);
5092+
}
5093+
5094+
/// Retrieve the Objective-C requirements in this protocol that have the
5095+
/// given Objective-C method key.
5096+
FunctionList getRequirements(AbstractFunctionDecl *requirement) const {
5097+
auto key = getObjCMethodKey(requirement);
5098+
auto known = storage.find(key);
5099+
if (known == storage.end())
5100+
return {};
5101+
5102+
return known->second;
5103+
}
5104+
};
5105+
50775106
/// ProtocolDecl - A declaration of a protocol, for example:
50785107
///
50795108
/// protocol Drawable {
@@ -5303,6 +5332,10 @@ class ProtocolDecl final : public NominalTypeDecl {
53035332
/// i.e., for a protocol P, returns the kind if inverse constraint ~P exists.
53045333
std::optional<InvertibleProtocolKind> getInvertibleProtocolKind() const;
53055334

5335+
/// Returns a dictionary that maps Obj-C protocol requirement selectors to a
5336+
/// list of function decls.
5337+
ObjCRequirementMap getObjCRequiremenMap() const;
5338+
53065339
private:
53075340
void computeKnownProtocolKind() const;
53085341

include/swift/AST/TypeCheckRequests.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4828,6 +4828,23 @@ class LocalTypeDeclsRequest
48284828
bool isCached() const { return true; }
48294829
};
48304830

4831+
class ObjCRequirementMapRequest
4832+
: public SimpleRequest<ObjCRequirementMapRequest,
4833+
ObjCRequirementMap(const ProtocolDecl *proto),
4834+
RequestFlags::Cached> {
4835+
public:
4836+
using SimpleRequest::SimpleRequest;
4837+
4838+
private:
4839+
friend SimpleRequest;
4840+
4841+
ObjCRequirementMap evaluate(Evaluator &evaluator,
4842+
const ProtocolDecl *proto) const;
4843+
4844+
public:
4845+
bool isCached() const { return true; }
4846+
};
4847+
48314848
#define SWIFT_TYPEID_ZONE TypeChecker
48324849
#define SWIFT_TYPEID_HEADER "swift/AST/TypeCheckerTypeIDZone.def"
48334850
#include "swift/Basic/DefineTypeIDZone.h"

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,3 +559,7 @@ SWIFT_REQUEST(TypeChecker, UniqueUnderlyingTypeSubstitutionsRequest,
559559
SWIFT_REQUEST(TypeChecker, LocalTypeDeclsRequest,
560560
ArrayRef<TypeDecl *>(SourceFile *),
561561
Cached, NoLocationInfo)
562+
SWIFT_REQUEST(TypeChecker, ObjCRequirementMapRequest,
563+
ObjCRequirementMap(const ProtocolDecl *proto),
564+
Cached, NoLocationInfo)
565+

lib/AST/Decl.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6467,6 +6467,15 @@ ProtocolDecl::getInvertibleProtocolKind() const {
64676467
return std::nullopt;
64686468
}
64696469

6470+
ObjCRequirementMap ProtocolDecl::getObjCRequiremenMap() const {
6471+
ObjCRequirementMap defaultMap;
6472+
if (!isObjC())
6473+
return defaultMap;
6474+
6475+
return evaluateOrDefault(getASTContext().evaluator,
6476+
ObjCRequirementMapRequest{this}, defaultMap);
6477+
}
6478+
64706479
ArrayRef<ProtocolDecl *> ProtocolDecl::getInheritedProtocols() const {
64716480
auto *mutThis = const_cast<ProtocolDecl *>(this);
64726481
return evaluateOrDefault(getASTContext().evaluator,

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 45 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1840,54 +1840,39 @@ RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement,
18401840
# pragma mark Witness resolution
18411841

18421842
/// Retrieve the Objective-C method key from the given function.
1843-
namespace {
1844-
using ObjCMethodKey = std::pair<ObjCSelector, char>;
1845-
using ObjCRequirementMap = llvm::SmallDenseMap<ObjCMethodKey,
1846-
TinyPtrVector<AbstractFunctionDecl *>, 4>;
1847-
}
1848-
1849-
/// Retrieve the Objective-C method key from the given function.
1850-
static ObjCMethodKey getObjCMethodKey(AbstractFunctionDecl *func) {
1843+
ObjCRequirementMap::MethodKey
1844+
ObjCRequirementMap::getObjCMethodKey(AbstractFunctionDecl *func) {
18511845
return std::make_pair(func->getObjCSelector(), func->isInstanceMember());
18521846
}
18531847

18541848
/// Precompute map for getObjCRequirements().
1855-
static ObjCRequirementMap getObjCRequirementMap(ProtocolDecl *proto) {
1856-
ObjCRequirementMap map;
1857-
1858-
if (!proto->isObjC())
1859-
return map;
1849+
ObjCRequirementMap
1850+
ObjCRequirementMapRequest::evaluate(Evaluator &evaluator,
1851+
const ProtocolDecl *proto) const {
1852+
// This map only applies to Obj-C protocols so it's wasteful to evaluate this
1853+
// request and cache the result for non-Obj-C protocols.
1854+
assert(proto->isObjC());
18601855

1856+
ObjCRequirementMap map;
18611857
for (auto requirement : proto->getProtocolRequirements()) {
18621858
auto funcRequirement = dyn_cast<AbstractFunctionDecl>(requirement);
18631859
if (!funcRequirement)
18641860
continue;
18651861

1866-
map[getObjCMethodKey(funcRequirement)].push_back(funcRequirement);
1862+
map.addRequirement(funcRequirement);
18671863
}
18681864

18691865
return map;
18701866
}
18711867

1872-
/// Retrieve the Objective-C requirements in this protocol that have the
1873-
/// given Objective-C method key.
1874-
static ArrayRef<AbstractFunctionDecl *>
1875-
getObjCRequirements(const ObjCRequirementMap &map, ObjCMethodKey key) {
1876-
auto known = map.find(key);
1877-
if (known == map.end())
1878-
return { };
1879-
1880-
return known->second;
1881-
}
1882-
18831868
/// @returns a non-null requirement if the given requirement is part of a
18841869
/// group of ObjC requirements that share the same ObjC method key.
18851870
/// The first such requirement that the predicate function returns true for
18861871
/// is the requirement required by this function. Otherwise, nullptr is
18871872
/// returned.
18881873
static ValueDecl *getObjCRequirementSibling(
1889-
ProtocolDecl *proto, ValueDecl *requirement, const ObjCRequirementMap &map,
1890-
llvm::function_ref<bool(AbstractFunctionDecl*)> predicate) {
1874+
ProtocolDecl *proto, ValueDecl *requirement,
1875+
llvm::function_ref<bool(AbstractFunctionDecl *)> predicate) {
18911876
if (!proto->isObjC())
18921877
return nullptr;
18931878

@@ -1896,8 +1881,8 @@ static ValueDecl *getObjCRequirementSibling(
18961881

18971882
// We only care about functions
18981883
if (auto fnRequirement = dyn_cast<AbstractFunctionDecl>(requirement)) {
1899-
auto fnSelector = getObjCMethodKey(fnRequirement);
1900-
auto similarRequirements = getObjCRequirements(map, fnSelector);
1884+
auto map = proto->getObjCRequiremenMap();
1885+
auto similarRequirements = map.getRequirements(fnRequirement);
19011886
// ... whose selector is one that maps to multiple requirement declarations.
19021887
for (auto candidate : similarRequirements) {
19031888
if (candidate == fnRequirement)
@@ -1948,9 +1933,8 @@ class MultiConformanceChecker {
19481933
NormalProtocolConformance *conformance);
19491934

19501935
/// Determine whether the given requirement was left unsatisfied.
1951-
bool isUnsatisfiedReq(
1952-
NormalProtocolConformance *conformance, ValueDecl *req,
1953-
const ObjCRequirementMap &map);
1936+
bool isUnsatisfiedReq(NormalProtocolConformance *conformance, ValueDecl *req);
1937+
19541938
public:
19551939
MultiConformanceChecker(ASTContext &ctx) : Context(ctx) {}
19561940

@@ -1977,9 +1961,8 @@ class MultiConformanceChecker {
19771961

19781962
}
19791963

1980-
bool MultiConformanceChecker::
1981-
isUnsatisfiedReq(NormalProtocolConformance *conformance, ValueDecl *req,
1982-
const ObjCRequirementMap &map) {
1964+
bool MultiConformanceChecker::isUnsatisfiedReq(
1965+
NormalProtocolConformance *conformance, ValueDecl *req) {
19831966
if (conformance->isInvalid()) return false;
19841967
if (isa<TypeDecl>(req)) return false;
19851968

@@ -1993,7 +1976,7 @@ isUnsatisfiedReq(NormalProtocolConformance *conformance, ValueDecl *req,
19931976
// If another @objc requirement refers to the same Objective-C
19941977
// method, this requirement isn't unsatisfied.
19951978
if (getObjCRequirementSibling(
1996-
proto, req, map, [conformance](AbstractFunctionDecl *cand) {
1979+
proto, req, [conformance](AbstractFunctionDecl *cand) {
19971980
return static_cast<bool>(conformance->getWitness(cand));
19981981
})) {
19991982
return false;
@@ -2034,12 +2017,11 @@ void MultiConformanceChecker::checkAllConformances() {
20342017
if (!anyInvalid) {
20352018
// Check whether there are any unsatisfied requirements.
20362019
auto proto = conformance->getProtocol();
2037-
ObjCRequirementMap map = getObjCRequirementMap(proto);
20382020

20392021
for (auto *req : proto->getProtocolRequirements()) {
20402022
// If the requirement is unsatisfied, we might want to warn
20412023
// about near misses; record it.
2042-
if (isUnsatisfiedReq(conformance, req, map)) {
2024+
if (isUnsatisfiedReq(conformance, req)) {
20432025
UnsatisfiedReqs.push_back(req);
20442026
continue;
20452027
}
@@ -3726,10 +3708,8 @@ hasSatisfiedObjCSiblingRequirement(ProtocolDecl *proto,
37263708
if (!proto->isObjC())
37273709
return false;
37283710

3729-
auto map = getObjCRequirementMap(proto);
3730-
37313711
if (getObjCRequirementSibling(
3732-
proto, fnRequirement, map,
3712+
proto, fnRequirement,
37333713
[proto, conformance](AbstractFunctionDecl *candidate) {
37343714
// FIXME: This performs a recursive lookup in the lazy case, so
37353715
// we have to dodge the cycle.
@@ -3962,6 +3942,27 @@ getAdopteeSelfSameTypeConstraint(ClassDecl *selfClass, ValueDecl *witness) {
39623942
return target;
39633943
}
39643944

3945+
static bool allowOptionalWitness(ProtocolDecl *proto,
3946+
NormalProtocolConformance *conformance,
3947+
ValueDecl *requirement) {
3948+
auto Attrs = requirement->getAttrs();
3949+
3950+
// An optional requirement is trivially satisfied with an empty requirement.
3951+
if (Attrs.hasAttribute<OptionalAttr>())
3952+
return true;
3953+
3954+
// An 'unavailable' requirement is treated like an optional requirement.
3955+
if (Attrs.isUnavailable(proto->getASTContext()))
3956+
return true;
3957+
3958+
// A requirement with a satisfied Obj-C alternative requirement is effectively
3959+
// optional.
3960+
if (hasSatisfiedObjCSiblingRequirement(proto, conformance, requirement))
3961+
return true;
3962+
3963+
return false;
3964+
}
3965+
39653966
void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement,
39663967
ValueDecl *witness) {
39673968
auto *classDecl = DC->getSelfClassDecl();
@@ -4388,7 +4389,7 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
43884389
//
43894390
// Treat 'unavailable' implicitly as if it were 'optional'.
43904391
// The compiler will reject actual uses.
4391-
if (allowOptionalWitness(requirement)) {
4392+
if (allowOptionalWitness(Proto, Conformance, requirement)) {
43924393
return ResolveWitnessResult::Missing;
43934394
}
43944395

@@ -4561,7 +4562,7 @@ ResolveWitnessResult ConformanceChecker::resolveWitnessViaDefault(
45614562
ValueDecl *requirement) {
45624563
assert(!isa<AssociatedTypeDecl>(requirement) && "Use resolveTypeWitnessVia*");
45634564

4564-
if (allowOptionalWitness(requirement)) {
4565+
if (allowOptionalWitness(Proto, Conformance, requirement)) {
45654566
recordOptionalWitness(requirement);
45664567
return ResolveWitnessResult::Success;
45674568
}
@@ -4632,25 +4633,6 @@ void ConformanceChecker::resolveSingleWitness(ValueDecl *requirement) {
46324633
}
46334634
}
46344635

4635-
bool ConformanceChecker::allowOptionalWitness(ValueDecl *requirement) {
4636-
auto Attrs = requirement->getAttrs();
4637-
4638-
// An optional requirement is trivially satisfied with an empty requirement.
4639-
if (Attrs.hasAttribute<OptionalAttr>())
4640-
return true;
4641-
4642-
// An 'unavailable' requirement is treated like an optional requirement.
4643-
if (Attrs.isUnavailable(getASTContext()))
4644-
return true;
4645-
4646-
// A requirement with a satisfied Obj-C alternative requirement is effectively
4647-
// optional.
4648-
if (hasSatisfiedObjCSiblingRequirement(Proto, Conformance, requirement))
4649-
return true;
4650-
4651-
return false;
4652-
}
4653-
46544636
/// FIXME: It feels like this could be part of findExistentialSelfReferences().
46554637
static std::optional<Requirement>
46564638
hasInvariantSelfRequirement(const ProtocolDecl *proto,
@@ -4973,7 +4955,6 @@ hasInvalidTypeInConformanceContext(const ValueDecl *requirement,
49734955

49744956
void ConformanceChecker::resolveValueWitnesses() {
49754957
bool usesPreconcurrencyConformance = false;
4976-
auto objcRequirementMap = getObjCRequirementMap(Proto);
49774958

49784959
for (auto *requirement : Proto->getProtocolRequirements()) {
49794960
// Associated type requirements handled elsewhere.
@@ -5119,7 +5100,7 @@ void ConformanceChecker::resolveValueWitnesses() {
51195100
// async-looking ObjC protocol method requirement into two Swift protocol
51205101
// requirements: an async version and a sync version. Exactly one of the two
51215102
// must be witnessed by the conformer.
5122-
if (getObjCRequirementSibling(Proto, requirement, objcRequirementMap,
5103+
if (getObjCRequirementSibling(Proto, requirement,
51235104
[this](AbstractFunctionDecl *cand) {
51245105
return static_cast<bool>(
51255106
this->Conformance->getWitness(cand));

lib/Sema/TypeCheckProtocol.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,6 @@ class ConformanceChecker : public WitnessChecker {
168168
/// particular requirement and adoptee is required, before the
169169
/// conformance has been completed checked.
170170
void resolveSingleWitness(ValueDecl *requirement);
171-
172-
private:
173-
bool allowOptionalWitness(ValueDecl *requirement);
174171
};
175172

176173
/// Match the given witness to the given requirement.

0 commit comments

Comments
 (0)