Skip to content

Commit 1f297fd

Browse files
committed
[NFC] Change how @objcImpl async matching is done
#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 813f965 commit 1f297fd

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
@@ -2892,15 +2892,6 @@ class ObjCImplementationChecker {
28922892
/// Candidates with their explicit ObjC names, if any.
28932893
llvm::SmallDenseMap<ValueDecl *, ObjCSelector, 16> unmatchedCandidates;
28942894

2895-
/// Key that can be used to uniquely identify a particular Objective-C
2896-
/// method.
2897-
using ObjCMethodKey = std::pair<ObjCSelector, char>;
2898-
2899-
/// Mapping from Objective-C methods to the set of requirements within this
2900-
/// protocol that have the same selector and instance/class designation.
2901-
llvm::SmallDenseMap<ObjCMethodKey, TinyPtrVector<AbstractFunctionDecl *>, 4>
2902-
objcMethodRequirements;
2903-
29042895
public:
29052896
ObjCImplementationChecker(ExtensionDecl *ext)
29062897
: diags(ext->getASTContext().Diags)
@@ -2922,8 +2913,30 @@ class ObjCImplementationChecker {
29222913
}
29232914

29242915
private:
2925-
auto getObjCMethodKey(AbstractFunctionDecl *func) const -> ObjCMethodKey {
2926-
return ObjCMethodKey(func->getObjCSelector(), func->isInstanceMember());
2916+
static bool hasAsync(ValueDecl *member) {
2917+
if (!member)
2918+
return false;
2919+
2920+
if (auto func = dyn_cast<AbstractFunctionDecl>(member))
2921+
return func->hasAsync();
2922+
2923+
if (auto storage = dyn_cast<AbstractStorageDecl>(member))
2924+
return hasAsync(storage->getEffectfulGetAccessor());
2925+
2926+
return false;
2927+
}
2928+
2929+
static ValueDecl *getAsyncAlternative(ValueDecl *req) {
2930+
if (auto func = dyn_cast<AbstractFunctionDecl>(req)) {
2931+
auto asyncFunc = func->getAsyncAlternative();
2932+
2933+
if (auto asyncAccessor = dyn_cast<AccessorDecl>(asyncFunc))
2934+
return asyncAccessor->getStorage();
2935+
2936+
return asyncFunc;
2937+
}
2938+
2939+
return nullptr;
29272940
}
29282941

29292942
void addRequirements(IterableDeclContext *idc) {
@@ -2939,12 +2952,13 @@ class ObjCImplementationChecker {
29392952
if (member->getAttrs().isUnavailable(member->getASTContext()))
29402953
continue;
29412954

2955+
// Skip async versions of members. We'll match against the completion
2956+
// handler versions, hopping over to `getAsyncAlternative()` if needed.
2957+
if (hasAsync(member))
2958+
continue;
2959+
29422960
auto inserted = unmatchedRequirements.insert(member);
29432961
assert(inserted && "objc interface member added twice?");
2944-
2945-
if (auto func = dyn_cast<AbstractFunctionDecl>(member)) {
2946-
objcMethodRequirements[getObjCMethodKey(func)].push_back(func);
2947-
}
29482962
}
29492963
}
29502964

@@ -3041,19 +3055,6 @@ class ObjCImplementationChecker {
30413055
}
30423056
};
30433057

3044-
/// Determine whether the set of matched requirements are ambiguous for the
3045-
/// given candidate.
3046-
bool areRequirementsAmbiguous(const BestMatchList &reqs, ValueDecl *cand) {
3047-
if (reqs.matches.size() != 2)
3048-
return reqs.matches.size() > 2;
3049-
3050-
bool firstIsAsyncAlternative =
3051-
matchesAsyncAlternative(reqs.matches[0], cand);
3052-
bool secondIsAsyncAlternative =
3053-
matchesAsyncAlternative(reqs.matches[1], cand);
3054-
return firstIsAsyncAlternative == secondIsAsyncAlternative;
3055-
}
3056-
30573058
void matchRequirementsAtThreshold(MatchOutcome threshold) {
30583059
SmallString<32> scratch;
30593060

@@ -3113,14 +3114,11 @@ class ObjCImplementationChecker {
31133114
// removing them.
31143115
requirementsToRemove.set_union(matchedRequirements.matches);
31153116

3116-
if (!areRequirementsAmbiguous(matchedRequirements, cand)) {
3117+
if (matchedRequirements.matches.size() == 1) {
31173118
// Note that this is BestMatchList::insert(), so it'll only keep the
31183119
// matches with the best outcomes.
3119-
for (auto req : matchedRequirements.matches) {
3120-
matchesByRequirement[req]
3121-
.insert(cand, matchedRequirements.currentOutcome);
3122-
}
3123-
3120+
matchesByRequirement[matchedRequirements.matches.front()]
3121+
.insert(cand, matchedRequirements.currentOutcome);
31243122
continue;
31253123
}
31263124

@@ -3202,35 +3200,6 @@ class ObjCImplementationChecker {
32023200
unmatchedCandidates.erase(cand);
32033201
}
32043202

3205-
/// Whether the candidate matches the async alternative of the given
3206-
/// requirement.
3207-
bool matchesAsyncAlternative(ValueDecl *req, ValueDecl *cand) const {
3208-
auto reqFunc = dyn_cast<AbstractFunctionDecl>(req);
3209-
if (!reqFunc)
3210-
return false;
3211-
3212-
auto candFunc = dyn_cast<AbstractFunctionDecl>(cand);
3213-
if (!candFunc)
3214-
return false;
3215-
3216-
if (reqFunc->hasAsync() == candFunc->hasAsync())
3217-
return false;
3218-
3219-
auto otherReqFuncs =
3220-
objcMethodRequirements.find(getObjCMethodKey(reqFunc));
3221-
if (otherReqFuncs == objcMethodRequirements.end())
3222-
return false;
3223-
3224-
for (auto otherReqFunc : otherReqFuncs->second) {
3225-
if (otherReqFunc->getName() == cand->getName() &&
3226-
otherReqFunc->hasAsync() == candFunc->hasAsync() &&
3227-
req->getObjCRuntimeName() == cand->getObjCRuntimeName())
3228-
return true;
3229-
}
3230-
3231-
return false;
3232-
}
3233-
32343203
static bool areSwiftNamesEqual(DeclName lhs, DeclName rhs) {
32353204
// Conflate `foo()` and `foo`. This allows us to diagnose
32363205
// method-vs.-property mistakes more nicely.
@@ -3244,8 +3213,8 @@ class ObjCImplementationChecker {
32443213
return lhs == rhs;
32453214
}
32463215

3247-
MatchOutcome matches(ValueDecl *req, ValueDecl *cand,
3248-
ObjCSelector explicitObjCName) const {
3216+
MatchOutcome matchesImpl(ValueDecl *req, ValueDecl *cand,
3217+
ObjCSelector explicitObjCName) const {
32493218
bool hasObjCNameMatch =
32503219
req->getObjCRuntimeName() == cand->getObjCRuntimeName();
32513220
bool hasSwiftNameMatch = areSwiftNamesEqual(req->getName(), cand->getName());
@@ -3261,10 +3230,7 @@ class ObjCImplementationChecker {
32613230
&& req->getObjCRuntimeName() != explicitObjCName)
32623231
return MatchOutcome::WrongExplicitObjCName;
32633232

3264-
// If the ObjC selectors matched but the Swift names do not, and these are
3265-
// functions with mismatched 'async', check whether the "other" requirement
3266-
// (the completion-handler or async version)'s Swift name matches.
3267-
if (!hasSwiftNameMatch && !matchesAsyncAlternative(req, cand))
3233+
if (!hasSwiftNameMatch)
32683234
return MatchOutcome::WrongSwiftName;
32693235

32703236
if (!hasObjCNameMatch)
@@ -3294,6 +3260,17 @@ class ObjCImplementationChecker {
32943260
return MatchOutcome::Match;
32953261
}
32963262

3263+
MatchOutcome matches(ValueDecl *req, ValueDecl *cand,
3264+
ObjCSelector explicitObjCName) const {
3265+
// If the candidate we're considering is async, see if the requirement has
3266+
// an async alternate and try to match against that instead.
3267+
if (hasAsync(cand))
3268+
if (auto asyncAltReq = getAsyncAlternative(req))
3269+
return matchesImpl(asyncAltReq, cand, explicitObjCName);
3270+
3271+
return matchesImpl(req, cand, explicitObjCName);
3272+
}
3273+
32973274
void diagnoseOutcome(MatchOutcome outcome, ValueDecl *req, ValueDecl *cand,
32983275
ObjCSelector explicitObjCName) {
32993276
auto reqObjCName = *req->getObjCRuntimeName();

0 commit comments

Comments
 (0)