@@ -2882,6 +2882,15 @@ class ObjCImplementationChecker {
2882
2882
// / Candidates with their explicit ObjC names, if any.
2883
2883
llvm::SmallDenseMap<ValueDecl *, ObjCSelector, 16 > unmatchedCandidates;
2884
2884
2885
+ // / Key that can be used to uniquely identify a particular Objective-C
2886
+ // / method.
2887
+ using ObjCMethodKey = std::pair<ObjCSelector, char >;
2888
+
2889
+ // / Mapping from Objective-C methods to the set of requirements within this
2890
+ // / protocol that have the same selector and instance/class designation.
2891
+ llvm::SmallDenseMap<ObjCMethodKey, TinyPtrVector<AbstractFunctionDecl *>, 4 >
2892
+ objcMethodRequirements;
2893
+
2885
2894
public:
2886
2895
ObjCImplementationChecker (ExtensionDecl *ext)
2887
2896
: diags(ext->getASTContext ().Diags)
@@ -2903,6 +2912,10 @@ class ObjCImplementationChecker {
2903
2912
}
2904
2913
2905
2914
private:
2915
+ auto getObjCMethodKey (AbstractFunctionDecl *func) const -> ObjCMethodKey {
2916
+ return ObjCMethodKey (func->getObjCSelector (), func->isInstanceMember ());
2917
+ }
2918
+
2906
2919
void addRequirements (IterableDeclContext *idc) {
2907
2920
assert (idc->getDecl ()->hasClangNode ());
2908
2921
for (Decl *_member : idc->getMembers ()) {
@@ -2918,6 +2931,10 @@ class ObjCImplementationChecker {
2918
2931
2919
2932
auto inserted = unmatchedRequirements.insert (member);
2920
2933
assert (inserted && " objc interface member added twice?" );
2934
+
2935
+ if (auto func = dyn_cast<AbstractFunctionDecl>(member)) {
2936
+ objcMethodRequirements[getObjCMethodKey (func)].push_back (func);
2937
+ }
2921
2938
}
2922
2939
}
2923
2940
@@ -3011,6 +3028,19 @@ class ObjCImplementationChecker {
3011
3028
}
3012
3029
};
3013
3030
3031
+ // / Determine whether the set of matched requirements are ambiguous for the
3032
+ // / given candidate.
3033
+ bool areRequirementsAmbiguous (const BestMatchList &reqs, ValueDecl *cand) {
3034
+ if (reqs.matches .size () != 2 )
3035
+ return reqs.matches .size () > 2 ;
3036
+
3037
+ bool firstIsAsyncAlternative =
3038
+ matchesAsyncAlternative (reqs.matches [0 ], cand);
3039
+ bool secondIsAsyncAlternative =
3040
+ matchesAsyncAlternative (reqs.matches [1 ], cand);
3041
+ return firstIsAsyncAlternative == secondIsAsyncAlternative;
3042
+ }
3043
+
3014
3044
void matchRequirementsAtThreshold (MatchOutcome threshold) {
3015
3045
SmallString<32 > scratch;
3016
3046
@@ -3070,11 +3100,14 @@ class ObjCImplementationChecker {
3070
3100
// removing them.
3071
3101
requirementsToRemove.set_union (matchedRequirements.matches );
3072
3102
3073
- if (matchedRequirements. matches . size () == 1 ) {
3103
+ if (! areRequirementsAmbiguous (matchedRequirements, cand) ) {
3074
3104
// Note that this is BestMatchList::insert(), so it'll only keep the
3075
3105
// matches with the best outcomes.
3076
- matchesByRequirement[matchedRequirements.matches .front ()]
3077
- .insert (cand, matchedRequirements.currentOutcome );
3106
+ for (auto req : matchedRequirements.matches ) {
3107
+ matchesByRequirement[req]
3108
+ .insert (cand, matchedRequirements.currentOutcome );
3109
+ }
3110
+
3078
3111
continue ;
3079
3112
}
3080
3113
@@ -3156,6 +3189,35 @@ class ObjCImplementationChecker {
3156
3189
unmatchedCandidates.erase (cand);
3157
3190
}
3158
3191
3192
+ // / Whether the candidate matches the async alternative of the given
3193
+ // / requirement.
3194
+ bool matchesAsyncAlternative (ValueDecl *req, ValueDecl *cand) const {
3195
+ auto reqFunc = dyn_cast<AbstractFunctionDecl>(req);
3196
+ if (!reqFunc)
3197
+ return false ;
3198
+
3199
+ auto candFunc = dyn_cast<AbstractFunctionDecl>(cand);
3200
+ if (!candFunc)
3201
+ return false ;
3202
+
3203
+ if (reqFunc->hasAsync () == candFunc->hasAsync ())
3204
+ return false ;
3205
+
3206
+ auto otherReqFuncs =
3207
+ objcMethodRequirements.find (getObjCMethodKey (reqFunc));
3208
+ if (otherReqFuncs == objcMethodRequirements.end ())
3209
+ return false ;
3210
+
3211
+ for (auto otherReqFunc : otherReqFuncs->second ) {
3212
+ if (otherReqFunc->getName () == cand->getName () &&
3213
+ otherReqFunc->hasAsync () == candFunc->hasAsync () &&
3214
+ req->getObjCRuntimeName () == cand->getObjCRuntimeName ())
3215
+ return true ;
3216
+ }
3217
+
3218
+ return false ;
3219
+ }
3220
+
3159
3221
MatchOutcome matches (ValueDecl *req, ValueDecl *cand,
3160
3222
ObjCSelector explicitObjCName) const {
3161
3223
bool hasObjCNameMatch =
@@ -3173,7 +3235,10 @@ class ObjCImplementationChecker {
3173
3235
&& req->getObjCRuntimeName () != explicitObjCName)
3174
3236
return MatchOutcome::WrongExplicitObjCName;
3175
3237
3176
- if (!hasSwiftNameMatch)
3238
+ // If the ObjC selectors matched but the Swift names do not, and these are
3239
+ // functions with mismatched 'async', check whether the "other" requirement
3240
+ // (the completion-handler or async version)'s Swift name matches.
3241
+ if (!hasSwiftNameMatch && !matchesAsyncAlternative (req, cand))
3177
3242
return MatchOutcome::WrongSwiftName;
3178
3243
3179
3244
if (!hasObjCNameMatch)
0 commit comments