@@ -2900,15 +2900,6 @@ class ObjCImplementationChecker {
2900
2900
// / Candidates with their explicit ObjC names, if any.
2901
2901
llvm::SmallDenseMap<ValueDecl *, ObjCSelector, 16 > unmatchedCandidates;
2902
2902
2903
- // / Key that can be used to uniquely identify a particular Objective-C
2904
- // / method.
2905
- using ObjCMethodKey = std::pair<ObjCSelector, char >;
2906
-
2907
- // / Mapping from Objective-C methods to the set of requirements within this
2908
- // / protocol that have the same selector and instance/class designation.
2909
- llvm::SmallDenseMap<ObjCMethodKey, TinyPtrVector<AbstractFunctionDecl *>, 4 >
2910
- objcMethodRequirements;
2911
-
2912
2903
public:
2913
2904
ObjCImplementationChecker (ExtensionDecl *ext)
2914
2905
: diags(ext->getASTContext ().Diags)
@@ -2930,8 +2921,30 @@ class ObjCImplementationChecker {
2930
2921
}
2931
2922
2932
2923
private:
2933
- auto getObjCMethodKey (AbstractFunctionDecl *func) const -> ObjCMethodKey {
2934
- return ObjCMethodKey (func->getObjCSelector (), func->isInstanceMember ());
2924
+ static bool hasAsync (ValueDecl *member) {
2925
+ if (!member)
2926
+ return false ;
2927
+
2928
+ if (auto func = dyn_cast<AbstractFunctionDecl>(member))
2929
+ return func->hasAsync ();
2930
+
2931
+ if (auto storage = dyn_cast<AbstractStorageDecl>(member))
2932
+ return hasAsync (storage->getEffectfulGetAccessor ());
2933
+
2934
+ return false ;
2935
+ }
2936
+
2937
+ static ValueDecl *getAsyncAlternative (ValueDecl *req) {
2938
+ if (auto func = dyn_cast<AbstractFunctionDecl>(req)) {
2939
+ auto asyncFunc = func->getAsyncAlternative ();
2940
+
2941
+ if (auto asyncAccessor = dyn_cast<AccessorDecl>(asyncFunc))
2942
+ return asyncAccessor->getStorage ();
2943
+
2944
+ return asyncFunc;
2945
+ }
2946
+
2947
+ return nullptr ;
2935
2948
}
2936
2949
2937
2950
void addRequirements (IterableDeclContext *idc) {
@@ -2947,12 +2960,13 @@ class ObjCImplementationChecker {
2947
2960
if (member->getAttrs ().isUnavailable (member->getASTContext ()))
2948
2961
continue ;
2949
2962
2963
+ // Skip async versions of members. We'll match against the completion
2964
+ // handler versions, hopping over to `getAsyncAlternative()` if needed.
2965
+ if (hasAsync (member))
2966
+ continue ;
2967
+
2950
2968
auto inserted = unmatchedRequirements.insert (member);
2951
2969
assert (inserted && " objc interface member added twice?" );
2952
-
2953
- if (auto func = dyn_cast<AbstractFunctionDecl>(member)) {
2954
- objcMethodRequirements[getObjCMethodKey (func)].push_back (func);
2955
- }
2956
2970
}
2957
2971
}
2958
2972
@@ -3013,6 +3027,9 @@ class ObjCImplementationChecker {
3013
3027
WrongImplicitObjCName,
3014
3028
WrongStaticness,
3015
3029
WrongCategory,
3030
+ WrongDeclKind,
3031
+ WrongType,
3032
+ WrongWritability,
3016
3033
3017
3034
Match,
3018
3035
MatchWithExplicitObjCName,
@@ -3046,19 +3063,6 @@ class ObjCImplementationChecker {
3046
3063
}
3047
3064
};
3048
3065
3049
- // / Determine whether the set of matched requirements are ambiguous for the
3050
- // / given candidate.
3051
- bool areRequirementsAmbiguous (const BestMatchList &reqs, ValueDecl *cand) {
3052
- if (reqs.matches .size () != 2 )
3053
- return reqs.matches .size () > 2 ;
3054
-
3055
- bool firstIsAsyncAlternative =
3056
- matchesAsyncAlternative (reqs.matches [0 ], cand);
3057
- bool secondIsAsyncAlternative =
3058
- matchesAsyncAlternative (reqs.matches [1 ], cand);
3059
- return firstIsAsyncAlternative == secondIsAsyncAlternative;
3060
- }
3061
-
3062
3066
void matchRequirementsAtThreshold (MatchOutcome threshold) {
3063
3067
SmallString<32 > scratch;
3064
3068
@@ -3118,14 +3122,11 @@ class ObjCImplementationChecker {
3118
3122
// removing them.
3119
3123
requirementsToRemove.set_union (matchedRequirements.matches );
3120
3124
3121
- if (! areRequirementsAmbiguous ( matchedRequirements, cand) ) {
3125
+ if (matchedRequirements. matches . size () == 1 ) {
3122
3126
// Note that this is BestMatchList::insert(), so it'll only keep the
3123
3127
// matches with the best outcomes.
3124
- for (auto req : matchedRequirements.matches ) {
3125
- matchesByRequirement[req]
3126
- .insert (cand, matchedRequirements.currentOutcome );
3127
- }
3128
-
3128
+ matchesByRequirement[matchedRequirements.matches .front ()]
3129
+ .insert (cand, matchedRequirements.currentOutcome );
3129
3130
continue ;
3130
3131
}
3131
3132
@@ -3207,40 +3208,89 @@ class ObjCImplementationChecker {
3207
3208
unmatchedCandidates.erase (cand);
3208
3209
}
3209
3210
3210
- // / Whether the candidate matches the async alternative of the given
3211
- // / requirement.
3212
- bool matchesAsyncAlternative (ValueDecl *req, ValueDecl *cand) const {
3213
- auto reqFunc = dyn_cast<AbstractFunctionDecl>(req);
3214
- if (!reqFunc)
3215
- return false ;
3211
+ static bool areSwiftNamesEqual (DeclName lhs, DeclName rhs) {
3212
+ // Conflate `foo()` and `foo`. This allows us to diagnose
3213
+ // method-vs.-property mistakes more nicely.
3216
3214
3217
- auto candFunc = dyn_cast<AbstractFunctionDecl>(cand);
3218
- if (!candFunc)
3219
- return false ;
3215
+ if (lhs.isCompoundName () && lhs.getArgumentNames ().empty ())
3216
+ lhs = lhs.getBaseName ();
3220
3217
3221
- if (reqFunc-> hasAsync () == candFunc-> hasAsync ())
3222
- return false ;
3218
+ if (rhs. isCompoundName () && rhs. getArgumentNames (). empty ())
3219
+ rhs = rhs. getBaseName () ;
3223
3220
3224
- auto otherReqFuncs =
3225
- objcMethodRequirements.find (getObjCMethodKey (reqFunc));
3226
- if (otherReqFuncs == objcMethodRequirements.end ())
3227
- return false ;
3221
+ return lhs == rhs;
3222
+ }
3228
3223
3229
- for (auto otherReqFunc : otherReqFuncs->second ) {
3230
- if (otherReqFunc->getName () == cand->getName () &&
3231
- otherReqFunc->hasAsync () == candFunc->hasAsync () &&
3232
- req->getObjCRuntimeName () == cand->getObjCRuntimeName ())
3233
- return true ;
3234
- }
3224
+ static bool matchParamTypes (Type reqTy, Type implTy, ValueDecl *implDecl) {
3225
+ TypeMatchOptions matchOpts = {};
3226
+
3227
+ // Try a plain type match.
3228
+ if (implTy->matchesParameter (reqTy, matchOpts))
3229
+ return true ;
3230
+
3231
+ // If the implementation type is IUO, try unwrapping it.
3232
+ if (auto unwrappedImplTy = implTy->getOptionalObjectType ())
3233
+ return implDecl->isImplicitlyUnwrappedOptional ()
3234
+ && unwrappedImplTy->matchesParameter (reqTy, matchOpts);
3235
3235
3236
3236
return false ;
3237
3237
}
3238
3238
3239
- MatchOutcome matches (ValueDecl *req, ValueDecl *cand,
3240
- ObjCSelector explicitObjCName) const {
3239
+ static bool matchTypes (Type reqTy, Type implTy, ValueDecl *implDecl) {
3240
+ TypeMatchOptions matchOpts = {};
3241
+
3242
+ // Try a plain type match.
3243
+ if (reqTy->matches (implTy, matchOpts))
3244
+ return true ;
3245
+
3246
+ // If the implementation type is optional, try unwrapping it.
3247
+ if (auto unwrappedImplTy = implTy->getOptionalObjectType ())
3248
+ return implDecl->isImplicitlyUnwrappedOptional ()
3249
+ && reqTy->matches (unwrappedImplTy, matchOpts);
3250
+
3251
+ // Apply these rules to the result type and parameters if it's a function
3252
+ // type.
3253
+ if (auto funcReqTy = reqTy->getAs <AnyFunctionType>())
3254
+ if (auto funcImplTy = implTy->getAs <AnyFunctionType>())
3255
+ return funcReqTy->matchesFunctionType (funcImplTy, matchOpts,
3256
+ [=]() -> bool {
3257
+ auto reqParams = funcReqTy->getParams ();
3258
+ auto implParams = funcImplTy->getParams ();
3259
+ if (reqParams.size () != implParams.size ())
3260
+ return false ;
3261
+
3262
+ auto implParamList =
3263
+ cast<AbstractFunctionDecl>(implDecl)->getParameters ();
3264
+
3265
+ for (auto i : indices (reqParams)) {
3266
+ const auto &reqParam = reqParams[i];
3267
+ const auto &implParam = implParams[i];
3268
+ ParamDecl *implParamDecl = implParamList->get (i);
3269
+
3270
+ if (!matchParamTypes (reqParam.getOldType (), implParam.getOldType (),
3271
+ implParamDecl))
3272
+ return false ;
3273
+ }
3274
+
3275
+ return matchTypes (funcReqTy->getResult (), funcImplTy->getResult (),
3276
+ implDecl);
3277
+ });
3278
+
3279
+ return false ;
3280
+ }
3281
+
3282
+ static Type getMemberType (ValueDecl *decl) {
3283
+ if (isa<AbstractFunctionDecl>(decl))
3284
+ // Strip off the uncurried `self` parameter.
3285
+ return decl->getInterfaceType ()->getAs <AnyFunctionType>()->getResult ();
3286
+ return decl->getInterfaceType ();
3287
+ }
3288
+
3289
+ MatchOutcome matchesImpl (ValueDecl *req, ValueDecl *cand,
3290
+ ObjCSelector explicitObjCName) const {
3241
3291
bool hasObjCNameMatch =
3242
3292
req->getObjCRuntimeName () == cand->getObjCRuntimeName ();
3243
- bool hasSwiftNameMatch = req->getName () == cand->getName ();
3293
+ bool hasSwiftNameMatch = areSwiftNamesEqual ( req->getName (), cand->getName () );
3244
3294
3245
3295
// If neither the ObjC nor Swift names match, there's absolutely no reason
3246
3296
// to think these two methods are related.
@@ -3253,10 +3303,7 @@ class ObjCImplementationChecker {
3253
3303
&& req->getObjCRuntimeName () != explicitObjCName)
3254
3304
return MatchOutcome::WrongExplicitObjCName;
3255
3305
3256
- // If the ObjC selectors matched but the Swift names do not, and these are
3257
- // functions with mismatched 'async', check whether the "other" requirement
3258
- // (the completion-handler or async version)'s Swift name matches.
3259
- if (!hasSwiftNameMatch && !matchesAsyncAlternative (req, cand))
3306
+ if (!hasSwiftNameMatch)
3260
3307
return MatchOutcome::WrongSwiftName;
3261
3308
3262
3309
if (!hasObjCNameMatch)
@@ -3269,9 +3316,16 @@ class ObjCImplementationChecker {
3269
3316
!= req->getDeclContext ())
3270
3317
return MatchOutcome::WrongCategory;
3271
3318
3272
- // FIXME: Diagnose candidate without a required setter
3273
- // FIXME: Diagnose declaration kind mismatches
3274
- // FIXME: Diagnose type mismatches (with allowance for extra optionality)
3319
+ if (cand->getKind () != req->getKind ())
3320
+ return MatchOutcome::WrongDeclKind;
3321
+
3322
+ if (!matchTypes (getMemberType (req), getMemberType (cand), cand))
3323
+ return MatchOutcome::WrongType;
3324
+
3325
+ if (auto reqVar = dyn_cast<AbstractStorageDecl>(req))
3326
+ if (reqVar->isSettable (nullptr ) &&
3327
+ !cast<AbstractStorageDecl>(cand)->isSettable (nullptr ))
3328
+ return MatchOutcome::WrongWritability;
3275
3329
3276
3330
// If we got here, everything matched. But at what quality?
3277
3331
if (explicitObjCName)
@@ -3280,6 +3334,17 @@ class ObjCImplementationChecker {
3280
3334
return MatchOutcome::Match;
3281
3335
}
3282
3336
3337
+ MatchOutcome matches (ValueDecl *req, ValueDecl *cand,
3338
+ ObjCSelector explicitObjCName) const {
3339
+ // If the candidate we're considering is async, see if the requirement has
3340
+ // an async alternate and try to match against that instead.
3341
+ if (hasAsync (cand))
3342
+ if (auto asyncAltReq = getAsyncAlternative (req))
3343
+ return matchesImpl (asyncAltReq, cand, explicitObjCName);
3344
+
3345
+ return matchesImpl (req, cand, explicitObjCName);
3346
+ }
3347
+
3283
3348
void diagnoseOutcome (MatchOutcome outcome, ValueDecl *req, ValueDecl *cand,
3284
3349
ObjCSelector explicitObjCName) {
3285
3350
auto reqObjCName = *req->getObjCRuntimeName ();
@@ -3333,6 +3398,22 @@ class ObjCImplementationChecker {
3333
3398
getCategoryName (cand->getDeclContext ()->
3334
3399
getImplementedObjCContext ()));
3335
3400
return ;
3401
+
3402
+ case MatchOutcome::WrongDeclKind:
3403
+ diagnose (cand, diag::objc_implementation_wrong_decl_kind,
3404
+ cand->getDescriptiveKind (), cand, req->getDescriptiveKind ());
3405
+ return ;
3406
+
3407
+ case MatchOutcome::WrongType:
3408
+ diagnose (cand, diag::objc_implementation_type_mismatch,
3409
+ cand->getDescriptiveKind (), cand,
3410
+ getMemberType (cand), getMemberType (req));
3411
+ return ;
3412
+
3413
+ case MatchOutcome::WrongWritability:
3414
+ diagnose (cand, diag::objc_implementation_must_be_settable,
3415
+ cand->getDescriptiveKind (), cand, req->getDescriptiveKind ());
3416
+ return ;
3336
3417
}
3337
3418
3338
3419
llvm_unreachable (" Unknown MatchOutcome" );
0 commit comments