Skip to content

Commit 7aa5b7f

Browse files
authored
Merge pull request #71445 from beccadax/category-error
2 parents 5bf28be + 68ec6b6 commit 7aa5b7f

16 files changed

+261
-172
lines changed

Diff for: include/swift/AST/Decl.h

+38-29
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
356356
// for the inline bitfields.
357357
union { uint64_t OpaqueBits;
358358

359-
SWIFT_INLINE_BITFIELD_BASE(Decl, bitmax(NumDeclKindBits,8)+1+1+1+1+1+1,
359+
SWIFT_INLINE_BITFIELD_BASE(Decl, bitmax(NumDeclKindBits,8)+1+1+1+1+1+1+1,
360360
Kind : bitmax(NumDeclKindBits,8),
361361

362362
/// Whether this declaration is invalid.
@@ -382,7 +382,12 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
382382
Hoisted : 1,
383383

384384
/// Whether the set of semantic attributes has been computed.
385-
SemanticAttrsComputed : 1
385+
SemanticAttrsComputed : 1,
386+
387+
/// True if \c ObjCInterfaceAndImplementationRequest has been computed
388+
/// and did \em not find anything. This is the fast path where we can bail
389+
/// out without checking other caches or computing anything.
390+
LacksObjCInterfaceOrImplementation : 1
386391
);
387392

388393
SWIFT_INLINE_BITFIELD_FULL(PatternBindingDecl, Decl, 1+1+2+16,
@@ -810,13 +815,6 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
810815
private:
811816
llvm::PointerUnion<DeclContext *, ASTContext *> Context;
812817

813-
/// The imported Clang declaration representing the \c @_objcInterface for
814-
/// this declaration (or vice versa), or \c nullptr if there is none.
815-
///
816-
/// If \c this (an otherwise nonsensical value), the value has not yet been
817-
/// computed.
818-
Decl *CachedObjCImplementationDecl;
819-
820818
Decl(const Decl&) = delete;
821819
void operator=(const Decl&) = delete;
822820
SourceLoc getLocFromSource() const;
@@ -834,14 +832,15 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
834832
protected:
835833

836834
Decl(DeclKind kind, llvm::PointerUnion<DeclContext *, ASTContext *> context)
837-
: Context(context), CachedObjCImplementationDecl(this) {
835+
: Context(context) {
838836
Bits.OpaqueBits = 0;
839837
Bits.Decl.Kind = unsigned(kind);
840838
Bits.Decl.Invalid = false;
841839
Bits.Decl.Implicit = false;
842840
Bits.Decl.FromClang = false;
843841
Bits.Decl.EscapedFromIfConfig = false;
844842
Bits.Decl.Hoisted = false;
843+
Bits.Decl.LacksObjCInterfaceOrImplementation = false;
845844
}
846845

847846
/// Get the Clang node associated with this declaration.
@@ -1162,29 +1161,36 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
11621161
}
11631162

11641163
/// If this is the Swift implementation of a declaration imported from ObjC,
1165-
/// returns the imported declaration. Otherwise return \c nullptr.
1164+
/// returns the imported declaration. (If there are several, only the main
1165+
/// class body will be returned.) Otherwise return \c nullptr.
1166+
///
1167+
/// \seeAlso ExtensionDecl::isObjCInterface()
1168+
Decl *getImplementedObjCDecl() const {
1169+
auto impls = getAllImplementedObjCDecls();
1170+
if (impls.empty())
1171+
return nullptr;
1172+
return impls.front();
1173+
}
1174+
1175+
/// If this is the Swift implementation of a declaration imported from ObjC,
1176+
/// returns the imported declarations. (There may be several for a main class
1177+
/// body; if so, the first will be the class itself.) Otherwise return an empty list.
11661178
///
11671179
/// \seeAlso ExtensionDecl::isObjCInterface()
1168-
Decl *getImplementedObjCDecl() const;
1180+
llvm::TinyPtrVector<Decl *> getAllImplementedObjCDecls() const;
11691181

11701182
/// If this is the ObjC interface of a declaration implemented in Swift,
11711183
/// returns the implementating declaration. Otherwise return \c nullptr.
11721184
///
11731185
/// \seeAlso ExtensionDecl::isObjCInterface()
11741186
Decl *getObjCImplementationDecl() const;
11751187

1176-
llvm::Optional<Decl *> getCachedObjCImplementationDecl() const {
1177-
if (CachedObjCImplementationDecl == this)
1178-
return llvm::None;
1179-
return CachedObjCImplementationDecl;
1188+
bool getCachedLacksObjCInterfaceOrImplementation() const {
1189+
return Bits.Decl.LacksObjCInterfaceOrImplementation;
11801190
}
11811191

1182-
void setCachedObjCImplementationDecl(Decl *decl) {
1183-
assert((CachedObjCImplementationDecl == this
1184-
|| CachedObjCImplementationDecl == decl)
1185-
&& "can't change CachedObjCInterfaceDecl once it's computed");
1186-
assert(decl != this && "can't form circular reference");
1187-
CachedObjCImplementationDecl = decl;
1192+
void setCachedLacksObjCInterfaceOrImplementation(bool value) {
1193+
Bits.Decl.LacksObjCInterfaceOrImplementation = value;
11881194
}
11891195

11901196
/// Return the GenericContext if the Decl has one.
@@ -1846,8 +1852,8 @@ class ExtensionDecl final : public GenericContext, public Decl,
18461852
bool isEquivalentToExtendedContext() const;
18471853

18481854
/// Returns the name of the category specified by the \c \@_objcImplementation
1849-
/// attribute, or \c None if the name is invalid. Do not call unless
1850-
/// \c isObjCImplementation() returns \c true.
1855+
/// attribute, or \c None if the name is invalid or
1856+
/// \c isObjCImplementation() is false.
18511857
llvm::Optional<Identifier> getCategoryNameForObjCImplementation() const;
18521858

18531859
/// If this extension represents an imported Objective-C category, returns the
@@ -4962,11 +4968,14 @@ class ClassDecl final : public NominalTypeDecl {
49624968
/// the Objective-C runtime.
49634969
StringRef getObjCRuntimeName(llvm::SmallVectorImpl<char> &buffer) const;
49644970

4965-
/// Return the imported declaration for the category with the given name; this
4966-
/// will always be an Objective-C-backed \c ExtensionDecl or, if \p name is
4967-
/// empty, \c ClassDecl. Returns \c nullptr if the class was not imported from
4968-
/// Objective-C or does not have an imported category by that name.
4969-
IterableDeclContext *getImportedObjCCategory(Identifier name) const;
4971+
/// Return the imported declaration(s) for the category with the given name; this
4972+
/// will either be a single imported \c ExtensionDecl, an imported
4973+
/// \c ClassDecl followed by zero or more imported \c ExtensionDecl s (if
4974+
/// \p name is empty; the extensions are for any class extensions), or empty
4975+
/// if the class was not imported from Objective-C or does not have a
4976+
/// category by that name.
4977+
llvm::TinyPtrVector<Decl *>
4978+
getImportedObjCCategory(Identifier name) const;
49704979

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

Diff for: include/swift/ClangImporter/ClangImporter.h

+6
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,12 @@ typedef llvm::PointerUnion<const clang::Decl *, const clang::MacroInfo *,
132132
class ClangImporter final : public ClangModuleLoader {
133133
friend class ClangModuleUnit;
134134

135+
// Make requests in the ClangImporter zone friends so they can access `Impl`.
136+
#define SWIFT_REQUEST(Zone, Name, Sig, Caching, LocOptions) \
137+
friend class Name;
138+
#include "swift/ClangImporter/ClangImporterTypeIDZone.def"
139+
#undef SWIFT_REQUEST
140+
135141
public:
136142
class Implementation;
137143

Diff for: include/swift/ClangImporter/ClangImporterRequests.h

+35-25
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 *),

Diff for: include/swift/ClangImporter/ClangImporterTypeIDZone.def

+1-1
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,

Diff for: lib/AST/Decl.cpp

+1-3
Original file line numberDiff line numberDiff line change
@@ -1813,11 +1813,9 @@ bool Decl::isObjCImplementation() const {
18131813

18141814
llvm::Optional<Identifier>
18151815
ExtensionDecl::getCategoryNameForObjCImplementation() const {
1816-
assert(isObjCImplementation());
1817-
18181816
auto attr = getAttrs()
18191817
.getAttribute<ObjCImplementationAttr>(/*AllowInvalid=*/true);
1820-
if (attr->isCategoryNameInvalid())
1818+
if (!attr || attr->isCategoryNameInvalid())
18211819
return llvm::None;
18221820

18231821
return attr->CategoryName;

0 commit comments

Comments
 (0)