Skip to content

Commit e05b958

Browse files
authored
Merge pull request #64790 from beccadax/checkmate-ii-5.9
[5.9] Re-merge #63668: Improve @objcImplementation member checking
2 parents 0fb7f8f + ef8debf commit e05b958

20 files changed

+1053
-144
lines changed

include/swift/AST/Decl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1641,6 +1641,10 @@ class ExtensionDecl final : public GenericContext, public Decl,
16411641
/// \c isObjCImplementation() returns \c true.
16421642
Optional<Identifier> getCategoryNameForObjCImplementation() const;
16431643

1644+
/// If this extension represents an imported Objective-C category, returns the
1645+
/// category's name. Otherwise returns the empty identifier.
1646+
Identifier getObjCCategoryName() const;
1647+
16441648
// Implement isa/cast/dyncast/etc.
16451649
static bool classof(const Decl *D) {
16461650
return D->getKind() == DeclKind::Extension;

include/swift/AST/DiagnosticsSema.def

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1602,14 +1602,56 @@ ERROR(member_of_objc_implementation_not_objc_or_final,none,
16021602
"%0 %1 does not match any %0 declared in the headers for %2; did you use "
16031603
"the %0's Swift name?",
16041604
(DescriptiveDeclKind, ValueDecl *, ValueDecl *))
1605-
NOTE(fixit_add_objc_for_objc_implementation,none,
1606-
"add '@objc' to define an Objective-C-compatible %0 not declared in "
1607-
"the header",
1605+
NOTE(fixit_add_private_for_objc_implementation,none,
1606+
"add 'private' or 'fileprivate' to define an Objective-C-compatible %0 "
1607+
"not declared in the header",
16081608
(DescriptiveDeclKind))
16091609
NOTE(fixit_add_final_for_objc_implementation,none,
16101610
"add 'final' to define a Swift %0 that cannot be overridden",
16111611
(DescriptiveDeclKind))
16121612

1613+
ERROR(objc_implementation_wrong_category,none,
1614+
"%0 %1 should be implemented in extension for "
1615+
"%select{main class interface|category %2}2, not "
1616+
"%select{main class interface|category %3}3",
1617+
(DescriptiveDeclKind, ValueDecl *, Identifier, Identifier))
1618+
1619+
ERROR(objc_implementation_wrong_objc_name,none,
1620+
"selector %0 for %1 %2 not found in header; did you mean %3?",
1621+
(ObjCSelector, DescriptiveDeclKind, ValueDecl *, ObjCSelector))
1622+
1623+
ERROR(objc_implementation_wrong_swift_name,none,
1624+
"selector %0 used in header by an %1 with a different name; did you "
1625+
"mean %2?",
1626+
(ObjCSelector, DescriptiveDeclKind, DeclName))
1627+
1628+
ERROR(objc_implementation_missing_impl,none,
1629+
"extension for %select{main class interface|category %0}0 should "
1630+
"provide implementation for %1 %2",
1631+
(Identifier, DescriptiveDeclKind, ValueDecl *))
1632+
1633+
ERROR(objc_implementation_class_or_instance_mismatch,none,
1634+
"%0 %1 does not match %2 declared in header",
1635+
(DescriptiveDeclKind, ValueDecl *, DescriptiveDeclKind))
1636+
1637+
ERROR(objc_implementation_multiple_matching_candidates,none,
1638+
"found multiple implementations that could match %0 %1 with selector %2",
1639+
(DescriptiveDeclKind, ValueDecl *, ObjCSelector))
1640+
NOTE(objc_implementation_candidate_impl_here,none,
1641+
"%0 %1 is a potential match%select{|; insert '@objc(%3)' to use it}2",
1642+
(DescriptiveDeclKind, ValueDecl *, bool, StringRef))
1643+
NOTE(objc_implementation_requirement_here,none,
1644+
"%0 %1 declared in header here",
1645+
(DescriptiveDeclKind, ValueDecl *))
1646+
1647+
ERROR(objc_implementation_multiple_matching_requirements,none,
1648+
"%0 %1 could match several different members declared in the header",
1649+
(DescriptiveDeclKind, ValueDecl *))
1650+
NOTE(objc_implementation_one_matched_requirement,none,
1651+
"%0 %1 (with selector %2) is a potential match%select{|; insert "
1652+
"'@objc(%4)' to use it}3",
1653+
(DescriptiveDeclKind, ValueDecl *, ObjCSelector, bool, StringRef))
1654+
16131655
ERROR(cdecl_not_at_top_level,none,
16141656
"@_cdecl can only be applied to global functions", ())
16151657
ERROR(cdecl_empty_name,none,

include/swift/AST/TypeCheckRequests.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4166,6 +4166,28 @@ class LocalDiscriminatorsRequest
41664166
bool isCached() const { return true; }
41674167
};
41684168

4169+
/// Checks that all of a class's \c \@objcImplementation extensions provide
4170+
/// complete and correct implementations for their corresponding interfaces.
4171+
/// This is done on all of a class's implementations at once to improve diagnostics.
4172+
class TypeCheckObjCImplementationRequest
4173+
: public SimpleRequest<TypeCheckObjCImplementationRequest,
4174+
evaluator::SideEffect(ExtensionDecl *),
4175+
RequestFlags::Cached> {
4176+
public:
4177+
using SimpleRequest::SimpleRequest;
4178+
4179+
private:
4180+
friend SimpleRequest;
4181+
4182+
// Evaluation.
4183+
evaluator::SideEffect
4184+
evaluate(Evaluator &evaluator, ExtensionDecl *ED) const;
4185+
4186+
public:
4187+
// Separate caching.
4188+
bool isCached() const { return true; }
4189+
};
4190+
41694191
void simple_display(llvm::raw_ostream &out, ASTNode node);
41704192
void simple_display(llvm::raw_ostream &out, Type value);
41714193
void simple_display(llvm::raw_ostream &out, const TypeRepr *TyR);

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,3 +466,6 @@ SWIFT_REQUEST(TypeChecker, GetRuntimeDiscoverableAttributes,
466466
SWIFT_REQUEST(TypeChecker, LocalDiscriminatorsRequest,
467467
unsigned(DeclContext *),
468468
Cached, NoLocationInfo)
469+
SWIFT_REQUEST(TypeChecker, TypeCheckObjCImplementationRequest,
470+
unsigned(ExtensionDecl *),
471+
Cached, NoLocationInfo)

lib/AST/ASTPrinter.cpp

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ static bool contributesToParentTypeStorage(const AbstractStorageDecl *ASD) {
137137
return !ND->isResilient() && ASD->hasStorage() && !ASD->isStatic();
138138
}
139139

140+
static bool isInObjCImpl(const ValueDecl *VD) {
141+
auto *ED = dyn_cast<ExtensionDecl>(VD->getDeclContext());
142+
return ED && ED->isObjCImplementation();
143+
}
144+
140145
PrintOptions PrintOptions::printSwiftInterfaceFile(ModuleDecl *ModuleToPrint,
141146
bool preferTypeRepr,
142147
bool printFullConvention,
@@ -208,9 +213,9 @@ PrintOptions PrintOptions::printSwiftInterfaceFile(ModuleDecl *ModuleToPrint,
208213
if (!options.PrintSPIs && D->isSPI())
209214
return false;
210215

211-
// Skip anything that isn't 'public' or '@usableFromInline' or has a
212-
// _specialize attribute with a targetFunction parameter.
213216
if (auto *VD = dyn_cast<ValueDecl>(D)) {
217+
// Skip anything that isn't 'public' or '@usableFromInline' or has a
218+
// _specialize attribute with a targetFunction parameter.
214219
if (!isPublicOrUsableFromInline(VD) &&
215220
!isPrespecilizationDeclWithTarget(VD)) {
216221
// We do want to print private stored properties, without their
@@ -221,6 +226,13 @@ PrintOptions PrintOptions::printSwiftInterfaceFile(ModuleDecl *ModuleToPrint,
221226

222227
return false;
223228
}
229+
230+
// Skip member implementations and @objc overrides in @objcImpl
231+
// extensions.
232+
if (VD->isObjCMemberImplementation()
233+
|| (isInObjCImpl(VD) && VD->getOverriddenDecl() && VD->isObjC())) {
234+
return false;
235+
}
224236
}
225237

226238
// Skip extensions that extend things we wouldn't print.
@@ -312,6 +324,7 @@ PrintOptions PrintOptions::printSwiftInterfaceFile(ModuleDecl *ModuleToPrint,
312324
DAK_AccessControl,
313325
DAK_SetterAccess,
314326
DAK_Lazy,
327+
DAK_ObjCImplementation,
315328
DAK_StaticInitializeObjCMetadata,
316329
DAK_RestatedObjCConformance,
317330
DAK_NonSendable,
@@ -1125,8 +1138,10 @@ void PrintAST::printAttributes(const Decl *D) {
11251138

11261139
if (auto vd = dyn_cast<VarDecl>(D)) {
11271140
// Don't print @_hasInitialValue if we're printing an initializer
1128-
// expression or if the storage is resilient.
1129-
if (vd->isInitExposedToClients() || vd->isResilient())
1141+
// expression, if the storage is resilient, or if it's in an
1142+
// @objcImplementation extension (where final properties should appear
1143+
// computed).
1144+
if (vd->isInitExposedToClients() || vd->isResilient() || isInObjCImpl(vd))
11301145
Options.ExcludeAttrList.push_back(DAK_HasInitialValue);
11311146

11321147
if (!Options.PrintForSIL) {
@@ -2105,8 +2120,9 @@ void PrintAST::printAccessors(const AbstractStorageDecl *ASD) {
21052120
// Don't print accessors for trivially stored properties...
21062121
if (impl.isSimpleStored()) {
21072122
// ...unless we're printing for SIL, which expects a { get set? } on
2108-
// trivial properties
2109-
if (Options.PrintForSIL) {
2123+
// trivial properties, or in an @objcImpl extension, which treats
2124+
// final stored properties as computed.
2125+
if (Options.PrintForSIL || isInObjCImpl(ASD)) {
21102126
Printer << " { get " << (impl.supportsMutation() ? "set }" : "}");
21112127
}
21122128
// ...or you're private/internal(set), at which point we'll print

lib/AST/Decl.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5361,8 +5361,10 @@ synthesizeEmptyFunctionBody(AbstractFunctionDecl *afd, void *context) {
53615361

53625362
DestructorDecl *
53635363
GetDestructorRequest::evaluate(Evaluator &evaluator, ClassDecl *CD) const {
5364+
auto dc = CD->getImplementationContext();
5365+
53645366
auto &ctx = CD->getASTContext();
5365-
auto *DD = new (ctx) DestructorDecl(CD->getLoc(), CD);
5367+
auto *DD = new (ctx) DestructorDecl(CD->getLoc(), dc->getAsGenericContext());
53665368

53675369
DD->setImplicit();
53685370

lib/AST/NameLookupRequests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ Optional<DestructorDecl *> GetDestructorRequest::getCachedResult() const {
219219

220220
void GetDestructorRequest::cacheResult(DestructorDecl *value) const {
221221
auto *classDecl = std::get<0>(getStorage());
222-
classDecl->addMember(value);
222+
classDecl->getImplementationContext()->addMember(value);
223223
}
224224

225225
//----------------------------------------------------------------------------//

lib/ClangImporter/ClangImporter.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5183,20 +5183,20 @@ findImplsGivenInterface(ClassDecl *classDecl, Identifier categoryName) {
51835183
return impls;
51845184
}
51855185

5186-
static Identifier getCategoryName(ExtensionDecl *ext) {
5186+
Identifier ExtensionDecl::getObjCCategoryName() const {
51875187
// Could it be an imported category?
5188-
if (!ext || !ext->hasClangNode())
5188+
if (!hasClangNode())
51895189
// Nope, not imported.
51905190
return Identifier();
51915191

5192-
auto category = dyn_cast<clang::ObjCCategoryDecl>(ext->getClangDecl());
5192+
auto category = dyn_cast<clang::ObjCCategoryDecl>(getClangDecl());
51935193
if (!category)
51945194
// Nope, not a category.
51955195
return Identifier();
51965196

51975197
// We'll look for an implementation with this category name.
51985198
auto clangCategoryName = category->getName();
5199-
return ext->getASTContext().getIdentifier(clangCategoryName);
5199+
return getASTContext().getIdentifier(clangCategoryName);
52005200
}
52015201

52025202
static IterableDeclContext *
@@ -5246,7 +5246,7 @@ findContextInterfaceAndImplementation(DeclContext *dc) {
52465246

52475247
// Is this an imported category? If so, extract its name so we can look for
52485248
// implementations of that category.
5249-
categoryName = getCategoryName(ext);
5249+
categoryName = ext->getObjCCategoryName();
52505250
if (categoryName.empty())
52515251
return {};
52525252

lib/Sema/CodeSynthesis.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -937,6 +937,29 @@ static bool canInheritDesignatedInits(Evaluator &eval, ClassDecl *decl) {
937937
areAllStoredPropertiesDefaultInitializable(eval, decl);
938938
}
939939

940+
static ValueDecl *findImplementedObjCDecl(ValueDecl *VD) {
941+
// If VD has an ObjC name...
942+
if (auto vdSelector = VD->getObjCRuntimeName()) {
943+
// and it's in an extension...
944+
if (auto implED = dyn_cast<ExtensionDecl>(VD->getDeclContext())) {
945+
// and that extension is the @objcImplementation of a class's main body...
946+
if (auto interfaceCD =
947+
dyn_cast_or_null<ClassDecl>(implED->getImplementedObjCDecl())) {
948+
// Find the initializer in the class's main body that matches VD.
949+
for (auto interfaceVD : interfaceCD->getAllMembers()) {
950+
if (auto interfaceCtor = dyn_cast<ConstructorDecl>(interfaceVD)) {
951+
if (vdSelector == interfaceCtor->getObjCRuntimeName()) {
952+
return interfaceCtor;
953+
}
954+
}
955+
}
956+
}
957+
}
958+
}
959+
960+
return VD;
961+
}
962+
940963
static void collectNonOveriddenSuperclassInits(
941964
ClassDecl *subclass, SmallVectorImpl<ConstructorDecl *> &results) {
942965
auto *superclassDecl = subclass->getSuperclassDecl();
@@ -968,6 +991,15 @@ static void collectNonOveriddenSuperclassInits(
968991
subOptions, lookupResults);
969992

970993
for (auto decl : lookupResults) {
994+
// HACK: If an @objcImplementation extension declares an initializer, its
995+
// interface usually also has a declaration. We need the interface decl for
996+
// access control computations, but the name lookup returns the
997+
// implementation decl because it's in the Swift module. Go find the
998+
// matching interface decl.
999+
// (Note that this is necessary for both newly-declared inits and overrides,
1000+
// even implicit ones.)
1001+
decl = findImplementedObjCDecl(decl);
1002+
9711003
auto superclassCtor = cast<ConstructorDecl>(decl);
9721004

9731005
// Skip invalid superclass initializers.

0 commit comments

Comments
 (0)