Skip to content

Commit a4a6d09

Browse files
authored
Merge pull request #59479 from beccadax/you-completion-me
Allow @objc protocols to declare async alternatives
2 parents a8ad273 + 5459737 commit a4a6d09

22 files changed

+174
-59
lines changed

include/swift/AST/Decl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6757,7 +6757,7 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
67576757
bool hasDynamicSelfResult() const;
67586758

67596759
/// The async function marked as the alternative to this function, if any.
6760-
AbstractFunctionDecl *getAsyncAlternative() const;
6760+
AbstractFunctionDecl *getAsyncAlternative(bool isKnownObjC = false) const;
67616761

67626762
/// If \p asyncAlternative is set, then compare its parameters to this
67636763
/// (presumed synchronous) function's parameters to find the index of the

include/swift/AST/ModuleLoader.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -226,11 +226,11 @@ class ModuleLoader {
226226
virtual void loadExtensions(NominalTypeDecl *nominal,
227227
unsigned previousGeneration) { }
228228

229-
/// Load the methods within the given class that produce
229+
/// Load the methods within the given type that produce
230230
/// Objective-C class or instance methods with the given selector.
231231
///
232-
/// \param classDecl The class in which we are searching for @objc methods.
233-
/// The search only considers this class and its extensions; not any
232+
/// \param typeDecl The type in which we are searching for @objc methods.
233+
/// The search only considers this type and its extensions; not any
234234
/// superclasses.
235235
///
236236
/// \param selector The selector to search for.
@@ -246,7 +246,7 @@ class ModuleLoader {
246246
/// selector and are instance/class methods as requested. This list will be
247247
/// extended with any methods found in subsequent generations.
248248
virtual void loadObjCMethods(
249-
ClassDecl *classDecl,
249+
NominalTypeDecl *typeDecl,
250250
ObjCSelector selector,
251251
bool isInstanceMethod,
252252
unsigned previousGeneration,

include/swift/AST/SourceFile.h

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "swift/AST/Import.h"
1818
#include "swift/AST/SynthesizedFileUnit.h"
1919
#include "swift/Basic/Debug.h"
20+
#include "llvm/ADT/Hashing.h"
2021
#include "llvm/ADT/SetVector.h"
2122
#include "llvm/ADT/SmallPtrSet.h"
2223

@@ -243,11 +244,20 @@ class SourceFile final : public FileUnit {
243244
std::vector<ObjCUnsatisfiedOptReq> ObjCUnsatisfiedOptReqs;
244245

245246
/// A selector that is used by two different declarations in the same class.
246-
/// Fields: typeDecl, selector, isInstanceMethod.
247-
using ObjCMethodConflict = std::tuple<NominalTypeDecl *, ObjCSelector, bool>;
247+
struct ObjCMethodConflict {
248+
NominalTypeDecl *typeDecl;
249+
ObjCSelector selector;
250+
bool isInstanceMethod;
251+
252+
ObjCMethodConflict(NominalTypeDecl *typeDecl, ObjCSelector selector,
253+
bool isInstanceMethod)
254+
: typeDecl(typeDecl), selector(selector),
255+
isInstanceMethod(isInstanceMethod)
256+
{}
257+
};
248258

249259
/// List of Objective-C member conflicts we have found during type checking.
250-
std::vector<ObjCMethodConflict> ObjCMethodConflicts;
260+
llvm::SetVector<ObjCMethodConflict> ObjCMethodConflicts;
251261

252262
/// List of attributes added by access notes, used to emit remarks for valid
253263
/// ones.
@@ -636,4 +646,30 @@ inline void simple_display(llvm::raw_ostream &out, const SourceFile *SF) {
636646
}
637647
} // end namespace swift
638648

649+
namespace llvm {
650+
651+
template<>
652+
struct DenseMapInfo<swift::SourceFile::ObjCMethodConflict> {
653+
using ObjCMethodConflict = swift::SourceFile::ObjCMethodConflict;
654+
655+
static inline ObjCMethodConflict getEmptyKey() {
656+
return ObjCMethodConflict(nullptr, {}, false);
657+
}
658+
static inline ObjCMethodConflict getTombstoneKey() {
659+
return ObjCMethodConflict(nullptr, {}, true);
660+
}
661+
static inline unsigned getHashValue(ObjCMethodConflict a) {
662+
return hash_combine(hash_value(a.typeDecl),
663+
DenseMapInfo<swift::ObjCSelector>::getHashValue(a.selector),
664+
hash_value(a.isInstanceMethod));
665+
}
666+
static bool isEqual(ObjCMethodConflict a, ObjCMethodConflict b) {
667+
return a.typeDecl == b.typeDecl && a.selector == b.selector &&
668+
a.isInstanceMethod == b.isInstanceMethod;
669+
}
670+
};
671+
672+
}
673+
674+
639675
#endif

include/swift/AST/TypeCheckRequests.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3441,7 +3441,8 @@ class ConditionalRequirementsRequest
34413441

34423442
class RenamedDeclRequest
34433443
: public SimpleRequest<RenamedDeclRequest,
3444-
ValueDecl *(const ValueDecl *, const AvailableAttr *),
3444+
ValueDecl *(const ValueDecl *, const AvailableAttr *,
3445+
bool isKnownObjC),
34453446
RequestFlags::Cached> {
34463447
public:
34473448
using SimpleRequest::SimpleRequest;
@@ -3450,7 +3451,7 @@ class RenamedDeclRequest
34503451
friend SimpleRequest;
34513452

34523453
ValueDecl *evaluate(Evaluator &evaluator, const ValueDecl *attached,
3453-
const AvailableAttr *attr) const;
3454+
const AvailableAttr *attr, bool isKnownObjC) const;
34543455

34553456
public:
34563457
bool isCached() const { return true; }

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ SWIFT_REQUEST(TypeChecker, GetImplicitSendableRequest,
390390
ProtocolConformance *(NominalTypeDecl *),
391391
Cached, NoLocationInfo)
392392
SWIFT_REQUEST(TypeChecker, RenamedDeclRequest,
393-
ValueDecl *(const ValueDecl *),
393+
ValueDecl *(const ValueDecl *, const AvailableAttr *, bool),
394394
Cached, NoLocationInfo)
395395
SWIFT_REQUEST(TypeChecker, ClosureEffectsRequest,
396396
FunctionType::ExtInfo(ClosureExpr *),

include/swift/ClangImporter/ClangImporter.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ class ClangImporter final : public ClangModuleLoader {
309309
unsigned previousGeneration) override;
310310

311311
virtual void loadObjCMethods(
312-
ClassDecl *classDecl,
312+
NominalTypeDecl *typeDecl,
313313
ObjCSelector selector,
314314
bool isInstanceMethod,
315315
unsigned previousGeneration,

include/swift/Sema/SourceLoader.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ class SourceLoader : public ModuleLoader {
8585
unsigned previousGeneration) override;
8686

8787
virtual void loadObjCMethods(
88-
ClassDecl *classDecl,
88+
NominalTypeDecl *typeDecl,
8989
ObjCSelector selector,
9090
bool isInstanceMethod,
9191
unsigned previousGeneration,

include/swift/Serialization/SerializedModuleLoader.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ class SerializedModuleLoaderBase : public ModuleLoader {
190190
unsigned previousGeneration) override;
191191

192192
virtual void loadObjCMethods(
193-
ClassDecl *classDecl,
193+
NominalTypeDecl *typeDecl,
194194
ObjCSelector selector,
195195
bool isInstanceMethod,
196196
unsigned previousGeneration,

lib/AST/ASTContext.cpp

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1919,24 +1919,12 @@ void ASTContext::loadObjCMethods(
19191919
PrettyStackTraceSelector stackTraceSelector("looking for", selector);
19201920
PrettyStackTraceDecl stackTraceDecl("...in", tyDecl);
19211921

1922-
// @objc protocols cannot have @objc extension members, so if we've recorded
1923-
// everything in the protocol definition, we've recorded everything. And we
1924-
// only ever use the ObjCSelector version of `NominalTypeDecl::lookupDirect()`
1925-
// on protocols in primary file typechecking, so we don't care about protocols
1926-
// that need to be loaded from files.
1927-
// TODO: Rework `ModuleLoader::loadObjCMethods()` to support protocols too if
1928-
// selector-based `NominalTypeDecl::lookupDirect()` ever needs to work
1929-
// in more situations.
1930-
ClassDecl *classDecl = dyn_cast<ClassDecl>(tyDecl);
1931-
if (!classDecl)
1932-
return;
1933-
19341922
for (auto &loader : getImpl().ModuleLoaders) {
19351923
// Ignore the Clang importer if we've been asked for Swift-only results.
19361924
if (swiftOnly && loader.get() == getClangModuleLoader())
19371925
continue;
19381926

1939-
loader->loadObjCMethods(classDecl, selector, isInstanceMethod,
1927+
loader->loadObjCMethods(tyDecl, selector, isInstanceMethod,
19401928
previousGeneration, methods);
19411929
}
19421930
}

lib/AST/Decl.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7627,7 +7627,8 @@ bool AbstractFunctionDecl::hasDynamicSelfResult() const {
76277627
return isa<ConstructorDecl>(this);
76287628
}
76297629

7630-
AbstractFunctionDecl *AbstractFunctionDecl::getAsyncAlternative() const {
7630+
AbstractFunctionDecl *
7631+
AbstractFunctionDecl::getAsyncAlternative(bool isKnownObjC) const {
76317632
// Async functions can't have async alternatives
76327633
if (hasAsync())
76337634
return nullptr;
@@ -7651,7 +7652,8 @@ AbstractFunctionDecl *AbstractFunctionDecl::getAsyncAlternative() const {
76517652
}
76527653

76537654
auto *renamedDecl = evaluateOrDefault(
7654-
getASTContext().evaluator, RenamedDeclRequest{this, avAttr}, nullptr);
7655+
getASTContext().evaluator, RenamedDeclRequest{this, avAttr, isKnownObjC},
7656+
nullptr);
76557657
auto *alternative = dyn_cast_or_null<AbstractFunctionDecl>(renamedDecl);
76567658
if (!alternative || !alternative->hasAsync())
76577659
return nullptr;

lib/AST/NameLookup.cpp

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,7 +1310,24 @@ class swift::ObjCMethodLookupTable
13101310
: public llvm::DenseMap<std::pair<ObjCSelector, char>,
13111311
StoredObjCMethods>,
13121312
public ASTAllocated<ObjCMethodLookupTable>
1313-
{};
1313+
{
1314+
SWIFT_DEBUG_DUMP {
1315+
llvm::errs() << "ObjCMethodLookupTable:\n";
1316+
for (auto pair : *this) {
1317+
auto selector = pair.getFirst().first;
1318+
auto isInstanceMethod = pair.getFirst().second;
1319+
auto &methods = pair.getSecond();
1320+
1321+
llvm::errs() << " \"" << (isInstanceMethod ? "-" : "+") << selector
1322+
<< "\":\n";
1323+
for (auto method : methods.Methods) {
1324+
llvm::errs() << " - \"";
1325+
method->dumpRef(llvm::errs());
1326+
llvm::errs() << "\"\n";
1327+
}
1328+
}
1329+
}
1330+
};
13141331

13151332
MemberLookupTable::MemberLookupTable(ASTContext &ctx) {
13161333
// Register a cleanup with the ASTContext to call the lookup table
@@ -1699,6 +1716,16 @@ NominalTypeDecl::lookupDirect(ObjCSelector selector, bool isInstance) {
16991716
return stored.Methods;
17001717
}
17011718

1719+
/// Is the new method an async alternative of any existing method, or vice
1720+
/// versa?
1721+
static bool isAnAsyncAlternative(AbstractFunctionDecl *newDecl,
1722+
llvm::TinyPtrVector<AbstractFunctionDecl *> &vec) {
1723+
return llvm::any_of(vec, [&](AbstractFunctionDecl *oldDecl) {
1724+
return newDecl->getAsyncAlternative(/*isKnownObjC=*/true) == oldDecl
1725+
|| oldDecl->getAsyncAlternative(/*isKnownObjC=*/true) == newDecl;
1726+
});
1727+
}
1728+
17021729
void NominalTypeDecl::recordObjCMethod(AbstractFunctionDecl *method,
17031730
ObjCSelector selector) {
17041731
if (!ObjCMethodLookup && !createObjCMethodLookup())
@@ -1714,12 +1741,11 @@ void NominalTypeDecl::recordObjCMethod(AbstractFunctionDecl *method,
17141741
return;
17151742

17161743
if (auto *sf = method->getParentSourceFile()) {
1717-
if (vec.size() == 1) {
1718-
// We have a conflict.
1719-
sf->ObjCMethodConflicts.push_back(std::make_tuple(this, selector,
1720-
isInstanceMethod));
1721-
} if (vec.empty()) {
1744+
if (vec.empty()) {
17221745
sf->ObjCMethodList.push_back(method);
1746+
} else if (!isa<ProtocolDecl>(this) || !isAnAsyncAlternative(method, vec)) {
1747+
// We have a conflict.
1748+
sf->ObjCMethodConflicts.insert({ this, selector, isInstanceMethod });
17231749
}
17241750
}
17251751

lib/ClangImporter/ClangImporter.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3569,11 +3569,16 @@ void ClangImporter::loadExtensions(NominalTypeDecl *nominal,
35693569
}
35703570

35713571
void ClangImporter::loadObjCMethods(
3572-
ClassDecl *classDecl,
3572+
NominalTypeDecl *typeDecl,
35733573
ObjCSelector selector,
35743574
bool isInstanceMethod,
35753575
unsigned previousGeneration,
35763576
llvm::TinyPtrVector<AbstractFunctionDecl *> &methods) {
3577+
// TODO: We don't currently need to load methods from imported ObjC protocols.
3578+
auto classDecl = dyn_cast<ClassDecl>(typeDecl);
3579+
if (!classDecl)
3580+
return;
3581+
35773582
const auto *objcClass =
35783583
dyn_cast_or_null<clang::ObjCInterfaceDecl>(classDecl->getClangDecl());
35793584
if (!objcClass)

lib/PrintAsClang/DeclAndTypePrinter.cpp

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,7 +1124,8 @@ class DeclAndTypePrinter::Implementation
11241124
assert(!AvAttr->Rename.empty());
11251125

11261126
auto *renamedDecl = evaluateOrDefault(
1127-
getASTContext().evaluator, RenamedDeclRequest{D, AvAttr}, nullptr);
1127+
getASTContext().evaluator, RenamedDeclRequest{D, AvAttr, false},
1128+
nullptr);
11281129
if (renamedDecl) {
11291130
assert(shouldInclude(renamedDecl) &&
11301131
"ObjC printer logic mismatch with renamed decl");
@@ -2112,12 +2113,31 @@ auto DeclAndTypePrinter::getImpl() -> Implementation {
21122113
return Implementation(os, *this, outputLang);
21132114
}
21142115

2116+
static bool isAsyncAlternativeOfOtherDecl(const ValueDecl *VD) {
2117+
auto AFD = dyn_cast<AbstractFunctionDecl>(VD);
2118+
if (!AFD || !AFD->isAsyncContext() || !AFD->getObjCSelector())
2119+
return false;
2120+
2121+
auto type = AFD->getDeclContext()->getSelfNominalTypeDecl();
2122+
if (!type)
2123+
return false;
2124+
auto others = type->lookupDirect(AFD->getObjCSelector(),
2125+
AFD->isInstanceMember());
2126+
2127+
for (auto other : others)
2128+
if (other->getAsyncAlternative() == AFD)
2129+
return true;
2130+
2131+
return false;
2132+
}
2133+
21152134
bool DeclAndTypePrinter::shouldInclude(const ValueDecl *VD) {
21162135
return !VD->isInvalid() &&
21172136
(outputLang == OutputLanguageMode::Cxx
21182137
? cxx_translation::isVisibleToCxx(VD, minRequiredAccess)
21192138
: isVisibleToObjC(VD, minRequiredAccess)) &&
2120-
!VD->getAttrs().hasAttribute<ImplementationOnlyAttr>();
2139+
!VD->getAttrs().hasAttribute<ImplementationOnlyAttr>() &&
2140+
!isAsyncAlternativeOfOtherDecl(VD);
21212141
}
21222142

21232143
void DeclAndTypePrinter::print(const Decl *D) {

lib/Sema/TypeCheckAttr.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6294,7 +6294,8 @@ static bool parametersMatch(const AbstractFunctionDecl *a,
62946294

62956295
ValueDecl *RenamedDeclRequest::evaluate(Evaluator &evaluator,
62966296
const ValueDecl *attached,
6297-
const AvailableAttr *attr) const {
6297+
const AvailableAttr *attr,
6298+
bool isKnownObjC) const {
62986299
if (!attached || !attr)
62996300
return nullptr;
63006301

@@ -6325,7 +6326,7 @@ ValueDecl *RenamedDeclRequest::evaluate(Evaluator &evaluator,
63256326
auto minAccess = AccessLevel::Private;
63266327
if (attached->getModuleContext()->isExternallyConsumed())
63276328
minAccess = AccessLevel::Public;
6328-
bool attachedIsObjcVisible =
6329+
bool attachedIsObjcVisible = isKnownObjC ||
63296330
objc_translation::isVisibleToObjC(attached, minAccess);
63306331

63316332
SmallVector<ValueDecl *, 4> lookupResults;

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2422,11 +2422,8 @@ bool swift::diagnoseUnintendedObjCMethodOverrides(SourceFile &sf) {
24222422
/// Retrieve the source file for the given Objective-C member conflict.
24232423
static TinyPtrVector<AbstractFunctionDecl *>
24242424
getObjCMethodConflictDecls(const SourceFile::ObjCMethodConflict &conflict) {
2425-
NominalTypeDecl *typeDecl = std::get<0>(conflict);
2426-
ObjCSelector selector = std::get<1>(conflict);
2427-
bool isInstanceMethod = std::get<2>(conflict);
2428-
2429-
return typeDecl->lookupDirect(selector, isInstanceMethod);
2425+
return conflict.typeDecl->lookupDirect(conflict.selector,
2426+
conflict.isInstanceMethod);
24302427
}
24312428

24322429
static ObjCAttr *getObjCAttrIfFromAccessNote(ValueDecl *VD) {
@@ -2452,7 +2449,8 @@ bool swift::diagnoseObjCMethodConflicts(SourceFile &sf) {
24522449
// Sort the set of conflicts so we get a deterministic order for
24532450
// diagnostics. We use the first conflicting declaration in each set to
24542451
// perform the sort.
2455-
auto localConflicts = sf.ObjCMethodConflicts;
2452+
llvm::SmallVector<SourceFile::ObjCMethodConflict, 4> localConflicts;
2453+
llvm::copy(sf.ObjCMethodConflicts, std::back_inserter(localConflicts));
24562454
std::sort(localConflicts.begin(), localConflicts.end(),
24572455
[&](const SourceFile::ObjCMethodConflict &lhs,
24582456
const SourceFile::ObjCMethodConflict &rhs) {
@@ -2463,9 +2461,6 @@ bool swift::diagnoseObjCMethodConflicts(SourceFile &sf) {
24632461
// Diagnose each conflict.
24642462
bool anyConflicts = false;
24652463
for (const auto &conflict : localConflicts) {
2466-
NominalTypeDecl *tyDecl = std::get<0>(conflict);
2467-
ObjCSelector selector = std::get<1>(conflict);
2468-
24692464
auto methods = getObjCMethodConflictDecls(conflict);
24702465

24712466
// Erase any invalid or stub declarations. We don't want to complain about
@@ -2545,10 +2540,10 @@ bool swift::diagnoseObjCMethodConflicts(SourceFile &sf) {
25452540
: diag::objc_redecl,
25462541
diagInfo.first, diagInfo.second,
25472542
origDiagInfo.first, origDiagInfo.second,
2548-
selector);
2543+
conflict.selector);
25492544

25502545
// Protocols weren't checked for selector conflicts in 5.0.
2551-
diag.warnUntilSwiftVersionIf(!isa<ClassDecl>(tyDecl), 6);
2546+
diag.warnUntilSwiftVersionIf(!isa<ClassDecl>(conflict.typeDecl), 6);
25522547

25532548
auto objcAttr = getObjCAttrIfFromAccessNote(conflictingDecl);
25542549
swift::softenIfAccessNote(conflictingDecl, objcAttr, diag);

0 commit comments

Comments
 (0)