-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Add @_objcImplementation #60630
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add @_objcImplementation #60630
Changes from all commits
1ee12c5
e2f76c9
4682743
f2a0ab7
e544c21
0678a38
3a2f12a
ecf0ee6
ba1ec90
6143b83
f61f807
a36b521
ad359fa
7f0791e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -172,6 +172,10 @@ class DeclAttribute : public AttributeBase { | |
kind : NumKnownProtocolKindBits, | ||
isUnchecked : 1 | ||
); | ||
|
||
SWIFT_INLINE_BITFIELD(ObjCImplementationAttr, DeclAttribute, 1, | ||
isCategoryNameInvalid : 1 | ||
); | ||
} Bits; | ||
|
||
DeclAttribute *Next = nullptr; | ||
|
@@ -2275,6 +2279,31 @@ class DocumentationAttr: public DeclAttribute { | |
} | ||
}; | ||
|
||
class ObjCImplementationAttr final : public DeclAttribute { | ||
public: | ||
Identifier CategoryName; | ||
|
||
ObjCImplementationAttr(Identifier CategoryName, SourceLoc AtLoc, | ||
SourceRange Range, bool Implicit = false, | ||
bool isCategoryNameInvalid = false) | ||
: DeclAttribute(DAK_ObjCImplementation, AtLoc, Range, Implicit), | ||
CategoryName(CategoryName) { | ||
Bits.ObjCImplementationAttr.isCategoryNameInvalid = isCategoryNameInvalid; | ||
} | ||
|
||
bool isCategoryNameInvalid() const { | ||
return Bits.ObjCImplementationAttr.isCategoryNameInvalid; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This means that a category name was given but doesn't match anything from the class? |
||
|
||
void setCategoryNameInvalid(bool newValue = true) { | ||
Bits.ObjCImplementationAttr.isCategoryNameInvalid = newValue; | ||
} | ||
|
||
static bool classof(const DeclAttribute *DA) { | ||
return DA->getKind() == DAK_ObjCImplementation; | ||
} | ||
}; | ||
|
||
/// Attributes that may be applied to declarations. | ||
class DeclAttributes { | ||
/// Linked list of declaration attributes. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -723,6 +723,13 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> { | |
private: | ||
llvm::PointerUnion<DeclContext *, ASTContext *> Context; | ||
|
||
/// The imported Clang declaration representing the \c @_objcInterface for | ||
/// this declaration (or vice versa), or \c nullptr if there is none. | ||
/// | ||
/// If \c this (an otherwise nonsensical value), the value has not yet been | ||
/// computed. | ||
Decl *CachedObjCImplementationDecl; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we want to pay for this on literally every declaration we make in the compiler. I doubt this is even hot. Can you use out-of-line storage for it? |
||
|
||
Decl(const Decl&) = delete; | ||
void operator=(const Decl&) = delete; | ||
SourceLoc getLocFromSource() const; | ||
|
@@ -740,7 +747,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> { | |
protected: | ||
|
||
Decl(DeclKind kind, llvm::PointerUnion<DeclContext *, ASTContext *> context) | ||
: Context(context) { | ||
: Context(context), CachedObjCImplementationDecl(this) { | ||
Bits.OpaqueBits = 0; | ||
Bits.Decl.Kind = unsigned(kind); | ||
Bits.Decl.Invalid = false; | ||
|
@@ -975,6 +982,32 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> { | |
return getClangNodeImpl().getAsMacro(); | ||
} | ||
|
||
/// If this is the Swift implementation of a declaration imported from ObjC, | ||
/// returns the imported declaration. Otherwise return \c nullptr. | ||
/// | ||
/// \seeAlso ExtensionDecl::isObjCInterface() | ||
Decl *getImplementedObjCDecl() const; | ||
|
||
/// If this is the ObjC interface of a declaration implemented in Swift, | ||
/// returns the implementating declaration. Otherwise return \c nullptr. | ||
/// | ||
/// \seeAlso ExtensionDecl::isObjCInterface() | ||
Decl *getObjCImplementationDecl() const; | ||
|
||
Optional<Decl *> getCachedObjCImplementationDecl() const { | ||
if (CachedObjCImplementationDecl == this) | ||
return None; | ||
return CachedObjCImplementationDecl; | ||
} | ||
|
||
void setCachedObjCImplementationDecl(Decl *decl) { | ||
assert((CachedObjCImplementationDecl == this | ||
|| CachedObjCImplementationDecl == decl) | ||
&& "can't change CachedObjCInterfaceDecl once it's computed"); | ||
assert(decl != this && "can't form circular reference"); | ||
CachedObjCImplementationDecl = decl; | ||
} | ||
|
||
/// Return the GenericContext if the Decl has one. | ||
LLVM_READONLY | ||
const GenericContext *getAsGenericContext() const; | ||
|
@@ -1496,6 +1529,16 @@ class ExtensionDecl final : public GenericContext, public Decl, | |
/// resiliently moved into the original protocol itself. | ||
bool isEquivalentToExtendedContext() const; | ||
|
||
/// True if this extension provides an implementation for an imported | ||
/// Objective-C \c \@interface. This implies various restrictions and special | ||
/// behaviors for its members. | ||
bool isObjCImplementation() const; | ||
|
||
/// Returns the name of the category specified by the \c \@_objcImplementation | ||
/// attribute, or \c None if the name is invalid. Do not call unless | ||
/// \c isObjCImplementation() returns \c true. | ||
Optional<Identifier> getCategoryNameForObjCImplementation() const; | ||
|
||
// Implement isa/cast/dyncast/etc. | ||
static bool classof(const Decl *D) { | ||
return D->getKind() == DeclKind::Extension; | ||
|
@@ -4460,6 +4503,12 @@ class ClassDecl final : public NominalTypeDecl { | |
/// the Objective-C runtime. | ||
StringRef getObjCRuntimeName(llvm::SmallVectorImpl<char> &buffer) const; | ||
|
||
/// Return the imported declaration for the category with the given name; this | ||
/// will always be an Objective-C-backed \c ExtensionDecl or, if \p name is | ||
/// empty, \c ClassDecl. Returns \c nullptr if the class was not imported from | ||
/// Objective-C or does not have an imported category by that name. | ||
IterableDeclContext *getImportedObjCCategory(Identifier name) const; | ||
|
||
// Implement isa/cast/dyncast/etc. | ||
static bool classof(const Decl *D) { | ||
return D->getKind() == DeclKind::Class; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this is just internal documentation, but I think I would phrase this as:
Some part of me really hates that the first use case is done with an
extension
.I don't know that I have a better option, though. Having two
ClassDecl
s, orgutting one for the other, both seem worse.