Skip to content

Commit cece98b

Browse files
committed
AST: Generalize ClassDecl::checkObjCAncestry() to ClassDecl::checkAncestry()
This replaces ClassDecl::hasObjCMembers() and some hand-coded walks.
1 parent a204997 commit cece98b

File tree

12 files changed

+89
-147
lines changed

12 files changed

+89
-147
lines changed

include/swift/AST/Decl.h

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ class alignas(1 << DeclAlignInBits) Decl {
542542
NumRequirementsInSignature : 16
543543
);
544544

545-
SWIFT_INLINE_BITFIELD(ClassDecl, NominalTypeDecl, 1+2+1+2+1+3+1+1+1+1,
545+
SWIFT_INLINE_BITFIELD(ClassDecl, NominalTypeDecl, 1+2+1+2+1+6+1+1+1,
546546
/// Whether this class requires all of its instance variables to
547547
/// have in-class initializers.
548548
RequiresStoredPropertyInits : 1,
@@ -563,12 +563,11 @@ class alignas(1 << DeclAlignInBits) Decl {
563563
/// control inserting the implicit destructor.
564564
HasDestructorDecl : 1,
565565

566-
/// Whether the class has @objc ancestry.
567-
ObjCKind : 3,
566+
/// Information about the class's ancestry.
567+
Ancestry : 6,
568568

569-
/// Whether this class has @objc members.
570-
HasObjCMembersComputed : 1,
571-
HasObjCMembers : 1,
569+
/// Whether we have computed the above field or not.
570+
AncestryComputed : 1,
572571

573572
HasMissingDesignatedInitializers : 1,
574573
HasMissingVTableEntries : 1
@@ -3545,21 +3544,33 @@ enum class ArtificialMainKind : uint8_t {
35453544
UIApplicationMain,
35463545
};
35473546

3548-
enum class ObjCClassKind : uint8_t {
3549-
/// Neither the class nor any of its superclasses are @objc.
3550-
NonObjC,
3551-
/// One of the superclasses is @objc but another superclass or the
3552-
/// class itself has generic parameters, so while it cannot be
3553-
/// directly represented in Objective-C, it has implicitly @objc
3554-
/// members.
3555-
ObjCMembers,
3556-
/// The top-level ancestor of this class is not @objc, but the
3557-
/// class itself is.
3558-
ObjCWithSwiftRoot,
3559-
/// The class is bona-fide @objc.
3560-
ObjC,
3547+
/// This is the base type for AncestryOptions. Each flag describes possible
3548+
/// interesting kinds of superclasses that a class may have.
3549+
enum class AncestryFlags : uint8_t {
3550+
/// The class or one of its superclasses is @objc.
3551+
ObjC = (1<<0),
3552+
3553+
/// The class or one of its superclasses is @objcMembers.
3554+
ObjCMembers = (1<<1),
3555+
3556+
/// The class or one of its superclasses is generic.
3557+
Generic = (1<<2),
3558+
3559+
/// The class or one of its superclasses is resilient.
3560+
Resilient = (1<<3),
3561+
3562+
/// The class or one of its superclasses has resilient metadata and is in a
3563+
/// different resilience domain.
3564+
ResilientOther = (1<<4),
3565+
3566+
/// The class or one of its superclasses is imported from Clang.
3567+
ClangImported = (1<<5),
35613568
};
35623569

3570+
/// Return type of ClassDecl::checkAncestry(). Describes a set of interesting
3571+
/// kinds of superclasses that a class may have.
3572+
using AncestryOptions = OptionSet<AncestryFlags>;
3573+
35633574
/// ClassDecl - This is the declaration of a class, for example:
35643575
///
35653576
/// class Complex { var R : Double, I : Double }
@@ -3589,8 +3600,6 @@ class ClassDecl final : public NominalTypeDecl {
35893600
friend class SuperclassTypeRequest;
35903601
friend class TypeChecker;
35913602

3592-
bool hasObjCMembersSlow();
3593-
35943603
public:
35953604
ClassDecl(SourceLoc ClassLoc, Identifier Name, SourceLoc NameLoc,
35963605
MutableArrayRef<TypeLoc> Inherited,
@@ -3749,18 +3758,13 @@ class ClassDecl final : public NominalTypeDecl {
37493758
Bits.ClassDecl.InheritsSuperclassInits = true;
37503759
}
37513760

3752-
/// Returns if this class has any @objc ancestors, or if it is directly
3753-
/// visible to Objective-C. The latter is a stronger condition which is only
3754-
/// true if the class does not have any generic ancestry.
3755-
ObjCClassKind checkObjCAncestry() const;
3756-
3757-
/// Returns if the class has implicitly @objc members. This is true if any
3758-
/// ancestor class has the @objcMembers attribute.
3759-
bool hasObjCMembers() const {
3760-
if (Bits.ClassDecl.HasObjCMembersComputed)
3761-
return Bits.ClassDecl.HasObjCMembers;
3761+
/// Walks the class hierarchy starting from this class, checking various
3762+
/// conditions.
3763+
AncestryOptions checkAncestry() const;
37623764

3763-
return const_cast<ClassDecl *>(this)->hasObjCMembersSlow();
3765+
/// Check if the class has ancestry of the given kind.
3766+
bool checkAncestry(AncestryFlags flag) const {
3767+
return checkAncestry().contains(flag);
37643768
}
37653769

37663770
/// The type of metaclass to use for a class.

lib/AST/Decl.cpp

Lines changed: 28 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3519,9 +3519,8 @@ ClassDecl::ClassDecl(SourceLoc ClassLoc, Identifier Name, SourceLoc NameLoc,
35193519
Bits.ClassDecl.InheritsSuperclassInits = 0;
35203520
Bits.ClassDecl.RawForeignKind = 0;
35213521
Bits.ClassDecl.HasDestructorDecl = 0;
3522-
Bits.ClassDecl.ObjCKind = 0;
3523-
Bits.ClassDecl.HasObjCMembersComputed = 0;
3524-
Bits.ClassDecl.HasObjCMembers = 0;
3522+
Bits.ClassDecl.Ancestry = 0;
3523+
Bits.ClassDecl.AncestryComputed = 0;
35253524
Bits.ClassDecl.HasMissingDesignatedInitializers = 0;
35263525
Bits.ClassDecl.HasMissingVTableEntries = 0;
35273526
}
@@ -3634,72 +3633,57 @@ bool ClassDecl::inheritsSuperclassInitializers(LazyResolver *resolver) {
36343633
return Bits.ClassDecl.InheritsSuperclassInits;
36353634
}
36363635

3637-
ObjCClassKind ClassDecl::checkObjCAncestry() const {
3636+
AncestryOptions ClassDecl::checkAncestry() const {
36383637
// See if we've already computed this.
3639-
if (Bits.ClassDecl.ObjCKind)
3640-
return ObjCClassKind(Bits.ClassDecl.ObjCKind - 1);
3638+
if (Bits.ClassDecl.AncestryComputed)
3639+
return AncestryOptions(Bits.ClassDecl.Ancestry);
36413640

36423641
llvm::SmallPtrSet<const ClassDecl *, 8> visited;
3643-
bool genericAncestry = false, isObjC = false;
3642+
3643+
AncestryOptions result;
36443644
const ClassDecl *CD = this;
3645+
auto *M = getParentModule();
36453646

3646-
for (;;) {
3647+
do {
36473648
// If we hit circularity, we will diagnose at some point in typeCheckDecl().
36483649
// However we have to explicitly guard against that here because we get
36493650
// called as part of validateDecl().
36503651
if (!visited.insert(CD).second)
36513652
break;
36523653

36533654
if (CD->isGenericContext())
3654-
genericAncestry = true;
3655+
result |= AncestryFlags::Generic;
36553656

3656-
// Is this class @objc? For the current class, only look at the attribute
3657-
// to avoid cycles; for superclasses, compute @objc completely.
3658-
if ((CD == this && CD->getAttrs().hasAttribute<ObjCAttr>()) ||
3659-
(CD != this && CD->isObjC()))
3660-
isObjC = true;
3657+
// Note: it's OK to check for @objc explicitly instead of calling isObjC()
3658+
// to infer it since we're going to visit every superclass.
3659+
if (CD->getAttrs().hasAttribute<ObjCAttr>())
3660+
result |= AncestryFlags::ObjC;
36613661

3662-
if (!CD->hasSuperclass())
3663-
break;
3664-
CD = CD->getSuperclassDecl();
3665-
// If we don't have a valid class here, we should have diagnosed
3666-
// elsewhere.
3667-
if (!CD)
3668-
break;
3669-
}
3662+
if (CD->getAttrs().hasAttribute<ObjCMembersAttr>())
3663+
result |= AncestryFlags::ObjCMembers;
36703664

3671-
ObjCClassKind kind = ObjCClassKind::ObjC;
3672-
if (!isObjC)
3673-
kind = ObjCClassKind::NonObjC;
3674-
else if (genericAncestry)
3675-
kind = ObjCClassKind::ObjCMembers;
3676-
else if (CD == this || !CD->isObjC())
3677-
kind = ObjCClassKind::ObjCWithSwiftRoot;
3665+
if (CD->hasClangNode())
3666+
result |= AncestryFlags::ClangImported;
36783667

3679-
// Save the result for later.
3680-
const_cast<ClassDecl *>(this)->Bits.ClassDecl.ObjCKind
3681-
= unsigned(kind) + 1;
3682-
return kind;
3683-
}
3668+
if (CD->hasResilientMetadata())
3669+
result |= AncestryFlags::Resilient;
36843670

3685-
bool ClassDecl::hasObjCMembersSlow() {
3686-
// Don't attempt to calculate this again.
3687-
Bits.ClassDecl.HasObjCMembersComputed = true;
3671+
if (CD->hasResilientMetadata(M, ResilienceExpansion::Maximal))
3672+
result |= AncestryFlags::ResilientOther;
36883673

3689-
bool result = false;
3690-
if (getAttrs().hasAttribute<ObjCMembersAttr>())
3691-
result = true;
3692-
else if (auto *superclassDecl = getSuperclassDecl())
3693-
result = superclassDecl->hasObjCMembers();
3674+
CD = CD->getSuperclassDecl();
3675+
} while (CD != nullptr);
36943676

3695-
Bits.ClassDecl.HasObjCMembers = result;
3677+
// Save the result for later.
3678+
const_cast<ClassDecl *>(this)->Bits.ClassDecl.Ancestry = result.toRaw();
3679+
const_cast<ClassDecl *>(this)->Bits.ClassDecl.AncestryComputed = 1;
36963680
return result;
36973681
}
36983682

36993683
ClassDecl::MetaclassKind ClassDecl::getMetaclassKind() const {
37003684
assert(getASTContext().LangOpts.EnableObjCInterop &&
37013685
"querying metaclass kind without objc interop");
3702-
auto objc = checkObjCAncestry() != ObjCClassKind::NonObjC;
3686+
auto objc = checkAncestry(AncestryFlags::ObjC);
37033687
return objc ? MetaclassKind::ObjC : MetaclassKind::SwiftStub;
37043688
}
37053689

lib/AST/Type.cpp

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3950,13 +3950,9 @@ bool UnownedStorageType::isLoadable(ResilienceExpansion resilience) const {
39503950
}
39513951

39523952
static ReferenceCounting getClassReferenceCounting(ClassDecl *theClass) {
3953-
while (auto superclass = theClass->getSuperclassDecl()) {
3954-
theClass = superclass;
3955-
}
3956-
3957-
return theClass->hasClangNode()
3958-
? ReferenceCounting::ObjC
3959-
: ReferenceCounting::Native;
3953+
return (theClass->checkAncestry(AncestryFlags::ClangImported)
3954+
? ReferenceCounting::ObjC
3955+
: ReferenceCounting::Native);
39603956
}
39613957

39623958
ReferenceCounting TypeBase::getReferenceCounting() {

lib/IRGen/GenHeap.cpp

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1883,14 +1883,6 @@ llvm::Value *irgen::emitDynamicTypeOfHeapObject(IRGenFunction &IGF,
18831883
llvm_unreachable("unhandled ISA encoding");
18841884
}
18851885

1886-
static ClassDecl *getRootClass(ClassDecl *theClass) {
1887-
while (theClass->hasSuperclass()) {
1888-
theClass = theClass->getSuperclassDecl();
1889-
assert(theClass && "base type of class not a class?");
1890-
}
1891-
return theClass;
1892-
}
1893-
18941886
/// What isa encoding mechanism does a type have?
18951887
IsaEncoding irgen::getIsaEncodingForType(IRGenModule &IGM,
18961888
CanType type) {
@@ -1900,7 +1892,7 @@ IsaEncoding irgen::getIsaEncodingForType(IRGenModule &IGM,
19001892

19011893
if (auto theClass = type->getClassOrBoundGenericClass()) {
19021894
// We can access the isas of pure Swift classes directly.
1903-
if (getRootClass(theClass)->hasKnownSwiftImplementation())
1895+
if (!theClass->checkAncestry(AncestryFlags::ClangImported))
19041896
return IsaEncoding::Pointer;
19051897
// For ObjC or mixed classes, we need to use object_getClass.
19061898
return IsaEncoding::ObjC;

lib/IRGen/GenMeta.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2481,7 +2481,8 @@ namespace {
24812481
case ClassMetadataStrategy::Update:
24822482
case ClassMetadataStrategy::FixedOrUpdate:
24832483
case ClassMetadataStrategy::Fixed: {
2484-
auto type = (Target->checkObjCAncestry() != ObjCClassKind::NonObjC
2484+
// FIXME: Should this check HasImported instead?
2485+
auto type = (Target->checkAncestry(AncestryFlags::ObjC)
24852486
? IGM.Context.TheUnknownObjectType
24862487
: IGM.Context.TheNativeObjectType);
24872488
auto wtable = IGM.getAddrOfValueWitnessTable(type);

lib/SILGen/SILGenConstructor.cpp

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -451,14 +451,9 @@ void SILGenFunction::emitEnumConstructor(EnumElementDecl *element) {
451451
}
452452

453453
bool Lowering::usesObjCAllocator(ClassDecl *theClass) {
454-
while (true) {
455-
// If the root class was implemented in Objective-C, use Objective-C's
456-
// allocation methods because they may have been overridden.
457-
if (!theClass->hasSuperclass())
458-
return theClass->hasClangNode();
459-
460-
theClass = theClass->getSuperclassDecl();
461-
}
454+
// If the root class was implemented in Objective-C, use Objective-C's
455+
// allocation methods because they may have been overridden.
456+
return theClass->checkAncestry(AncestryFlags::ClangImported);
462457
}
463458

464459
void SILGenFunction::emitClassConstructorAllocator(ConstructorDecl *ctor) {

lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,8 +283,7 @@ static bool isDefaultCaseKnown(ClassHierarchyAnalysis *CHA,
283283

284284
// If the class has an @objc ancestry it can be dynamically subclassed and we
285285
// can't therefore statically know the default case.
286-
auto Ancestry = CD->checkObjCAncestry();
287-
if (Ancestry != ObjCClassKind::NonObjC)
286+
if (CD->checkAncestry(AncestryFlags::ObjC))
288287
return false;
289288

290289
// Without an associated context we cannot perform any

lib/Sema/CodeSynthesis.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ static void maybeMarkTransparent(AccessorDecl *accessor, ASTContext &ctx) {
322322
// Accessors for classes with @objc ancestry are not @_transparent,
323323
// since they use a field offset variable which is not exported.
324324
if (auto *classDecl = dyn_cast<ClassDecl>(nominalDecl))
325-
if (classDecl->checkObjCAncestry() != ObjCClassKind::NonObjC)
325+
if (classDecl->checkAncestry(AncestryFlags::ObjC))
326326
return;
327327

328328
// Accessors synthesized on-demand are never transaprent.

lib/Sema/TypeCheckDecl.cpp

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2593,17 +2593,8 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
25932593
ArrayRef<VarDecl *> vars) {
25942594
// Check whether we have an Objective-C-defined class in our
25952595
// inheritance chain.
2596-
while (classDecl) {
2597-
// If we found an Objective-C-defined class, continue checking.
2598-
if (classDecl->hasClangNode())
2599-
break;
2600-
2601-
// If we ran out of superclasses, we're done.
2602-
if (!classDecl->hasSuperclass())
2603-
return false;
2604-
2605-
classDecl = classDecl->getSuperclassDecl();
2606-
}
2596+
if (!classDecl->checkAncestry(AncestryFlags::ClangImported))
2597+
return false;
26072598

26082599
// If all of the variables are @objc, we can use @NSManaged.
26092600
for (auto var : vars) {

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -993,17 +993,17 @@ static bool isMemberOfObjCMembersClass(const ValueDecl *VD) {
993993
auto classDecl = VD->getDeclContext()->getSelfClassDecl();
994994
if (!classDecl) return false;
995995

996-
return classDecl->hasObjCMembers();
996+
return classDecl->checkAncestry(AncestryFlags::ObjCMembers);
997997
}
998998

999999
// A class is @objc if it does not have generic ancestry, and it either has
10001000
// an explicit @objc attribute, or its superclass is @objc.
10011001
static Optional<ObjCReason> shouldMarkClassAsObjC(const ClassDecl *CD) {
10021002
ASTContext &ctx = CD->getASTContext();
1003-
ObjCClassKind kind = CD->checkObjCAncestry();
1003+
auto ancestry = CD->checkAncestry();
10041004

10051005
if (auto attr = CD->getAttrs().getAttribute<ObjCAttr>()) {
1006-
if (kind == ObjCClassKind::ObjCMembers) {
1006+
if (ancestry.contains(AncestryFlags::Generic)) {
10071007
if (attr->hasName() && !CD->isGenericContext()) {
10081008
// @objc with a name on a non-generic subclass of a generic class is
10091009
// just controlling the runtime name. Don't diagnose this case.
@@ -1018,7 +1018,8 @@ static Optional<ObjCReason> shouldMarkClassAsObjC(const ClassDecl *CD) {
10181018

10191019
// Only allow ObjC-rooted classes to be @objc.
10201020
// (Leave a hole for test cases.)
1021-
if (kind == ObjCClassKind::ObjCWithSwiftRoot) {
1021+
if (ancestry.contains(AncestryFlags::ObjC) &&
1022+
!ancestry.contains(AncestryFlags::ClangImported)) {
10221023
if (ctx.LangOpts.EnableObjCAttrRequiresFoundation)
10231024
ctx.Diags.diagnose(attr->getLocation(),
10241025
diag::invalid_objc_swift_rooted_class)
@@ -1031,9 +1032,10 @@ static Optional<ObjCReason> shouldMarkClassAsObjC(const ClassDecl *CD) {
10311032
return ObjCReason(ObjCReason::ExplicitlyObjC);
10321033
}
10331034

1034-
if (kind == ObjCClassKind::ObjCWithSwiftRoot ||
1035-
kind == ObjCClassKind::ObjC)
1035+
if (ancestry.contains(AncestryFlags::ObjC) &&
1036+
!ancestry.contains(AncestryFlags::Generic)) {
10361037
return ObjCReason(ObjCReason::ImplicitlyObjC);
1038+
}
10371039

10381040
return None;
10391041
}
@@ -1214,9 +1216,8 @@ Optional<ObjCReason> shouldMarkAsObjC(const ValueDecl *VD, bool allowImplicit) {
12141216
if (classDecl->isForeign())
12151217
return None;
12161218

1217-
if (classDecl->checkObjCAncestry() != ObjCClassKind::NonObjC) {
1219+
if (classDecl->checkAncestry(AncestryFlags::ObjC))
12181220
return ObjCReason(ObjCReason::MemberOfObjCSubclass);
1219-
}
12201221
}
12211222

12221223
return None;

0 commit comments

Comments
 (0)