@@ -2885,15 +2885,6 @@ class ObjCImplementationChecker {
2885
2885
// / Candidates with their explicit ObjC names, if any.
2886
2886
llvm::SmallDenseMap<ValueDecl *, ObjCSelector, 16 > unmatchedCandidates;
2887
2887
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
-
2897
2888
public:
2898
2889
ObjCImplementationChecker (ExtensionDecl *ext)
2899
2890
: diags(ext->getASTContext ().Diags)
@@ -2915,8 +2906,30 @@ class ObjCImplementationChecker {
2915
2906
}
2916
2907
2917
2908
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 ;
2920
2933
}
2921
2934
2922
2935
void addRequirements (IterableDeclContext *idc) {
@@ -2932,12 +2945,13 @@ class ObjCImplementationChecker {
2932
2945
if (member->getAttrs ().isUnavailable (member->getASTContext ()))
2933
2946
continue ;
2934
2947
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
+
2935
2953
auto inserted = unmatchedRequirements.insert (member);
2936
2954
assert (inserted && " objc interface member added twice?" );
2937
-
2938
- if (auto func = dyn_cast<AbstractFunctionDecl>(member)) {
2939
- objcMethodRequirements[getObjCMethodKey (func)].push_back (func);
2940
- }
2941
2955
}
2942
2956
}
2943
2957
@@ -2998,6 +3012,9 @@ class ObjCImplementationChecker {
2998
3012
WrongImplicitObjCName,
2999
3013
WrongStaticness,
3000
3014
WrongCategory,
3015
+ WrongDeclKind,
3016
+ WrongType,
3017
+ WrongWritability,
3001
3018
3002
3019
Match,
3003
3020
MatchWithExplicitObjCName,
@@ -3031,19 +3048,6 @@ class ObjCImplementationChecker {
3031
3048
}
3032
3049
};
3033
3050
3034
- // / Determine whether the set of matched requirements are ambiguous for the
3035
- // / given candidate.
3036
- bool areRequirementsAmbiguous (const BestMatchList &reqs, ValueDecl *cand) {
3037
- if (reqs.matches .size () != 2 )
3038
- return reqs.matches .size () > 2 ;
3039
-
3040
- bool firstIsAsyncAlternative =
3041
- matchesAsyncAlternative (reqs.matches [0 ], cand);
3042
- bool secondIsAsyncAlternative =
3043
- matchesAsyncAlternative (reqs.matches [1 ], cand);
3044
- return firstIsAsyncAlternative == secondIsAsyncAlternative;
3045
- }
3046
-
3047
3051
void matchRequirementsAtThreshold (MatchOutcome threshold) {
3048
3052
SmallString<32 > scratch;
3049
3053
@@ -3103,14 +3107,11 @@ class ObjCImplementationChecker {
3103
3107
// removing them.
3104
3108
requirementsToRemove.set_union (matchedRequirements.matches );
3105
3109
3106
- if (! areRequirementsAmbiguous ( matchedRequirements, cand) ) {
3110
+ if (matchedRequirements. matches . size () == 1 ) {
3107
3111
// Note that this is BestMatchList::insert(), so it'll only keep the
3108
3112
// matches with the best outcomes.
3109
- for (auto req : matchedRequirements.matches ) {
3110
- matchesByRequirement[req]
3111
- .insert (cand, matchedRequirements.currentOutcome );
3112
- }
3113
-
3113
+ matchesByRequirement[matchedRequirements.matches .front ()]
3114
+ .insert (cand, matchedRequirements.currentOutcome );
3114
3115
continue ;
3115
3116
}
3116
3117
@@ -3192,40 +3193,89 @@ class ObjCImplementationChecker {
3192
3193
unmatchedCandidates.erase (cand);
3193
3194
}
3194
3195
3195
- // / Whether the candidate matches the async alternative of the given
3196
- // / requirement.
3197
- bool matchesAsyncAlternative (ValueDecl *req, ValueDecl *cand) const {
3198
- auto reqFunc = dyn_cast<AbstractFunctionDecl>(req);
3199
- if (!reqFunc)
3200
- return false ;
3196
+ static bool areSwiftNamesEqual (DeclName lhs, DeclName rhs) {
3197
+ // Conflate `foo()` and `foo`. This allows us to diagnose
3198
+ // method-vs.-property mistakes more nicely.
3201
3199
3202
- auto candFunc = dyn_cast<AbstractFunctionDecl>(cand);
3203
- if (!candFunc)
3204
- return false ;
3200
+ if (lhs.isCompoundName () && lhs.getArgumentNames ().empty ())
3201
+ lhs = lhs.getBaseName ();
3205
3202
3206
- if (reqFunc-> hasAsync () == candFunc-> hasAsync ())
3207
- return false ;
3203
+ if (rhs. isCompoundName () && rhs. getArgumentNames (). empty ())
3204
+ rhs = rhs. getBaseName () ;
3208
3205
3209
- auto otherReqFuncs =
3210
- objcMethodRequirements.find (getObjCMethodKey (reqFunc));
3211
- if (otherReqFuncs == objcMethodRequirements.end ())
3212
- return false ;
3206
+ return lhs == rhs;
3207
+ }
3213
3208
3214
- for (auto otherReqFunc : otherReqFuncs->second ) {
3215
- if (otherReqFunc->getName () == cand->getName () &&
3216
- otherReqFunc->hasAsync () == candFunc->hasAsync () &&
3217
- req->getObjCRuntimeName () == cand->getObjCRuntimeName ())
3218
- return true ;
3219
- }
3209
+ static bool matchParamTypes (Type reqTy, Type implTy, ValueDecl *implDecl) {
3210
+ TypeMatchOptions matchOpts = {};
3211
+
3212
+ // Try a plain type match.
3213
+ if (implTy->matchesParameter (reqTy, matchOpts))
3214
+ return true ;
3215
+
3216
+ // If the implementation type is IUO, try unwrapping it.
3217
+ if (auto unwrappedImplTy = implTy->getOptionalObjectType ())
3218
+ return implDecl->isImplicitlyUnwrappedOptional ()
3219
+ && unwrappedImplTy->matchesParameter (reqTy, matchOpts);
3220
3220
3221
3221
return false ;
3222
3222
}
3223
3223
3224
- MatchOutcome matches (ValueDecl *req, ValueDecl *cand,
3225
- ObjCSelector explicitObjCName) const {
3224
+ static bool matchTypes (Type reqTy, Type implTy, ValueDecl *implDecl) {
3225
+ TypeMatchOptions matchOpts = {};
3226
+
3227
+ // Try a plain type match.
3228
+ if (reqTy->matches (implTy, matchOpts))
3229
+ return true ;
3230
+
3231
+ // If the implementation type is optional, try unwrapping it.
3232
+ if (auto unwrappedImplTy = implTy->getOptionalObjectType ())
3233
+ return implDecl->isImplicitlyUnwrappedOptional ()
3234
+ && reqTy->matches (unwrappedImplTy, matchOpts);
3235
+
3236
+ // Apply these rules to the result type and parameters if it's a function
3237
+ // type.
3238
+ if (auto funcReqTy = reqTy->getAs <AnyFunctionType>())
3239
+ if (auto funcImplTy = implTy->getAs <AnyFunctionType>())
3240
+ return funcReqTy->matchesFunctionType (funcImplTy, matchOpts,
3241
+ [=]() -> bool {
3242
+ auto reqParams = funcReqTy->getParams ();
3243
+ auto implParams = funcImplTy->getParams ();
3244
+ if (reqParams.size () != implParams.size ())
3245
+ return false ;
3246
+
3247
+ auto implParamList =
3248
+ cast<AbstractFunctionDecl>(implDecl)->getParameters ();
3249
+
3250
+ for (auto i : indices (reqParams)) {
3251
+ const auto &reqParam = reqParams[i];
3252
+ const auto &implParam = implParams[i];
3253
+ ParamDecl *implParamDecl = implParamList->get (i);
3254
+
3255
+ if (!matchParamTypes (reqParam.getOldType (), implParam.getOldType (),
3256
+ implParamDecl))
3257
+ return false ;
3258
+ }
3259
+
3260
+ return matchTypes (funcReqTy->getResult (), funcImplTy->getResult (),
3261
+ implDecl);
3262
+ });
3263
+
3264
+ return false ;
3265
+ }
3266
+
3267
+ static Type getMemberType (ValueDecl *decl) {
3268
+ if (isa<AbstractFunctionDecl>(decl))
3269
+ // Strip off the uncurried `self` parameter.
3270
+ return decl->getInterfaceType ()->getAs <AnyFunctionType>()->getResult ();
3271
+ return decl->getInterfaceType ();
3272
+ }
3273
+
3274
+ MatchOutcome matchesImpl (ValueDecl *req, ValueDecl *cand,
3275
+ ObjCSelector explicitObjCName) const {
3226
3276
bool hasObjCNameMatch =
3227
3277
req->getObjCRuntimeName () == cand->getObjCRuntimeName ();
3228
- bool hasSwiftNameMatch = req->getName () == cand->getName ();
3278
+ bool hasSwiftNameMatch = areSwiftNamesEqual ( req->getName (), cand->getName () );
3229
3279
3230
3280
// If neither the ObjC nor Swift names match, there's absolutely no reason
3231
3281
// to think these two methods are related.
@@ -3238,10 +3288,7 @@ class ObjCImplementationChecker {
3238
3288
&& req->getObjCRuntimeName () != explicitObjCName)
3239
3289
return MatchOutcome::WrongExplicitObjCName;
3240
3290
3241
- // If the ObjC selectors matched but the Swift names do not, and these are
3242
- // functions with mismatched 'async', check whether the "other" requirement
3243
- // (the completion-handler or async version)'s Swift name matches.
3244
- if (!hasSwiftNameMatch && !matchesAsyncAlternative (req, cand))
3291
+ if (!hasSwiftNameMatch)
3245
3292
return MatchOutcome::WrongSwiftName;
3246
3293
3247
3294
if (!hasObjCNameMatch)
@@ -3254,9 +3301,16 @@ class ObjCImplementationChecker {
3254
3301
!= req->getDeclContext ())
3255
3302
return MatchOutcome::WrongCategory;
3256
3303
3257
- // FIXME: Diagnose candidate without a required setter
3258
- // FIXME: Diagnose declaration kind mismatches
3259
- // FIXME: Diagnose type mismatches (with allowance for extra optionality)
3304
+ if (cand->getKind () != req->getKind ())
3305
+ return MatchOutcome::WrongDeclKind;
3306
+
3307
+ if (!matchTypes (getMemberType (req), getMemberType (cand), cand))
3308
+ return MatchOutcome::WrongType;
3309
+
3310
+ if (auto reqVar = dyn_cast<AbstractStorageDecl>(req))
3311
+ if (reqVar->isSettable (nullptr ) &&
3312
+ !cast<AbstractStorageDecl>(cand)->isSettable (nullptr ))
3313
+ return MatchOutcome::WrongWritability;
3260
3314
3261
3315
// If we got here, everything matched. But at what quality?
3262
3316
if (explicitObjCName)
@@ -3265,6 +3319,17 @@ class ObjCImplementationChecker {
3265
3319
return MatchOutcome::Match;
3266
3320
}
3267
3321
3322
+ MatchOutcome matches (ValueDecl *req, ValueDecl *cand,
3323
+ ObjCSelector explicitObjCName) const {
3324
+ // If the candidate we're considering is async, see if the requirement has
3325
+ // an async alternate and try to match against that instead.
3326
+ if (hasAsync (cand))
3327
+ if (auto asyncAltReq = getAsyncAlternative (req))
3328
+ return matchesImpl (asyncAltReq, cand, explicitObjCName);
3329
+
3330
+ return matchesImpl (req, cand, explicitObjCName);
3331
+ }
3332
+
3268
3333
void diagnoseOutcome (MatchOutcome outcome, ValueDecl *req, ValueDecl *cand,
3269
3334
ObjCSelector explicitObjCName) {
3270
3335
auto reqObjCName = *req->getObjCRuntimeName ();
@@ -3318,6 +3383,22 @@ class ObjCImplementationChecker {
3318
3383
getCategoryName (cand->getDeclContext ()->
3319
3384
getImplementedObjCContext ()));
3320
3385
return ;
3386
+
3387
+ case MatchOutcome::WrongDeclKind:
3388
+ diagnose (cand, diag::objc_implementation_wrong_decl_kind,
3389
+ cand->getDescriptiveKind (), cand, req->getDescriptiveKind ());
3390
+ return ;
3391
+
3392
+ case MatchOutcome::WrongType:
3393
+ diagnose (cand, diag::objc_implementation_type_mismatch,
3394
+ cand->getDescriptiveKind (), cand,
3395
+ getMemberType (cand), getMemberType (req));
3396
+ return ;
3397
+
3398
+ case MatchOutcome::WrongWritability:
3399
+ diagnose (cand, diag::objc_implementation_must_be_settable,
3400
+ cand->getDescriptiveKind (), cand, req->getDescriptiveKind ());
3401
+ return ;
3321
3402
}
3322
3403
3323
3404
llvm_unreachable (" Unknown MatchOutcome" );
0 commit comments