Skip to content

Commit 9bcd818

Browse files
committed
[NFC] Make objcImpl request one-to-many
An @_objcImpl extension with no category name *should* implement not only the class’s main @interface, but also any class extension @interfaces. Start making this true by making ObjCInterfaceAndImplementationRequest return all of these decls as the interfaces for such an implementation. This commit doesn’t actually change Sema or IRGen to process the extra interfaces, so it’s NFC.
1 parent 1197768 commit 9bcd818

File tree

7 files changed

+145
-119
lines changed

7 files changed

+145
-119
lines changed

include/swift/AST/Decl.h

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,10 +1167,23 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
11671167
}
11681168

11691169
/// If this is the Swift implementation of a declaration imported from ObjC,
1170-
/// returns the imported declaration. Otherwise return \c nullptr.
1170+
/// returns the imported declaration. (If there are several, only the main
1171+
/// class body will be returned.) Otherwise return \c nullptr.
11711172
///
11721173
/// \seeAlso ExtensionDecl::isObjCInterface()
1173-
Decl *getImplementedObjCDecl() const;
1174+
Decl *getImplementedObjCDecl() const {
1175+
auto impls = getAllImplementedObjCDecls();
1176+
if (impls.empty())
1177+
return nullptr;
1178+
return impls.front();
1179+
}
1180+
1181+
/// If this is the Swift implementation of a declaration imported from ObjC,
1182+
/// returns the imported declarations. (There may be several for a main class
1183+
/// body; if so, the first will be the class itself.) Otherwise return an empty list.
1184+
///
1185+
/// \seeAlso ExtensionDecl::isObjCInterface()
1186+
llvm::TinyPtrVector<Decl *> getAllImplementedObjCDecls() const;
11741187

11751188
/// If this is the ObjC interface of a declaration implemented in Swift,
11761189
/// returns the implementating declaration. Otherwise return \c nullptr.
@@ -1835,8 +1848,8 @@ class ExtensionDecl final : public GenericContext, public Decl,
18351848
bool isEquivalentToExtendedContext() const;
18361849

18371850
/// Returns the name of the category specified by the \c \@_objcImplementation
1838-
/// attribute, or \c None if the name is invalid. Do not call unless
1839-
/// \c isObjCImplementation() returns \c true.
1851+
/// attribute, or \c None if the name is invalid or
1852+
/// \c isObjCImplementation() is false.
18401853
llvm::Optional<Identifier> getCategoryNameForObjCImplementation() const;
18411854

18421855
/// If this extension represents an imported Objective-C category, returns the
@@ -4920,11 +4933,14 @@ class ClassDecl final : public NominalTypeDecl {
49204933
/// the Objective-C runtime.
49214934
StringRef getObjCRuntimeName(llvm::SmallVectorImpl<char> &buffer) const;
49224935

4923-
/// Return the imported declaration for the category with the given name; this
4924-
/// will always be an Objective-C-backed \c ExtensionDecl or, if \p name is
4925-
/// empty, \c ClassDecl. Returns \c nullptr if the class was not imported from
4926-
/// Objective-C or does not have an imported category by that name.
4927-
IterableDeclContext *getImportedObjCCategory(Identifier name) const;
4936+
/// Return the imported declaration(s) for the category with the given name; this
4937+
/// will either be a single imported \c ExtensionDecl, an imported
4938+
/// \c ClassDecl followed by zero or more imported \c ExtensionDecl s (if
4939+
/// \p name is empty; the extensions are for any class extensions), or empty
4940+
/// if the class was not imported from Objective-C or does not have a
4941+
/// category by that name.
4942+
llvm::TinyPtrVector<Decl *>
4943+
getImportedObjCCategory(Identifier name) const;
49284944

49294945
// Implement isa/cast/dyncast/etc.
49304946
static bool classof(const Decl *D) {

include/swift/ClangImporter/ClangImporterRequests.h

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -207,17 +207,21 @@ void simple_display(llvm::raw_ostream &out,
207207
const ClangCategoryLookupDescriptor &desc);
208208
SourceLoc extractNearestSourceLoc(const ClangCategoryLookupDescriptor &desc);
209209

210-
/// Given a Swift class, find the imported Swift decl representing the
211-
/// \c \@interface with the given category name. That is, this will return an
212-
/// \c swift::ExtensionDecl backed by a \c clang::ObjCCategoryDecl, or a
213-
/// \c swift::ClassDecl backed by a \c clang::ObjCInterfaceDecl, or \c nullptr
214-
/// if the class is not imported from Clang or it does not have a category by
215-
/// that name.
210+
/// Given a Swift class, find the imported Swift decl(s) representing the
211+
/// \c \@interface with the given category name. An empty \c categoryName
212+
/// represents the main interface for the class.
216213
///
217-
/// An empty/invalid \c categoryName requests the main interface for the class.
214+
/// That is, this request will return one of:
215+
///
216+
/// \li a single \c swift::ExtensionDecl backed by a \c clang::ObjCCategoryDecl
217+
/// \li a \c swift::ClassDecl backed by a \c clang::ObjCInterfaceDecl, plus
218+
/// zero or more \c swift::ExtensionDecl s backed by
219+
/// \c clang::ObjCCategoryDecl s (representing ObjC class extensions).
220+
/// \li an empty list if the class is not imported from Clang or it does not
221+
/// have a category by that name.
218222
class ClangCategoryLookupRequest
219223
: public SimpleRequest<ClangCategoryLookupRequest,
220-
IterableDeclContext *(ClangCategoryLookupDescriptor),
224+
TinyPtrVector<Decl *>(ClangCategoryLookupDescriptor),
221225
RequestFlags::Uncached> {
222226
public:
223227
using SimpleRequest::SimpleRequest;
@@ -226,39 +230,46 @@ class ClangCategoryLookupRequest
226230
friend SimpleRequest;
227231

228232
// Evaluation.
229-
IterableDeclContext *evaluate(Evaluator &evaluator,
233+
TinyPtrVector<Decl *> evaluate(Evaluator &evaluator,
230234
ClangCategoryLookupDescriptor desc) const;
231235
};
232236

233-
/// Links an imported Clang decl to the native Swift decl(s) that implement it
234-
/// using \c \@_objcImplementation.
237+
/// Links an \c \@_objcImplementation decl to the imported declaration(s) that
238+
/// it implements.
239+
///
240+
/// There is usually a 1:1 correspondence between interfaces and
241+
/// implementations, except that a class's main implementation implements
242+
/// both its main interface and any class extension interfaces. In this
243+
/// situation, the main class is always the first decl in \c interfaceDecls.
235244
struct ObjCInterfaceAndImplementation final {
236-
Decl *interfaceDecl;
245+
llvm::TinyPtrVector<Decl *> interfaceDecls;
237246
Decl *implementationDecl;
238247

239-
ObjCInterfaceAndImplementation(Decl *interfaceDecl,
248+
ObjCInterfaceAndImplementation(llvm::TinyPtrVector<Decl *> interfaceDecls,
240249
Decl *implementationDecl)
241-
: interfaceDecl(interfaceDecl), implementationDecl(implementationDecl)
250+
: interfaceDecls(interfaceDecls), implementationDecl(implementationDecl)
242251
{
243-
assert(interfaceDecl && implementationDecl &&
252+
assert(!interfaceDecls.empty() && implementationDecl &&
244253
"interface and implementation are both non-null");
245254
}
246255

247256
ObjCInterfaceAndImplementation()
248-
: interfaceDecl(nullptr), implementationDecl(nullptr) {}
257+
: interfaceDecls(), implementationDecl(nullptr) {}
249258

250259
operator bool() const {
251-
return interfaceDecl;
260+
return interfaceDecls.empty();
252261
}
253262

254263
friend llvm::hash_code
255264
hash_value(const ObjCInterfaceAndImplementation &pair) {
256-
return llvm::hash_combine(pair.interfaceDecl, pair.implementationDecl);
265+
return hash_combine(llvm::hash_combine_range(pair.interfaceDecls.begin(),
266+
pair.interfaceDecls.end()),
267+
pair.implementationDecl);
257268
}
258269

259270
friend bool operator==(const ObjCInterfaceAndImplementation &lhs,
260271
const ObjCInterfaceAndImplementation &rhs) {
261-
return lhs.interfaceDecl == rhs.interfaceDecl
272+
return lhs.interfaceDecls == rhs.interfaceDecls
262273
&& lhs.implementationDecl == rhs.implementationDecl;
263274
}
264275

@@ -272,13 +283,12 @@ void simple_display(llvm::raw_ostream &out,
272283
const ObjCInterfaceAndImplementation &desc);
273284
SourceLoc extractNearestSourceLoc(const ObjCInterfaceAndImplementation &desc);
274285

275-
/// Given a \c Decl whose declaration is imported from ObjC but whose
276-
/// implementation is provided by a Swift \c \@_objcImplementation
277-
/// \c extension , return both decls, with the imported interface first.
278-
/// Otherwise return \c {nullptr,nullptr} .
286+
/// Given a \c Decl , determine if it is an implementation with separate
287+
/// interfaces imported from ObjC (or vice versa) and if so, return all of the
288+
/// declarations involved in this relationship. Otherwise return an empty value.
279289
///
280-
/// We retrieve both in a single request because we want to cache the
281-
/// relationship on both sides to avoid duplicating work.
290+
/// We perform this lookup in both directions using a single request because
291+
/// we want to cache the relationship on both sides to avoid duplicating work.
282292
class ObjCInterfaceAndImplementationRequest
283293
: public SimpleRequest<ObjCInterfaceAndImplementationRequest,
284294
ObjCInterfaceAndImplementation(Decl *),

include/swift/ClangImporter/ClangImporterTypeIDZone.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ SWIFT_REQUEST(ClangImporter, ClangRecordMemberLookup,
2525
Decl *(ClangRecordMemberLookupDescriptor), Uncached,
2626
NoLocationInfo)
2727
SWIFT_REQUEST(ClangImporter, ClangCategoryLookupRequest,
28-
IterableDeclContext *(ClangCategoryLookupDescriptor), Uncached,
28+
llvm::TinyPtrVector<Decl *>(ClangCategoryLookupDescriptor), Uncached,
2929
NoLocationInfo)
3030
SWIFT_REQUEST(ClangImporter, ObjCInterfaceAndImplementationRequest,
3131
ObjCInterfaceAndImplementation(Decl *), SeparatelyCached,

lib/AST/Decl.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1854,11 +1854,9 @@ bool Decl::isObjCImplementation() const {
18541854

18551855
llvm::Optional<Identifier>
18561856
ExtensionDecl::getCategoryNameForObjCImplementation() const {
1857-
assert(isObjCImplementation());
1858-
18591857
auto attr = getAttrs()
18601858
.getAttribute<ObjCImplementationAttr>(/*AllowInvalid=*/true);
1861-
if (attr->isCategoryNameInvalid())
1859+
if (!attr || attr->isCategoryNameInvalid())
18621860
return llvm::None;
18631861

18641862
return attr->CategoryName;

0 commit comments

Comments
 (0)