Skip to content

Commit 6c08ff7

Browse files
committed
[NFC] Change how @objcImpl async matching is done
swiftlang#65253 (79e2697) made it so that the async and completion-handler variants of a member would both be matched by the same implementation, but the technique used composes poorly with typechecking work later in this series of commits. Reimplement it so that async variants are filtered out of `ObjCImplementationChecker::unmatchedRequirements` and then async candidates are compared against async alternatives later, during matching.
1 parent 2333ff5 commit 6c08ff7

File tree

1 file changed

+46
-69
lines changed

1 file changed

+46
-69
lines changed

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 46 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -2885,15 +2885,6 @@ class ObjCImplementationChecker {
28852885
/// Candidates with their explicit ObjC names, if any.
28862886
llvm::SmallDenseMap<ValueDecl *, ObjCSelector, 16> unmatchedCandidates;
28872887

2888-
/// Key that can be used to uniquely identify a particular Objective-C
2889-
/// method.
2890-
using ObjCMethodKey = std::pair<ObjCSelector, char>;
2891-
2892-
/// Mapping from Objective-C methods to the set of requirements within this
2893-
/// protocol that have the same selector and instance/class designation.
2894-
llvm::SmallDenseMap<ObjCMethodKey, TinyPtrVector<AbstractFunctionDecl *>, 4>
2895-
objcMethodRequirements;
2896-
28972888
public:
28982889
ObjCImplementationChecker(ExtensionDecl *ext)
28992890
: diags(ext->getASTContext().Diags)
@@ -2915,8 +2906,30 @@ class ObjCImplementationChecker {
29152906
}
29162907

29172908
private:
2918-
auto getObjCMethodKey(AbstractFunctionDecl *func) const -> ObjCMethodKey {
2919-
return ObjCMethodKey(func->getObjCSelector(), func->isInstanceMember());
2909+
static bool hasAsync(ValueDecl *member) {
2910+
if (!member)
2911+
return false;
2912+
2913+
if (auto func = dyn_cast<AbstractFunctionDecl>(member))
2914+
return func->hasAsync();
2915+
2916+
if (auto storage = dyn_cast<AbstractStorageDecl>(member))
2917+
return hasAsync(storage->getEffectfulGetAccessor());
2918+
2919+
return false;
2920+
}
2921+
2922+
static ValueDecl *getAsyncAlternative(ValueDecl *req) {
2923+
if (auto func = dyn_cast<AbstractFunctionDecl>(req)) {
2924+
auto asyncFunc = func->getAsyncAlternative();
2925+
2926+
if (auto asyncAccessor = dyn_cast<AccessorDecl>(asyncFunc))
2927+
return asyncAccessor->getStorage();
2928+
2929+
return asyncFunc;
2930+
}
2931+
2932+
return nullptr;
29202933
}
29212934

29222935
void addRequirements(IterableDeclContext *idc) {
@@ -2932,12 +2945,13 @@ class ObjCImplementationChecker {
29322945
if (member->getAttrs().isUnavailable(member->getASTContext()))
29332946
continue;
29342947

2948+
// Skip async versions of members. We'll match against the completion
2949+
// handler versions, hopping over to `getAsyncAlternative()` if needed.
2950+
if (hasAsync(member))
2951+
continue;
2952+
29352953
auto inserted = unmatchedRequirements.insert(member);
29362954
assert(inserted && "objc interface member added twice?");
2937-
2938-
if (auto func = dyn_cast<AbstractFunctionDecl>(member)) {
2939-
objcMethodRequirements[getObjCMethodKey(func)].push_back(func);
2940-
}
29412955
}
29422956
}
29432957

@@ -3034,19 +3048,6 @@ class ObjCImplementationChecker {
30343048
}
30353049
};
30363050

3037-
/// Determine whether the set of matched requirements are ambiguous for the
3038-
/// given candidate.
3039-
bool areRequirementsAmbiguous(const BestMatchList &reqs, ValueDecl *cand) {
3040-
if (reqs.matches.size() != 2)
3041-
return reqs.matches.size() > 2;
3042-
3043-
bool firstIsAsyncAlternative =
3044-
matchesAsyncAlternative(reqs.matches[0], cand);
3045-
bool secondIsAsyncAlternative =
3046-
matchesAsyncAlternative(reqs.matches[1], cand);
3047-
return firstIsAsyncAlternative == secondIsAsyncAlternative;
3048-
}
3049-
30503051
void matchRequirementsAtThreshold(MatchOutcome threshold) {
30513052
SmallString<32> scratch;
30523053

@@ -3106,14 +3107,11 @@ class ObjCImplementationChecker {
31063107
// removing them.
31073108
requirementsToRemove.set_union(matchedRequirements.matches);
31083109

3109-
if (!areRequirementsAmbiguous(matchedRequirements, cand)) {
3110+
if (matchedRequirements.matches.size() == 1) {
31103111
// Note that this is BestMatchList::insert(), so it'll only keep the
31113112
// matches with the best outcomes.
3112-
for (auto req : matchedRequirements.matches) {
3113-
matchesByRequirement[req]
3114-
.insert(cand, matchedRequirements.currentOutcome);
3115-
}
3116-
3113+
matchesByRequirement[matchedRequirements.matches.front()]
3114+
.insert(cand, matchedRequirements.currentOutcome);
31173115
continue;
31183116
}
31193117

@@ -3195,35 +3193,6 @@ class ObjCImplementationChecker {
31953193
unmatchedCandidates.erase(cand);
31963194
}
31973195

3198-
/// Whether the candidate matches the async alternative of the given
3199-
/// requirement.
3200-
bool matchesAsyncAlternative(ValueDecl *req, ValueDecl *cand) const {
3201-
auto reqFunc = dyn_cast<AbstractFunctionDecl>(req);
3202-
if (!reqFunc)
3203-
return false;
3204-
3205-
auto candFunc = dyn_cast<AbstractFunctionDecl>(cand);
3206-
if (!candFunc)
3207-
return false;
3208-
3209-
if (reqFunc->hasAsync() == candFunc->hasAsync())
3210-
return false;
3211-
3212-
auto otherReqFuncs =
3213-
objcMethodRequirements.find(getObjCMethodKey(reqFunc));
3214-
if (otherReqFuncs == objcMethodRequirements.end())
3215-
return false;
3216-
3217-
for (auto otherReqFunc : otherReqFuncs->second) {
3218-
if (otherReqFunc->getName() == cand->getName() &&
3219-
otherReqFunc->hasAsync() == candFunc->hasAsync() &&
3220-
req->getObjCRuntimeName() == cand->getObjCRuntimeName())
3221-
return true;
3222-
}
3223-
3224-
return false;
3225-
}
3226-
32273196
static bool areSwiftNamesEqual(DeclName lhs, DeclName rhs) {
32283197
// Conflate `foo()` and `foo`. This allows us to diagnose
32293198
// method-vs.-property mistakes more nicely.
@@ -3237,8 +3206,8 @@ class ObjCImplementationChecker {
32373206
return lhs == rhs;
32383207
}
32393208

3240-
MatchOutcome matches(ValueDecl *req, ValueDecl *cand,
3241-
ObjCSelector explicitObjCName) const {
3209+
MatchOutcome matchesImpl(ValueDecl *req, ValueDecl *cand,
3210+
ObjCSelector explicitObjCName) const {
32423211
bool hasObjCNameMatch =
32433212
req->getObjCRuntimeName() == cand->getObjCRuntimeName();
32443213
bool hasSwiftNameMatch = areSwiftNamesEqual(req->getName(), cand->getName());
@@ -3254,10 +3223,7 @@ class ObjCImplementationChecker {
32543223
&& req->getObjCRuntimeName() != explicitObjCName)
32553224
return MatchOutcome::WrongExplicitObjCName;
32563225

3257-
// If the ObjC selectors matched but the Swift names do not, and these are
3258-
// functions with mismatched 'async', check whether the "other" requirement
3259-
// (the completion-handler or async version)'s Swift name matches.
3260-
if (!hasSwiftNameMatch && !matchesAsyncAlternative(req, cand))
3226+
if (!hasSwiftNameMatch)
32613227
return MatchOutcome::WrongSwiftName;
32623228

32633229
if (!hasObjCNameMatch)
@@ -3287,6 +3253,17 @@ class ObjCImplementationChecker {
32873253
return MatchOutcome::Match;
32883254
}
32893255

3256+
MatchOutcome matches(ValueDecl *req, ValueDecl *cand,
3257+
ObjCSelector explicitObjCName) const {
3258+
// If the candidate we're considering is async, see if the requirement has
3259+
// an async alternate and try to match against that instead.
3260+
if (hasAsync(cand))
3261+
if (auto asyncAltReq = getAsyncAlternative(req))
3262+
return matchesImpl(asyncAltReq, cand, explicitObjCName);
3263+
3264+
return matchesImpl(req, cand, explicitObjCName);
3265+
}
3266+
32903267
void diagnoseOutcome(MatchOutcome outcome, ValueDecl *req, ValueDecl *cand,
32913268
ObjCSelector explicitObjCName) {
32923269
auto reqObjCName = *req->getObjCRuntimeName();

0 commit comments

Comments
 (0)