@@ -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-
28972888public:
28982889 ObjCImplementationChecker (ExtensionDecl *ext)
28992890 : diags(ext->getASTContext ().Diags)
@@ -2915,8 +2906,30 @@ class ObjCImplementationChecker {
29152906 }
29162907
29172908private:
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
@@ -2998,6 +3012,9 @@ class ObjCImplementationChecker {
29983012 WrongImplicitObjCName,
29993013 WrongStaticness,
30003014 WrongCategory,
3015+ WrongDeclKind,
3016+ WrongType,
3017+ WrongWritability,
30013018
30023019 Match,
30033020 MatchWithExplicitObjCName,
@@ -3031,19 +3048,6 @@ class ObjCImplementationChecker {
30313048 }
30323049 };
30333050
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-
30473051 void matchRequirementsAtThreshold (MatchOutcome threshold) {
30483052 SmallString<32 > scratch;
30493053
@@ -3103,14 +3107,11 @@ class ObjCImplementationChecker {
31033107 // removing them.
31043108 requirementsToRemove.set_union (matchedRequirements.matches );
31053109
3106- if (! areRequirementsAmbiguous ( matchedRequirements, cand) ) {
3110+ if (matchedRequirements. matches . size () == 1 ) {
31073111 // Note that this is BestMatchList::insert(), so it'll only keep the
31083112 // 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 );
31143115 continue ;
31153116 }
31163117
@@ -3192,40 +3193,89 @@ class ObjCImplementationChecker {
31923193 unmatchedCandidates.erase (cand);
31933194 }
31943195
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.
32013199
3202- auto candFunc = dyn_cast<AbstractFunctionDecl>(cand);
3203- if (!candFunc)
3204- return false ;
3200+ if (lhs.isCompoundName () && lhs.getArgumentNames ().empty ())
3201+ lhs = lhs.getBaseName ();
32053202
3206- if (reqFunc-> hasAsync () == candFunc-> hasAsync ())
3207- return false ;
3203+ if (rhs. isCompoundName () && rhs. getArgumentNames (). empty ())
3204+ rhs = rhs. getBaseName () ;
32083205
3209- auto otherReqFuncs =
3210- objcMethodRequirements.find (getObjCMethodKey (reqFunc));
3211- if (otherReqFuncs == objcMethodRequirements.end ())
3212- return false ;
3206+ return lhs == rhs;
3207+ }
32133208
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);
32203220
32213221 return false ;
32223222 }
32233223
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 {
32263276 bool hasObjCNameMatch =
32273277 req->getObjCRuntimeName () == cand->getObjCRuntimeName ();
3228- bool hasSwiftNameMatch = req->getName () == cand->getName ();
3278+ bool hasSwiftNameMatch = areSwiftNamesEqual ( req->getName (), cand->getName () );
32293279
32303280 // If neither the ObjC nor Swift names match, there's absolutely no reason
32313281 // to think these two methods are related.
@@ -3238,10 +3288,7 @@ class ObjCImplementationChecker {
32383288 && req->getObjCRuntimeName () != explicitObjCName)
32393289 return MatchOutcome::WrongExplicitObjCName;
32403290
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)
32453292 return MatchOutcome::WrongSwiftName;
32463293
32473294 if (!hasObjCNameMatch)
@@ -3254,9 +3301,16 @@ class ObjCImplementationChecker {
32543301 != req->getDeclContext ())
32553302 return MatchOutcome::WrongCategory;
32563303
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;
32603314
32613315 // If we got here, everything matched. But at what quality?
32623316 if (explicitObjCName)
@@ -3265,6 +3319,17 @@ class ObjCImplementationChecker {
32653319 return MatchOutcome::Match;
32663320 }
32673321
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+
32683333 void diagnoseOutcome (MatchOutcome outcome, ValueDecl *req, ValueDecl *cand,
32693334 ObjCSelector explicitObjCName) {
32703335 auto reqObjCName = *req->getObjCRuntimeName ();
@@ -3318,6 +3383,22 @@ class ObjCImplementationChecker {
33183383 getCategoryName (cand->getDeclContext ()->
33193384 getImplementedObjCContext ()));
33203385 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 ;
33213402 }
33223403
33233404 llvm_unreachable (" Unknown MatchOutcome" );
0 commit comments