Skip to content

Commit e967219

Browse files
authored
Merge pull request #69468 from beccadax/c-implementation
Make @objcImpl work with @_cdecl
2 parents 15ff8a7 + 5aa932f commit e967219

26 files changed

+519
-176
lines changed

include/swift/AST/Attr.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ DECL_ATTR(_restatedObjCConformance, RestatedObjCConformance,
246246
OnProtocol | UserInaccessible | LongAttribute | RejectByParser | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
247247
70)
248248
DECL_ATTR(_objcImplementation, ObjCImplementation,
249-
OnExtension | UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove,
249+
OnExtension | OnAbstractFunction | UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove,
250250
72)
251251
DECL_ATTR(_optimize, Optimize,
252252
OnAbstractFunction | OnSubscript | OnVar | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,

include/swift/AST/Decl.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,11 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
941941
/// attribute macro expansion.
942942
DeclAttributes getSemanticAttrs() const;
943943

944+
/// True if this declaration provides an implementation for an imported
945+
/// Objective-C declaration. This implies various restrictions and special
946+
/// behaviors for it and, if it's an extension, its members.
947+
bool isObjCImplementation() const;
948+
944949
using AuxiliaryDeclCallback = llvm::function_ref<void(Decl *)>;
945950

946951
/// Iterate over the auxiliary declarations for this declaration,
@@ -1836,11 +1841,6 @@ class ExtensionDecl final : public GenericContext, public Decl,
18361841
/// resiliently moved into the original protocol itself.
18371842
bool isEquivalentToExtendedContext() const;
18381843

1839-
/// True if this extension provides an implementation for an imported
1840-
/// Objective-C \c \@interface. This implies various restrictions and special
1841-
/// behaviors for its members.
1842-
bool isObjCImplementation() const;
1843-
18441844
/// Returns the name of the category specified by the \c \@_objcImplementation
18451845
/// attribute, or \c None if the name is invalid. Do not call unless
18461846
/// \c isObjCImplementation() returns \c true.
@@ -2771,6 +2771,10 @@ class ValueDecl : public Decl {
27712771
return DeclNameRef(Name);
27722772
}
27732773

2774+
/// Retrieve the C declaration name that names this function, or empty
2775+
/// string if it has none.
2776+
StringRef getCDeclName() const;
2777+
27742778
/// Retrieve the name to use for this declaration when interoperating
27752779
/// with the Objective-C runtime.
27762780
///

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,9 @@ WARNING(api_pattern_attr_ignored, none,
134134

135135
ERROR(objc_implementation_two_impls, none,
136136
"duplicate implementation of Objective-C %select{|category %0 on }0"
137-
"class %1",
138-
(Identifier, ValueDecl *))
137+
"%kind1",
138+
(Identifier, Decl *))
139+
139140
NOTE(previous_objc_implementation, none,
140141
"previously implemented by extension here", ())
141142

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1740,9 +1740,17 @@ ERROR(attr_objc_implementation_category_not_found,none,
17401740
"could not find category %0 on Objective-C class %1; make sure your "
17411741
"umbrella or bridging header imports the header that declares it",
17421742
(Identifier, ValueDecl*))
1743+
ERROR(attr_objc_implementation_func_not_found,none,
1744+
"could not find imported function '%0' matching %kind1; make sure your "
1745+
"umbrella or bridging header imports the header that declares it",
1746+
(StringRef, ValueDecl*))
17431747
NOTE(attr_objc_implementation_fixit_remove_category_name,none,
17441748
"remove arguments to implement the main '@interface' for this class",
17451749
())
1750+
ERROR(attr_objc_implementation_no_category_for_func,none,
1751+
"%kind0 does not belong to an Objective-C category; remove the category "
1752+
"name from this attribute",
1753+
(ValueDecl*))
17461754
ERROR(attr_objc_implementation_no_conformance,none,
17471755
"'@_objcImplementation' extension cannot add conformance to %0; "
17481756
"add this conformance %select{with an ordinary extension|"

include/swift/AST/LookupKinds.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ enum NLOptions : unsigned {
4141

4242
/// Don't check access when doing lookup into a type.
4343
///
44-
/// This option is not valid when performing lookup into a module.
44+
/// When performing lookup into a module, this option only applies to
45+
/// declarations in the same module the lookup is coming from.
4546
NL_IgnoreAccessControl = 1 << 3,
4647

4748
/// This lookup should only return type declarations.

include/swift/AST/TypeCheckRequests.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4533,7 +4533,7 @@ class SemanticBriefCommentRequest
45334533
/// This is done on all of a class's implementations at once to improve diagnostics.
45344534
class TypeCheckObjCImplementationRequest
45354535
: public SimpleRequest<TypeCheckObjCImplementationRequest,
4536-
evaluator::SideEffect(ExtensionDecl *),
4536+
evaluator::SideEffect(Decl *),
45374537
RequestFlags::Cached> {
45384538
public:
45394539
using SimpleRequest::SimpleRequest;
@@ -4543,7 +4543,7 @@ class TypeCheckObjCImplementationRequest
45434543

45444544
// Evaluation.
45454545
evaluator::SideEffect
4546-
evaluate(Evaluator &evaluator, ExtensionDecl *ED) const;
4546+
evaluate(Evaluator &evaluator, Decl *D) const;
45474547

45484548
public:
45494549
// Separate caching.

lib/AST/Decl.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1851,7 +1851,7 @@ Type ExtensionDecl::getExtendedType() const {
18511851
return ErrorType::get(ctx);
18521852
}
18531853

1854-
bool ExtensionDecl::isObjCImplementation() const {
1854+
bool Decl::isObjCImplementation() const {
18551855
return getAttrs().hasAttribute<ObjCImplementationAttr>(/*AllowInvalid=*/true);
18561856
}
18571857

@@ -3867,6 +3867,22 @@ void ValueDecl::setInterfaceType(Type type) {
38673867
std::move(type));
38683868
}
38693869

3870+
StringRef ValueDecl::getCDeclName() const {
3871+
// Treat imported C functions as implicitly @_cdecl.
3872+
if (auto clangDecl = dyn_cast_or_null<clang::FunctionDecl>(getClangDecl())) {
3873+
if (clangDecl->getLanguageLinkage() == clang::CLanguageLinkage
3874+
&& clangDecl->getIdentifier())
3875+
return clangDecl->getName();
3876+
}
3877+
3878+
// Handle explicit cdecl attributes.
3879+
if (auto cdeclAttr = getAttrs().getAttribute<CDeclAttr>()) {
3880+
return cdeclAttr->Name;
3881+
}
3882+
3883+
return "";
3884+
}
3885+
38703886
llvm::Optional<ObjCSelector>
38713887
ValueDecl::getObjCRuntimeName(bool skipIsObjCResolution) const {
38723888
if (auto func = dyn_cast<AbstractFunctionDecl>(this))
@@ -4402,7 +4418,8 @@ static bool checkAccess(const DeclContext *useDC, const ValueDecl *VD,
44024418
// a context where we would access its storage directly, forbid access. Name
44034419
// lookups will instead find and use the matching interface decl.
44044420
// FIXME: Passing `true` for `isAccessOnSelf` may cause false positives.
4405-
if (isObjCMemberImplementation(VD, getAccessLevel) &&
4421+
if ((VD->isObjCImplementation() ||
4422+
isObjCMemberImplementation(VD, getAccessLevel)) &&
44064423
VD->getAccessSemanticsFromContext(useDC, /*isAccessOnSelf=*/true)
44074424
!= AccessSemantics::DirectToStorage)
44084425
return false;

lib/AST/ModuleNameLookup.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,11 @@ void ModuleNameLookup<LookupStrategy>::lookupInModule(
165165
if (resolutionKind == ResolutionKind::MacrosOnly && !isa<MacroDecl>(VD))
166166
return true;
167167
if (respectAccessControl &&
168+
// NL_IgnoreAccessControl applies only to the current module.
169+
!((options & NL_IgnoreAccessControl) &&
170+
moduleScopeContext &&
171+
moduleScopeContext->getParentModule() ==
172+
VD->getDeclContext()->getParentModule()) &&
168173
!VD->isAccessibleFrom(moduleScopeContext, false,
169174
includeUsableFromInline))
170175
return true;

lib/AST/SwiftNameTranslation.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ getObjCNameForSwiftDecl(const ValueDecl *VD, DeclName PreferredName){
139139
bool swift::objc_translation::
140140
isVisibleToObjC(const ValueDecl *VD, AccessLevel minRequiredAccess,
141141
bool checkParent) {
142-
if (!(VD->isObjC() || VD->getAttrs().hasAttribute<CDeclAttr>()))
142+
if (!(VD->isObjC() || !VD->getCDeclName().empty()))
143143
return false;
144144
if (VD->getFormalAccess() >= minRequiredAccess) {
145145
return true;

lib/ClangImporter/ClangImporter.cpp

Lines changed: 104 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5827,21 +5827,6 @@ findImplsGivenInterface(ClassDecl *classDecl, Identifier categoryName) {
58275827
impls.push_back(ext);
58285828
}
58295829

5830-
if (impls.size() > 1) {
5831-
llvm::sort(impls, OrderDecls());
5832-
5833-
auto &diags = classDecl->getASTContext().Diags;
5834-
for (auto extraImpl : llvm::ArrayRef<Decl *>(impls).drop_front()) {
5835-
auto attr = extraImpl->getAttrs().getAttribute<ObjCImplementationAttr>();
5836-
attr->setCategoryNameInvalid();
5837-
5838-
diags.diagnose(attr->getLocation(), diag::objc_implementation_two_impls,
5839-
categoryName, classDecl)
5840-
.fixItRemove(attr->getRangeWithAt());
5841-
diags.diagnose(impls.front(), diag::previous_objc_implementation);
5842-
}
5843-
}
5844-
58455830
return impls;
58465831
}
58475832

@@ -5872,10 +5857,26 @@ findInterfaceGivenImpl(ClassDecl *classDecl, ExtensionDecl *ext) {
58725857
}
58735858

58745859
static ObjCInterfaceAndImplementation
5875-
constructResult(Decl *interface, llvm::TinyPtrVector<Decl *> impls) {
5876-
if (impls.empty())
5860+
constructResult(Decl *interface, llvm::TinyPtrVector<Decl *> &impls,
5861+
Decl *diagnoseOn, Identifier categoryName) {
5862+
if (!interface || impls.empty())
58775863
return ObjCInterfaceAndImplementation();
58785864

5865+
if (impls.size() > 1) {
5866+
llvm::sort(impls, OrderDecls());
5867+
5868+
auto &diags = interface->getASTContext().Diags;
5869+
for (auto extraImpl : llvm::ArrayRef<Decl *>(impls).drop_front()) {
5870+
auto attr = extraImpl->getAttrs().getAttribute<ObjCImplementationAttr>();
5871+
attr->setCategoryNameInvalid();
5872+
5873+
diags.diagnose(attr->getLocation(), diag::objc_implementation_two_impls,
5874+
categoryName, diagnoseOn)
5875+
.fixItRemove(attr->getRangeWithAt());
5876+
diags.diagnose(impls.front(), diag::previous_objc_implementation);
5877+
}
5878+
}
5879+
58795880
return ObjCInterfaceAndImplementation(interface, impls.front());
58805881
}
58815882

@@ -5924,31 +5925,103 @@ findContextInterfaceAndImplementation(DeclContext *dc) {
59245925
// look for extensions implementing it.
59255926

59265927
auto implDecls = findImplsGivenInterface(classDecl, categoryName);
5927-
return constructResult(interfaceDecl, implDecls);
5928+
return constructResult(interfaceDecl, implDecls, classDecl, categoryName);
5929+
}
5930+
5931+
static void lookupRelatedFuncs(AbstractFunctionDecl *func,
5932+
SmallVectorImpl<ValueDecl *> &results) {
5933+
DeclName swiftName;
5934+
if (auto accessor = dyn_cast<AccessorDecl>(func))
5935+
swiftName = accessor->getStorage()->getName();
5936+
else
5937+
swiftName = func->getName();
5938+
5939+
if (auto ty = func->getDeclContext()->getSelfNominalTypeDecl()) {
5940+
ty->lookupQualified({ ty }, DeclNameRef(swiftName), func->getLoc(),
5941+
NL_QualifiedDefault | NL_IgnoreAccessControl, results);
5942+
}
5943+
else {
5944+
auto mod = func->getDeclContext()->getParentModule();
5945+
mod->lookupQualified(mod, DeclNameRef(swiftName), func->getLoc(),
5946+
NL_RemoveOverridden | NL_IgnoreAccessControl, results);
5947+
}
5948+
}
5949+
5950+
static ObjCInterfaceAndImplementation
5951+
findFunctionInterfaceAndImplementation(AbstractFunctionDecl *func) {
5952+
if (!func)
5953+
return {};
5954+
5955+
// If this isn't either a clang import or an implementation, there's no point
5956+
// doing any work here.
5957+
if (!func->hasClangNode() && !func->isObjCImplementation())
5958+
return {};
5959+
5960+
OptionalEnum<AccessorKind> accessorKind;
5961+
if (auto accessor = dyn_cast<AccessorDecl>(func))
5962+
accessorKind = accessor->getAccessorKind();
5963+
5964+
StringRef clangName = func->getCDeclName();
5965+
if (clangName.empty())
5966+
return {};
5967+
5968+
SmallVector<ValueDecl *, 4> results;
5969+
lookupRelatedFuncs(func, results);
5970+
5971+
// Classify the `results` as either the interface or an implementation.
5972+
// (Multiple implementations are invalid but utterable.)
5973+
Decl *interface = nullptr;
5974+
TinyPtrVector<Decl *> impls;
5975+
5976+
for (ValueDecl *result : results) {
5977+
AbstractFunctionDecl *resultFunc = nullptr;
5978+
if (accessorKind) {
5979+
if (auto resultStorage = dyn_cast<AbstractStorageDecl>(result))
5980+
resultFunc = resultStorage->getAccessor(*accessorKind);
5981+
}
5982+
else
5983+
resultFunc = dyn_cast<AbstractFunctionDecl>(result);
5984+
5985+
if (!resultFunc)
5986+
continue;
5987+
5988+
if (resultFunc->getCDeclName() != clangName)
5989+
continue;
5990+
5991+
if (resultFunc->hasClangNode()) {
5992+
if (interface) {
5993+
// This clang name is overloaded. That should only happen with C++
5994+
// functions/methods, which aren't currently supported.
5995+
return {};
5996+
}
5997+
interface = result;
5998+
} else if (resultFunc->isObjCImplementation()) {
5999+
impls.push_back(result);
6000+
}
6001+
}
6002+
6003+
// If we found enough decls to construct a result, `func` should be among them
6004+
// somewhere.
6005+
assert(interface == nullptr || impls.empty() ||
6006+
interface == func || llvm::is_contained(impls, func));
6007+
6008+
return constructResult(interface, impls, interface,
6009+
/*categoryName=*/Identifier());
59286010
}
59296011

59306012
ObjCInterfaceAndImplementation ObjCInterfaceAndImplementationRequest::
59316013
evaluate(Evaluator &evaluator, Decl *decl) const {
5932-
// These have direct links to their counterparts through the
6014+
// Types and extensions have direct links to their counterparts through the
59336015
// `@_objcImplementation` attribute. Let's resolve that.
59346016
// (Also directing nulls here, where they'll early-return.)
59356017
if (auto ty = dyn_cast_or_null<NominalTypeDecl>(decl))
59366018
return findContextInterfaceAndImplementation(ty);
59376019
else if (auto ext = dyn_cast<ExtensionDecl>(decl))
59386020
return findContextInterfaceAndImplementation(ext);
6021+
// Abstract functions have to be matched through their @_cdecl attributes.
6022+
else if (auto func = dyn_cast<AbstractFunctionDecl>(decl))
6023+
return findFunctionInterfaceAndImplementation(func);
59396024

5940-
// Anything else is resolved by first locating the context's interface and
5941-
// impl, then matching it to its counterpart. (Instead of calling
5942-
// `findContextInterfaceAndImplementation()` directly, we'll use the request
5943-
// recursively to take advantage of caching.)
5944-
auto contextDecl = decl->getDeclContext()->getAsDecl();
5945-
if (!contextDecl)
5946-
return {};
5947-
5948-
ObjCInterfaceAndImplementationRequest req(contextDecl);
5949-
/*auto contextPair =*/ evaluateOrDefault(evaluator, req, {});
5950-
5951-
// TODO: Implement member matching.
59526025
return {};
59536026
}
59546027

lib/IRGen/GenDecl.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3580,7 +3580,10 @@ llvm::Function *IRGenModule::getAddrOfSILFunction(
35803580
if (hasOrderNumber) {
35813581
auto &fnList = Module.getFunctionList();
35823582
fnList.remove(fn);
3583-
fnList.insert(llvm::Module::iterator(insertBefore), fn);
3583+
if (insertBefore)
3584+
fnList.insert(llvm::Module::iterator(insertBefore), fn);
3585+
else
3586+
fnList.push_back(fn);
35843587

35853588
EmittedFunctionsByOrder.insert(orderNumber, fn);
35863589
}

lib/PrintAsClang/DeclAndTypePrinter.cpp

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1414,8 +1414,7 @@ class DeclAndTypePrinter::Implementation
14141414

14151415
assert(FD->getAttrs().hasAttribute<CDeclAttr>() && "not a cdecl function");
14161416
os << "SWIFT_EXTERN ";
1417-
printFunctionDeclAsCFunctionDecl(
1418-
FD, FD->getAttrs().getAttribute<CDeclAttr>()->Name, resultTy);
1417+
printFunctionDeclAsCFunctionDecl(FD, FD->getCDeclName(), resultTy);
14191418
printFunctionClangAttributes(FD, funcTy);
14201419
printAvailability(FD);
14211420
os << ";\n";
@@ -1425,12 +1424,12 @@ class DeclAndTypePrinter::Implementation
14251424
FunctionSwiftABIInformation(AbstractFunctionDecl *FD,
14261425
LoweredFunctionSignature signature)
14271426
: signature(signature) {
1428-
isCDecl = FD->getAttrs().hasAttribute<CDeclAttr>();
1427+
isCDecl = !FD->getCDeclName().empty();
14291428
if (!isCDecl) {
14301429
auto mangledName = SILDeclRef(FD).mangle();
14311430
symbolName = FD->getASTContext().AllocateCopy(mangledName);
14321431
} else {
1433-
symbolName = FD->getAttrs().getAttribute<CDeclAttr>()->Name;
1432+
symbolName = FD->getCDeclName();
14341433
}
14351434
}
14361435

@@ -2858,12 +2857,16 @@ static bool hasExposeAttr(const ValueDecl *VD, bool isExtension = false) {
28582857
return false;
28592858
}
28602859

2861-
/// Skip \c \@objcImplementation \c extension member implementations and
2862-
/// overrides. They are already declared in handwritten headers, and they may
2863-
/// have attributes that aren't allowed in a category.
2860+
/// Skip \c \@objcImplementation functions, \c extension member
2861+
/// implementations, and overrides. They are already declared in handwritten
2862+
/// headers, and they may have attributes that aren't allowed in a category.
28642863
///
28652864
/// \return true if \p VD should \em not be included in the header.
28662865
static bool excludeForObjCImplementation(const ValueDecl *VD) {
2866+
// If it's an ObjC implementation (and not an extension, which might have
2867+
// members that need printing), skip it; it's declared elsewhere.
2868+
if (VD->isObjCImplementation() && ! isa<ExtensionDecl>(VD))
2869+
return true;
28672870
// Exclude member implementations; they are declared elsewhere.
28682871
if (VD->isObjCMemberImplementation())
28692872
return true;

0 commit comments

Comments
 (0)