Skip to content

Commit 3e910f9

Browse files
committed
Cache @_objcImpl pointers and diagnose dupes
1 parent 6ff7288 commit 3e910f9

File tree

10 files changed

+389
-55
lines changed

10 files changed

+389
-55
lines changed

include/swift/AST/Decl.h

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,13 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
723723
private:
724724
llvm::PointerUnion<DeclContext *, ASTContext *> Context;
725725

726+
/// The imported Clang declaration representing the \c @_objcInterface for
727+
/// this declaration (or vice versa), or \c nullptr if there is none.
728+
///
729+
/// If \c this (an otherwise nonsensical value), the value has not yet been
730+
/// computed.
731+
Decl *CachedObjCImplementationDecl;
732+
726733
Decl(const Decl&) = delete;
727734
void operator=(const Decl&) = delete;
728735
SourceLoc getLocFromSource() const;
@@ -740,7 +747,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
740747
protected:
741748

742749
Decl(DeclKind kind, llvm::PointerUnion<DeclContext *, ASTContext *> context)
743-
: Context(context) {
750+
: Context(context), CachedObjCImplementationDecl(this) {
744751
Bits.OpaqueBits = 0;
745752
Bits.Decl.Kind = unsigned(kind);
746753
Bits.Decl.Invalid = false;
@@ -975,6 +982,32 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
975982
return getClangNodeImpl().getAsMacro();
976983
}
977984

985+
/// If this is the Swift implementation of a declaration imported from ObjC,
986+
/// returns the imported declaration. Otherwise return \c nullptr.
987+
///
988+
/// \seeAlso ExtensionDecl::isObjCInterface()
989+
Decl *getImplementedObjCDecl() const;
990+
991+
/// If this is the ObjC interface of a declaration implemented in Swift,
992+
/// returns the implementating declaration. Otherwise return \c nullptr.
993+
///
994+
/// \seeAlso ExtensionDecl::isObjCInterface()
995+
Decl *getObjCImplementationDecl() const;
996+
997+
Optional<Decl *> getCachedObjCImplementationDecl() const {
998+
if (CachedObjCImplementationDecl == this)
999+
return None;
1000+
return CachedObjCImplementationDecl;
1001+
}
1002+
1003+
void setCachedObjCImplementationDecl(Decl *decl) {
1004+
assert((CachedObjCImplementationDecl == this
1005+
|| CachedObjCImplementationDecl == decl)
1006+
&& "can't change CachedObjCInterfaceDecl once it's computed");
1007+
assert(decl != this && "can't form circular reference");
1008+
CachedObjCImplementationDecl = decl;
1009+
}
1010+
9781011
/// Return the GenericContext if the Decl has one.
9791012
LLVM_READONLY
9801013
const GenericContext *getAsGenericContext() const;
@@ -1506,13 +1539,6 @@ class ExtensionDecl final : public GenericContext, public Decl,
15061539
/// \c isObjCImplementation() returns \c true.
15071540
Optional<Identifier> getCategoryNameForObjCImplementation() const;
15081541

1509-
/// Returns the \c clang::ObjCCategoryDecl or \c clang::ObjCInterfaceDecl
1510-
/// implemented by this extension.
1511-
///
1512-
/// This can return \c nullptr if the category doesn't exist. Do not call it
1513-
/// unless \c isObjCImplementation() returns \c true.
1514-
const clang::ObjCContainerDecl *getInterfaceForObjCImplementation() const;
1515-
15161542
// Implement isa/cast/dyncast/etc.
15171543
static bool classof(const Decl *D) {
15181544
return D->getKind() == DeclKind::Extension;
@@ -4380,7 +4406,7 @@ class ClassDecl final : public NominalTypeDecl {
43804406
/// will always be an Objective-C-backed \c ExtensionDecl or, if \p name is
43814407
/// empty, \c ClassDecl. Returns \c nullptr if the class was not imported from
43824408
/// Objective-C or does not have an imported category by that name.
4383-
Decl *getImportedObjCCategory(Identifier name) const;
4409+
IterableDeclContext *getImportedObjCCategory(Identifier name) const;
43844410

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

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,13 @@ WARNING(api_pattern_attr_ignored, none,
132132
"'%0' swift attribute ignored on type '%1': type is not copyable or destructible",
133133
(StringRef, StringRef))
134134

135+
ERROR(objc_implementation_two_impls, none,
136+
"duplicate implementation of Objective-C %select{|category %0 on }0"
137+
"class %1",
138+
(Identifier, ValueDecl *))
139+
NOTE(previous_objc_implementation, none,
140+
"previously implemented by extension here", ())
141+
135142
NOTE(macro_not_imported_unsupported_operator, none, "operator not supported in macro arithmetic", ())
136143
NOTE(macro_not_imported_unsupported_named_operator, none, "operator '%0' not supported in macro arithmetic", (StringRef))
137144
NOTE(macro_not_imported_invalid_string_literal, none, "invalid string literal", ())

include/swift/ClangImporter/ClangImporterRequests.h

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ SourceLoc extractNearestSourceLoc(const ClangCategoryLookupDescriptor &desc);
217217
/// An empty/invalid \c categoryName requests the main interface for the class.
218218
class ClangCategoryLookupRequest
219219
: public SimpleRequest<ClangCategoryLookupRequest,
220-
Decl *(ClangCategoryLookupDescriptor),
220+
IterableDeclContext *(ClangCategoryLookupDescriptor),
221221
RequestFlags::Uncached> {
222222
public:
223223
using SimpleRequest::SimpleRequest;
@@ -226,8 +226,78 @@ class ClangCategoryLookupRequest
226226
friend SimpleRequest;
227227

228228
// Evaluation.
229-
Decl *evaluate(Evaluator &evaluator,
230-
ClangCategoryLookupDescriptor desc) const;
229+
IterableDeclContext *evaluate(Evaluator &evaluator,
230+
ClangCategoryLookupDescriptor desc) const;
231+
};
232+
233+
/// Links an imported Clang decl to the native Swift decl(s) that implement it
234+
/// using \c \@_objcImplementation.
235+
struct ObjCInterfaceAndImplementation final {
236+
Decl *interfaceDecl;
237+
Decl *implementationDecl;
238+
239+
ObjCInterfaceAndImplementation(Decl *interfaceDecl,
240+
Decl *implementationDecl)
241+
: interfaceDecl(interfaceDecl), implementationDecl(implementationDecl)
242+
{
243+
assert(interfaceDecl && implementationDecl &&
244+
"interface and implementation are both non-null");
245+
}
246+
247+
ObjCInterfaceAndImplementation()
248+
: interfaceDecl(nullptr), implementationDecl(nullptr) {}
249+
250+
operator bool() const {
251+
return interfaceDecl;
252+
}
253+
254+
friend llvm::hash_code
255+
hash_value(const ObjCInterfaceAndImplementation &pair) {
256+
return llvm::hash_combine(pair.interfaceDecl, pair.implementationDecl);
257+
}
258+
259+
friend bool operator==(const ObjCInterfaceAndImplementation &lhs,
260+
const ObjCInterfaceAndImplementation &rhs) {
261+
return lhs.interfaceDecl == rhs.interfaceDecl
262+
&& lhs.implementationDecl == rhs.implementationDecl;
263+
}
264+
265+
friend bool operator!=(const ObjCInterfaceAndImplementation &lhs,
266+
const ObjCInterfaceAndImplementation &rhs) {
267+
return !(lhs == rhs);
268+
}
269+
};
270+
271+
void simple_display(llvm::raw_ostream &out,
272+
const ObjCInterfaceAndImplementation &desc);
273+
SourceLoc extractNearestSourceLoc(const ObjCInterfaceAndImplementation &desc);
274+
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} .
279+
///
280+
/// We retrieve both in a single request because we want to cache the
281+
/// relationship on both sides to avoid duplicating work.
282+
class ObjCInterfaceAndImplementationRequest
283+
: public SimpleRequest<ObjCInterfaceAndImplementationRequest,
284+
ObjCInterfaceAndImplementation(Decl *),
285+
RequestFlags::SeparatelyCached> {
286+
public:
287+
using SimpleRequest::SimpleRequest;
288+
289+
private:
290+
friend SimpleRequest;
291+
292+
// Evaluation.
293+
ObjCInterfaceAndImplementation
294+
evaluate(Evaluator &evaluator, Decl *decl) const;
295+
296+
public:
297+
// Separate caching.
298+
bool isCached() const { return true; }
299+
Optional<ObjCInterfaceAndImplementation> getCachedResult() const;
300+
void cacheResult(ObjCInterfaceAndImplementation value) const;
231301
};
232302

233303
enum class CxxRecordSemanticsKind {

include/swift/ClangImporter/ClangImporterTypeIDZone.def

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ SWIFT_REQUEST(ClangImporter, ClangRecordMemberLookup,
2525
Decl *(ClangRecordMemberLookupDescriptor), Uncached,
2626
NoLocationInfo)
2727
SWIFT_REQUEST(ClangImporter, ClangCategoryLookupRequest,
28-
Decl *(ClangCategoryLookupDescriptor), Uncached,
28+
IterableDeclContext *(ClangCategoryLookupDescriptor), Uncached,
29+
NoLocationInfo)
30+
SWIFT_REQUEST(ClangImporter, ObjCInterfaceAndImplementationRequest,
31+
ObjCInterfaceAndImplementation(Decl *), SeparatelyCached,
2932
NoLocationInfo)
3033
SWIFT_REQUEST(ClangImporter, CxxRecordSemantics,
3134
CxxRecordSemanticsKind(const clang::CXXRecordDecl *), Cached,

lib/AST/Decl.cpp

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1469,25 +1469,6 @@ bool ExtensionDecl::isObjCImplementation() const {
14691469
return getAttrs().hasAttribute<ObjCImplementationAttr>();
14701470
}
14711471

1472-
const clang::ObjCContainerDecl *
1473-
ExtensionDecl::getInterfaceForObjCImplementation() const {
1474-
auto categoryName = getCategoryNameForObjCImplementation();
1475-
if (!categoryName)
1476-
return nullptr;
1477-
1478-
auto CD = dyn_cast_or_null<ClassDecl>(getExtendedNominal());
1479-
if (!CD)
1480-
return nullptr;
1481-
1482-
auto importedDecl = CD->getImportedObjCCategory(*categoryName);
1483-
if (!importedDecl)
1484-
return nullptr;
1485-
1486-
assert(importedDecl->hasClangNode() &&
1487-
"@interface imported as clang-node-less decl?");
1488-
return cast<clang::ObjCContainerDecl>(importedDecl->getClangDecl());
1489-
}
1490-
14911472
Optional<Identifier>
14921473
ExtensionDecl::getCategoryNameForObjCImplementation() const {
14931474
assert(isObjCImplementation());

lib/AST/DiagnosticEngine.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -644,10 +644,16 @@ static void formatDiagnosticArgument(StringRef Modifier,
644644
break;
645645

646646
case DiagnosticArgumentKind::Identifier:
647-
assert(Modifier.empty() && "Improper modifier for identifier argument");
648-
Out << FormatOpts.OpeningQuotationMark;
649-
Arg.getAsIdentifier().printPretty(Out);
650-
Out << FormatOpts.ClosingQuotationMark;
647+
if (Modifier == "select") {
648+
formatSelectionArgument(ModifierArguments, Args,
649+
Arg.getAsIdentifier() ? 1 : 0, FormatOpts,
650+
Out);
651+
} else {
652+
assert(Modifier.empty() && "Improper modifier for identifier argument");
653+
Out << FormatOpts.OpeningQuotationMark;
654+
Arg.getAsIdentifier().printPretty(Out);
655+
Out << FormatOpts.ClosingQuotationMark;
656+
}
651657
break;
652658

653659
case DiagnosticArgumentKind::ObjCSelector:

0 commit comments

Comments
 (0)