Skip to content
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

Change objcImpl syntax to @objc @implementation #73309

Merged
merged 9 commits into from
May 21, 2024
3 changes: 3 additions & 0 deletions include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -2422,6 +2422,9 @@ class DocumentationAttr: public DeclAttribute {

class ObjCImplementationAttr final : public DeclAttribute {
public:
/// Name of the category being implemented. This should only be used with
/// the early adopter \@\_objcImplementation syntax, but we support it there
/// for backwards compatibility.
Identifier CategoryName;

ObjCImplementationAttr(Identifier CategoryName, SourceLoc AtLoc,
Expand Down
24 changes: 15 additions & 9 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1025,6 +1025,10 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
/// Returns the source range of the declaration including its attributes.
SourceRange getSourceRangeIncludingAttrs() const;

/// Retrieve the location at which we should insert a new attribute or
/// modifier.
SourceLoc getAttributeInsertionLoc(bool forModifier) const;

using ImportAccessLevel = std::optional<AttributedImport<ImportedModule>>;

/// Returns the import that may restrict the access to this decl
Expand Down Expand Up @@ -1912,11 +1916,6 @@ class ExtensionDecl final : public GenericContext, public Decl,
/// \endcode
bool isWrittenWithConstraints() const;

/// Returns the name of the category specified by the \c \@_objcImplementation
/// attribute, or \c None if the name is invalid or
/// \c isObjCImplementation() is false.
std::optional<Identifier> getCategoryNameForObjCImplementation() const;

/// If this extension represents an imported Objective-C category, returns the
/// category's name. Otherwise returns the empty identifier.
Identifier getObjCCategoryName() const;
Expand Down Expand Up @@ -3177,10 +3176,6 @@ class ValueDecl : public Decl {
/// can't be "static" or are in a context where "static" doesn't make sense.
bool isStatic() const;

/// Retrieve the location at which we should insert a new attribute or
/// modifier.
SourceLoc getAttributeInsertionLoc(bool forModifier) const;

static bool classof(const Decl *D) {
return D->getKind() >= DeclKind::First_ValueDecl &&
D->getKind() <= DeclKind::Last_ValueDecl;
Expand Down Expand Up @@ -4204,6 +4199,12 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
/// Retrieve the set of extensions of this type.
ExtensionRange getExtensions();

/// Retrieve the extension most recently added to this type. Helpful to
/// determine if an extension has been added.
ExtensionDecl *getLastExtension() const {
return LastExtension;
}

/// Special-behaviour flags passed to lookupDirect()
enum class LookupDirectFlags {
/// Whether to include @_implements members.
Expand Down Expand Up @@ -5069,6 +5070,11 @@ class ClassDecl final : public NominalTypeDecl {
llvm::TinyPtrVector<Decl *>
getImportedObjCCategory(Identifier name) const;

/// Return a map of category names to extensions with that category name,
/// whether imported or otherwise.
llvm::DenseMap<Identifier, llvm::TinyPtrVector<ExtensionDecl *>>
getObjCCategoryNameMap();

// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) {
return D->getKind() == DeclKind::Class;
Expand Down
8 changes: 3 additions & 5 deletions include/swift/AST/DiagnosticsClangImporter.def
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,10 @@ WARNING(api_pattern_attr_ignored, none,
(StringRef, StringRef))

ERROR(objc_implementation_two_impls, none,
"duplicate implementation of Objective-C %select{|category %0 on }0"
"%kind1",
(Identifier, Decl *))

"duplicate implementation of imported %kind0",
(Decl *))
NOTE(previous_objc_implementation, none,
"previously implemented by extension here", ())
"previously implemented here", ())

NOTE(macro_not_imported_unsupported_operator, none, "operator not supported in macro arithmetic", ())
NOTE(macro_not_imported_unsupported_named_operator, none, "operator '%0' not supported in macro arithmetic", (StringRef))
Expand Down
23 changes: 18 additions & 5 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,9 @@ ERROR(expr_selector_not_objc,none,
NOTE(make_decl_objc,none,
"add '@objc' to expose this %0 to Objective-C",
(DescriptiveDeclKind))
NOTE(make_decl_objc_for_implementation,none,
"add '@objc' to implement an Objective-C %0",
(DescriptiveDeclKind))

// Selectors-as-string-literals.
WARNING(selector_literal_invalid,none,
Expand Down Expand Up @@ -1760,10 +1763,6 @@ WARNING(objc_implementation_early_spelling_deprecated,none,
ERROR(attr_objc_implementation_must_be_unconditional,none,
"only unconditional extensions can implement an Objective-C '@interface'",
())
ERROR(attr_objc_implementation_must_extend_class,none,
"cannot mark extension of %kind0 with '@_objcImplementation'; it is not "
"an imported Objective-C class",
(ValueDecl *))
ERROR(attr_objc_implementation_must_be_imported,none,
"'@_objcImplementation' cannot be used to extend %kind0 because it was "
"defined by a Swift 'class' declaration, not an imported Objective-C "
Expand Down Expand Up @@ -1800,6 +1799,14 @@ ERROR(attr_objc_implementation_raise_minimum_deployment_target,none,
"'@implementation' of an Objective-C class requires a minimum deployment "
"target of at least %0 %1",
(StringRef, llvm::VersionTuple))
ERROR(attr_implementation_requires_language,none,
"'@implementation' used without specifying the language being "
"implemented",
())
ERROR(attr_implementation_category_goes_on_objc_attr,none,
"Objective-C category should be specified on '@objc', not "
"'@implementation'",
())

ERROR(member_of_objc_implementation_not_objc_or_final,none,
"%kind0 does not match any %kindonly0 declared in the headers for %1; "
Expand Down Expand Up @@ -6295,7 +6302,7 @@ ERROR(objc_extension_not_class,none,
"'@objc' can only be applied to an extension of a class", ())

// If you change this, also change enum ObjCReason
#define OBJC_ATTR_SELECT "select{marked @_cdecl|marked dynamic|marked @objc|marked @objcMembers|marked @IBOutlet|marked @IBAction|marked @IBSegueAction|marked @NSManaged|a member of an @objc protocol|implicitly @objc|an @objc override|an implementation of an @objc requirement|marked @IBInspectable|marked @GKInspectable|in an @objc extension of a class (without @nonobjc)|in an @_objcImplementation extension of a class (without final or @nonobjc)|marked @objc by an access note}"
#define OBJC_ATTR_SELECT "select{marked @_cdecl|marked dynamic|marked @objc|marked @objcMembers|marked @IBOutlet|marked @IBAction|marked @IBSegueAction|marked @NSManaged|a member of an @objc protocol|implicitly @objc|an @objc override|an implementation of an @objc requirement|marked @IBInspectable|marked @GKInspectable|in an @objc extension of a class (without @nonobjc)|in an @objc @implementation extension of a class (without final or @nonobjc)|marked @objc by an access note}"

ERROR(objc_invalid_on_var,none,
"property cannot be %" OBJC_ATTR_SELECT "0 "
Expand Down Expand Up @@ -6450,6 +6457,12 @@ ERROR(objc_redecl_same,none,
"previous declaration with the same Objective-C selector",
(unsigned, DeclName, unsigned, DeclName, ObjCSelector))

ERROR(objc_redecl_category_name,none,
"%select{|imported }0extension with Objective-C category name %2 "
"conflicts with previous %select{|imported }1extension with the same "
"category name",
(bool, bool, Identifier))

ERROR(objc_override_other,none,
OBJC_DIAG_SELECT " with Objective-C selector %4 conflicts with "
OBJC_DIAG_SELECT_2 " from superclass %5 with the same Objective-C "
Expand Down
33 changes: 33 additions & 0 deletions include/swift/AST/NameLookupRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -970,6 +970,39 @@ class LookupIntrinsicRequest
bool isCached() const { return true; }
};

using ObjCCategoryNameMap =
llvm::DenseMap<Identifier, llvm::TinyPtrVector<ExtensionDecl *>>;

/// Generate a map of all known extensions of the given class that have an
/// explicit category name. This request does not force clang categories that
/// haven't been imported already, but it will generate a new map if new
/// categories have been imported since the cached value was generated.
///
/// \seeAlso ClassDecl::getObjCCategoryNameMap()
class ObjCCategoryNameMapRequest
: public SimpleRequest<ObjCCategoryNameMapRequest,
ObjCCategoryNameMap(ClassDecl *, ExtensionDecl *),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;

// Convenience to automatically extract `lastExtension`.
ObjCCategoryNameMapRequest(ClassDecl *classDecl)
: ObjCCategoryNameMapRequest(classDecl, classDecl->getLastExtension())
{}

private:
friend SimpleRequest;

// Evaluation.
ObjCCategoryNameMap evaluate(Evaluator &evaluator,
ClassDecl *classDecl,
ExtensionDecl *lastExtension) const;

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

#define SWIFT_TYPEID_ZONE NameLookup
#define SWIFT_TYPEID_HEADER "swift/AST/NameLookupTypeIDZone.def"
#include "swift/Basic/DefineTypeIDZone.h"
Expand Down
2 changes: 2 additions & 0 deletions include/swift/AST/NameLookupTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,5 @@ SWIFT_REQUEST(NameLookup, ImplementsAttrProtocolRequest,
ProtocolDecl *(const ImplementsAttr *, DeclContext *), Cached, NoLocationInfo)
SWIFT_REQUEST(NameLookup, LookupIntrinsicRequest,
FuncDecl *(ModuleDecl *, Identifier), Cached, NoLocationInfo)
SWIFT_REQUEST(NameLookup, ObjCCategoryNameMapRequest,
ObjCCategoryNameMap(ClassDecl *, ExtensionDecl *), Cached, NoLocationInfo)
4 changes: 4 additions & 0 deletions include/swift/AST/SourceFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,10 @@ class SourceFile final : public FileUnit {
/// List of Objective-C member conflicts we have found during type checking.
llvm::SetVector<ObjCMethodConflict> ObjCMethodConflicts;

/// Categories (extensions with explicit @objc names) declared in this
/// source file. They need to be checked for conflicts after type checking.
llvm::TinyPtrVector<ExtensionDecl *> ObjCCategories;

/// List of attributes added by access notes, used to emit remarks for valid
/// ones.
llvm::DenseMap<ValueDecl *, std::vector<DeclAttribute *>>
Expand Down
2 changes: 1 addition & 1 deletion include/swift/ClangImporter/ClangImporterRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ struct ObjCInterfaceAndImplementation final {
ObjCInterfaceAndImplementation()
: interfaceDecls(), implementationDecl(nullptr) {}

operator bool() const {
bool empty() const {
return interfaceDecls.empty();
}

Expand Down
121 changes: 93 additions & 28 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1891,16 +1891,6 @@ bool Decl::isObjCImplementation() const {
return getAttrs().hasAttribute<ObjCImplementationAttr>(/*AllowInvalid=*/true);
}

std::optional<Identifier>
ExtensionDecl::getCategoryNameForObjCImplementation() const {
auto attr = getAttrs()
.getAttribute<ObjCImplementationAttr>(/*AllowInvalid=*/true);
if (!attr || attr->isCategoryNameInvalid())
return std::nullopt;

return attr->CategoryName;
}

PatternBindingDecl::PatternBindingDecl(SourceLoc StaticLoc,
StaticSpellingKind StaticSpelling,
SourceLoc VarLoc,
Expand Down Expand Up @@ -3717,6 +3707,20 @@ void ValueDecl::setIsObjC(bool value) {
LazySemanticInfo.isObjC = value;
}

Identifier ExtensionDecl::getObjCCategoryName() const {
// If there's an @objc attribute, it's authoritative. (ClangImporter
// attaches one automatically.)
if (auto objcAttr = getAttrs().getAttribute<ObjCAttr>(/*AllowInvalid*/true)) {
if (objcAttr->hasName() && objcAttr->getName()->getNumArgs() == 0)
return objcAttr->getName()->getSimpleName();

return Identifier();
}

// Not a category, evidently.
return Identifier();
}

bool ValueDecl::isSemanticallyFinal() const {
// Actor types are semantically final.
if (auto classDecl = dyn_cast<ClassDecl>(this)) {
Expand Down Expand Up @@ -4011,25 +4015,86 @@ bool ValueDecl::canInferObjCFromRequirement(ValueDecl *requirement) {
return false;
}

SourceLoc ValueDecl::getAttributeInsertionLoc(bool forModifier) const {
if (isImplicit())
SourceLoc Decl::getAttributeInsertionLoc(bool forModifier) const {
// Some decls have a parent/child split where the introducer keyword is on the
// parent, but the attributes are on the children. If this is a child in such
// a pair, `introDecl` will be changed to point to the parent. (The parent
// decl should delegate to one of its children.)
const Decl *introDecl = this;

switch (getKind()) {
case DeclKind::Module:
case DeclKind::TopLevelCode:
case DeclKind::IfConfig:
case DeclKind::PoundDiagnostic:
case DeclKind::Missing:
case DeclKind::MissingMember:
case DeclKind::MacroExpansion:
case DeclKind::BuiltinTuple:
// These don't take attributes.
return SourceLoc();

if (auto var = dyn_cast<VarDecl>(this)) {
// [attrs] var ...
// The attributes are part of the VarDecl, but the 'var' is part of the PBD.
SourceLoc resultLoc = var->getAttrs().getStartLoc(forModifier);
if (resultLoc.isValid()) {
return resultLoc;
} else if (auto pbd = var->getParentPatternBinding()) {
return pbd->getStartLoc();
} else {
return var->getStartLoc();
case DeclKind::EnumCase:
// An ECD's attributes are attached to its elements.
if (auto elem = cast<EnumCaseDecl>(this)->getFirstElement())
return elem->getAttributeInsertionLoc(forModifier);
break;

case DeclKind::EnumElement:
// An EED's introducer keyword is on its parent case.
if (auto parent = cast<EnumElementDecl>(this)->getParentCase())
introDecl = parent;
break;

case DeclKind::PatternBinding: {
// A PBD's attributes are attached to the vars in its patterns.
auto pbd = cast<PatternBindingDecl>(this);

for (unsigned i = 0; i < pbd->getNumPatternEntries(); i++) {
if (auto var = pbd->getAnchoringVarDecl(i)) {
return var->getAttributeInsertionLoc(forModifier);
}
}

break;
}

case DeclKind::Var:
case DeclKind::Param:
// A VarDecl's introducer keyword, if it has one, is on its pattern binding.
if (auto pbd = cast<VarDecl>(this)->getParentPatternBinding())
introDecl = pbd;
break;

case DeclKind::Enum:
case DeclKind::Struct:
case DeclKind::Class:
case DeclKind::Protocol:
case DeclKind::OpaqueType:
case DeclKind::TypeAlias:
case DeclKind::GenericTypeParam:
case DeclKind::AssociatedType:
case DeclKind::Subscript:
case DeclKind::Constructor:
case DeclKind::Destructor:
case DeclKind::Func:
case DeclKind::Accessor:
case DeclKind::Macro:
case DeclKind::Extension:
case DeclKind::Import:
case DeclKind::PrecedenceGroup:
case DeclKind::InfixOperator:
case DeclKind::PrefixOperator:
case DeclKind::PostfixOperator:
// Both the introducer keyword and the attributes are on `this`.
break;
}

if (isImplicit())
return SourceLoc();

SourceLoc resultLoc = getAttrs().getStartLoc(forModifier);
return resultLoc.isValid() ? resultLoc : getStartLoc();
return resultLoc.isValid() ? resultLoc : introDecl->getStartLoc();
}

/// Returns true if \p VD needs to be treated as publicly-accessible
Expand Down Expand Up @@ -11278,7 +11343,7 @@ void swift::simple_display(llvm::raw_ostream &out, const Decl *decl) {
}

if (auto value = dyn_cast<ValueDecl>(decl)) {
simple_display(out, value);
return simple_display(out, value);
} else if (auto ext = dyn_cast<ExtensionDecl>(decl)) {
out << "extension of ";
if (auto typeRepr = ext->getExtendedTypeRepr())
Expand All @@ -11288,13 +11353,13 @@ void swift::simple_display(llvm::raw_ostream &out, const Decl *decl) {
} else if (auto med = dyn_cast<MacroExpansionDecl>(decl)) {
out << '#' << med->getMacroName() << " in ";
printContext(out, med->getDeclContext());
if (med->getLoc().isValid()) {
out << '@';
med->getLoc().print(out, med->getASTContext().SourceMgr);
}
} else {
out << "(unknown decl)";
}
if (decl->getLoc().isValid()) {
out << '@';
decl->getLoc().print(out, decl->getASTContext().SourceMgr);
}
}

void swift::simple_display(llvm::raw_ostream &out,
Expand Down