Skip to content

Implement Swift support for Objective-C resilient class stubs [5.1] #23746

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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/ABI/Mangling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ Globals
global ::= nominal-type 'Mn' // nominal type descriptor
global ::= nominal-type 'Mu' // class method lookup function
global ::= nominal-type 'MU' // ObjC metadata update callback function
global ::= nominal-type 'Ms' // ObjC resilient class stub
global ::= nominal-type 'Mt' // Full ObjC resilient class stub (private)
global ::= module 'MXM' // module descriptor
global ::= context 'MXE' // extension descriptor
global ::= context 'MXX' // anonymous context descriptor
Expand Down
57 changes: 53 additions & 4 deletions include/swift/ABI/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -3683,6 +3683,28 @@ struct TargetResilientSuperclass {
TargetRelativeDirectPointer<Runtime, const void, /*nullable*/true> Superclass;
};

/// A structure that stores a reference to an Objective-C class stub.
///
/// This is not the class stub itself; it is part of a class context
/// descriptor.
template <typename Runtime>
struct TargetObjCResilientClassStubInfo {
/// A relative pointer to an Objective-C resilient class stub.
///
/// We do not declare a struct type for class stubs since the Swift runtime
/// does not need to interpret them. The class stub struct is part of
/// the Objective-C ABI, and is laid out as follows:
/// - isa pointer, always 1
/// - an update callback, of type 'Class (*)(Class *, objc_class_stub *)'
///
/// Class stubs are used for two purposes:
///
/// - Objective-C can reference class stubs when calling static methods.
/// - Objective-C and Swift can reference class stubs when emitting
/// categories (in Swift, extensions with @objc members).
TargetRelativeDirectPointer<Runtime, const void> Stub;
};

template <typename Runtime>
class TargetClassDescriptor final
: public TargetTypeContextDescriptor<Runtime>,
Expand All @@ -3695,7 +3717,8 @@ class TargetClassDescriptor final
TargetVTableDescriptorHeader<Runtime>,
TargetMethodDescriptor<Runtime>,
TargetOverrideTableHeader<Runtime>,
TargetMethodOverrideDescriptor<Runtime>> {
TargetMethodOverrideDescriptor<Runtime>,
TargetObjCResilientClassStubInfo<Runtime>> {
private:
using TrailingGenericContextObjects =
TrailingGenericContextObjects<TargetClassDescriptor<Runtime>,
Expand All @@ -3706,7 +3729,8 @@ class TargetClassDescriptor final
TargetVTableDescriptorHeader<Runtime>,
TargetMethodDescriptor<Runtime>,
TargetOverrideTableHeader<Runtime>,
TargetMethodOverrideDescriptor<Runtime>>;
TargetMethodOverrideDescriptor<Runtime>,
TargetObjCResilientClassStubInfo<Runtime>>;

using TrailingObjects =
typename TrailingGenericContextObjects::TrailingObjects;
Expand All @@ -3722,6 +3746,8 @@ class TargetClassDescriptor final
TargetForeignMetadataInitialization<Runtime>;
using SingletonMetadataInitialization =
TargetSingletonMetadataInitialization<Runtime>;
using ObjCResilientClassStubInfo =
TargetObjCResilientClassStubInfo<Runtime>;

using StoredPointer = typename Runtime::StoredPointer;
using StoredPointerDifference = typename Runtime::StoredPointerDifference;
Expand Down Expand Up @@ -3759,7 +3785,9 @@ class TargetClassDescriptor final
/// positive size of metadata objects of this class (in words).
uint32_t MetadataPositiveSizeInWords;

// Maybe add something here that's useful only for resilient types?
/// Otherwise, these flags are used to do things like indicating
/// the presence of an Objective-C resilient class stub.
ExtraClassDescriptorFlags ExtraClassFlags;
};

/// The number of additional members added by this class to the class
Expand Down Expand Up @@ -3835,6 +3863,10 @@ class TargetClassDescriptor final
return getOverrideTable()->NumEntries;
}

size_t numTrailingObjects(OverloadToken<ObjCResilientClassStubInfo>) const {
return hasObjCResilientClassStub() ? 1 : 0;
}

public:
const TargetRelativeDirectPointer<Runtime, const void, /*nullable*/true> &
getResilientSuperclass() const {
Expand Down Expand Up @@ -3954,7 +3986,24 @@ class TargetClassDescriptor final
&& i < numTrailingObjects(OverloadToken<MethodDescriptor>{}));
return getMethodDescriptors()[i].Impl.get();
}


/// Whether this context descriptor references an Objective-C resilient
/// class stub. See the above description of TargetObjCResilientClassStubInfo
/// for details.
bool hasObjCResilientClassStub() const {
if (!hasResilientSuperclass())
return false;
return ExtraClassFlags.hasObjCResilientClassStub();
}

const void *getObjCResilientClassStub() const {
if (!hasObjCResilientClassStub())
return nullptr;

return this->template getTrailingObjects<ObjCResilientClassStubInfo>()
->Stub.get();
}

static bool classof(const TargetContextDescriptor<Runtime> *cd) {
return cd->getKind() == ContextDescriptorKind::Class;
}
Expand Down
22 changes: 22 additions & 0 deletions include/swift/ABI/MetadataValues.h
Original file line number Diff line number Diff line change
Expand Up @@ -1309,6 +1309,28 @@ class TypeContextDescriptorFlags : public FlagSet<uint16_t> {
class_setResilientSuperclassReferenceKind)
};

/// Extra flags for resilient classes, since we need more than 16 bits of
/// flags there.
class ExtraClassDescriptorFlags : public FlagSet<uint32_t> {
enum {
/// Set if the context descriptor includes a pointer to an Objective-C
/// resilient class stub structure. See the description of
/// TargetObjCResilientClassStubInfo in Metadata.h for details.
///
/// Only meaningful for class descriptors when Objective-C interop is
/// enabled.
HasObjCResilientClassStub = 0,
};

public:
explicit ExtraClassDescriptorFlags(uint32_t bits) : FlagSet(bits) {}
constexpr ExtraClassDescriptorFlags() {}

FLAGSET_DEFINE_FLAG_ACCESSORS(HasObjCResilientClassStub,
hasObjCResilientClassStub,
setObjCResilientClassStub)
};

/// Flags for protocol context descriptors. These values are used as the
/// kindSpecificFlags of the ContextDescriptorFlags for the protocol.
class ProtocolContextDescriptorFlags : public FlagSet<uint16_t> {
Expand Down
71 changes: 39 additions & 32 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,7 @@ class alignas(1 << DeclAlignInBits) Decl {
NumRequirementsInSignature : 16
);

SWIFT_INLINE_BITFIELD(ClassDecl, NominalTypeDecl, 1+2+1+2+1+3+1+1+1+1,
SWIFT_INLINE_BITFIELD(ClassDecl, NominalTypeDecl, 1+2+1+2+1+6+1+1+1,
/// Whether this class requires all of its instance variables to
/// have in-class initializers.
RequiresStoredPropertyInits : 1,
Expand All @@ -554,12 +554,11 @@ class alignas(1 << DeclAlignInBits) Decl {
/// control inserting the implicit destructor.
HasDestructorDecl : 1,

/// Whether the class has @objc ancestry.
ObjCKind : 3,
/// Information about the class's ancestry.
Ancestry : 6,

/// Whether this class has @objc members.
HasObjCMembersComputed : 1,
HasObjCMembers : 1,
/// Whether we have computed the above field or not.
AncestryComputed : 1,

HasMissingDesignatedInitializers : 1,
HasMissingVTableEntries : 1
Expand Down Expand Up @@ -3536,21 +3535,33 @@ enum class ArtificialMainKind : uint8_t {
UIApplicationMain,
};

enum class ObjCClassKind : uint8_t {
/// Neither the class nor any of its superclasses are @objc.
NonObjC,
/// One of the superclasses is @objc but another superclass or the
/// class itself has generic parameters, so while it cannot be
/// directly represented in Objective-C, it has implicitly @objc
/// members.
ObjCMembers,
/// The top-level ancestor of this class is not @objc, but the
/// class itself is.
ObjCWithSwiftRoot,
/// The class is bona-fide @objc.
ObjC,
/// This is the base type for AncestryOptions. Each flag describes possible
/// interesting kinds of superclasses that a class may have.
enum class AncestryFlags : uint8_t {
/// The class or one of its superclasses is @objc.
ObjC = (1<<0),

/// The class or one of its superclasses is @objcMembers.
ObjCMembers = (1<<1),

/// The class or one of its superclasses is generic.
Generic = (1<<2),

/// The class or one of its superclasses is resilient.
Resilient = (1<<3),

/// The class or one of its superclasses has resilient metadata and is in a
/// different resilience domain.
ResilientOther = (1<<4),

/// The class or one of its superclasses is imported from Clang.
ClangImported = (1<<5),
};

/// Return type of ClassDecl::checkAncestry(). Describes a set of interesting
/// kinds of superclasses that a class may have.
using AncestryOptions = OptionSet<AncestryFlags>;

/// ClassDecl - This is the declaration of a class, for example:
///
/// class Complex { var R : Double, I : Double }
Expand Down Expand Up @@ -3580,8 +3591,6 @@ class ClassDecl final : public NominalTypeDecl {
friend class SuperclassTypeRequest;
friend class TypeChecker;

bool hasObjCMembersSlow();

public:
ClassDecl(SourceLoc ClassLoc, Identifier Name, SourceLoc NameLoc,
MutableArrayRef<TypeLoc> Inherited,
Expand Down Expand Up @@ -3613,6 +3622,9 @@ class ClassDecl final : public NominalTypeDecl {
/// is no superclass.
ClassDecl *getSuperclassDecl() const;

/// Check if this class is a superclass or equal to the given class.
bool isSuperclassOf(ClassDecl *other) const;

/// Set the superclass of this class.
void setSuperclass(Type superclass);

Expand Down Expand Up @@ -3753,18 +3765,13 @@ class ClassDecl final : public NominalTypeDecl {
Bits.ClassDecl.InheritsSuperclassInits = true;
}

/// Returns if this class has any @objc ancestors, or if it is directly
/// visible to Objective-C. The latter is a stronger condition which is only
/// true if the class does not have any generic ancestry.
ObjCClassKind checkObjCAncestry() const;

/// Returns if the class has implicitly @objc members. This is true if any
/// ancestor class has the @objcMembers attribute.
bool hasObjCMembers() const {
if (Bits.ClassDecl.HasObjCMembersComputed)
return Bits.ClassDecl.HasObjCMembers;
/// Walks the class hierarchy starting from this class, checking various
/// conditions.
AncestryOptions checkAncestry() const;

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

/// The type of metaclass to use for a class.
Expand Down
12 changes: 9 additions & 3 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -3592,9 +3592,11 @@ ERROR(invalid_nonobjc_extension,none,
ERROR(objc_in_extension_context,none,
"members of constrained extensions cannot be declared @objc", ())
ERROR(objc_in_generic_extension,none,
"members of extensions of "
"%select{classes from generic context|generic classes}0 "
"cannot be declared @objc", (bool))
"extensions of %select{classes from generic context|generic classes}0 "
"cannot contain '@objc' members", (bool))
ERROR(objc_in_resilient_extension,none,
"extensions of classes built with library evolution support "
"cannot contain '@objc' members", ())
ERROR(objc_operator, none,
"operator methods cannot be declared @objc", ())
ERROR(objc_operator_proto, none,
Expand All @@ -3615,6 +3617,10 @@ NOTE(objc_inference_swift3_addnonobjc,none,
ERROR(objc_for_generic_class,none,
"generic subclasses of '@objc' classes cannot have an explicit '@objc' "
"because they are not directly visible from Objective-C", ())
ERROR(objc_for_resilient_class,none,
"classes built with library evolution support cannot have explicit "
"'@objc' subclasses because they are not directly "
"visible from Objective-C", ())
ERROR(objc_getter_for_nonobjc_property,none,
"'@objc' getter for non-'@objc' property", ())
ERROR(objc_getter_for_nonobjc_subscript,none,
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,10 @@ class ModuleDecl : public DeclContext, public TypeDecl {
Bits.ModuleDecl.RawResilienceStrategy = unsigned(strategy);
}

bool isResilient() const {
return getResilienceStrategy() != ResilienceStrategy::Default;
}

/// Look up a (possibly overloaded) value set at top-level scope
/// (but with the specified access path, which may come from an import decl)
/// within the current module.
Expand Down
5 changes: 5 additions & 0 deletions include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,11 @@ namespace swift {
/// set to true.
bool ExperimentalDependenciesIncludeIntrafileOnes = false;

/// Enable experimental support for emitting Objective-C resilient class
/// stubs. This is a language option since it also determines if we admit
/// @objc members in extensions of classes with resilient ancestry.
bool EnableObjCResilientClassStubs = false;

/// Sets the target we are building for and updates platform conditions
/// to match.
///
Expand Down
2 changes: 2 additions & 0 deletions include/swift/Demangling/DemangleNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ NODE(MetatypeRepresentation)
NODE(Metaclass)
NODE(MethodLookupFunction)
NODE(ObjCMetadataUpdateFunction)
NODE(ObjCResilientClassStub)
NODE(FullObjCResilientClassStub)
CONTEXT_NODE(ModifyAccessor)
CONTEXT_NODE(Module)
CONTEXT_NODE(NativeOwningAddressor)
Expand Down
25 changes: 19 additions & 6 deletions include/swift/IRGen/Linking.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class LinkEntity {
// This field appears in the ValueWitness kind.
ValueWitnessShift = 8, ValueWitnessMask = 0xFF00,

// This field appears in the TypeMetadata kind.
// This field appears in the TypeMetadata and ObjCResilientClassStub kinds.
MetadataAddressShift = 8, MetadataAddressMask = 0x0300,

// This field appears in associated type access functions.
Expand Down Expand Up @@ -174,6 +174,12 @@ class LinkEntity {
/// ClassMetadataStrategy::Update or ::FixedOrUpdate.
ObjCMetadataUpdateFunction,

/// A stub that we emit to allow Clang-generated code to statically refer
/// to Swift classes with resiliently-sized metadata, since the metadata
/// is not statically-emitted. Used when getClassMetadataStrategy() is
/// equal to ClassMetadataStrategy::Resilient.
ObjCResilientClassStub,

/// A class metadata base offset global variable. This stores the offset
/// of the immediate members of a class (generic parameters, field offsets,
/// vtable offsets) in the class's metadata. The immediate members begin
Expand Down Expand Up @@ -623,13 +629,19 @@ class LinkEntity {
return entity;
}

static LinkEntity forObjCResilientClassStub(ClassDecl *decl,
TypeMetadataAddress addr) {
LinkEntity entity;
entity.setForDecl(Kind::ObjCResilientClassStub, decl);
entity.Data |= LINKENTITY_SET_FIELD(MetadataAddress, unsigned(addr));
return entity;
}

static LinkEntity forTypeMetadata(CanType concreteType,
TypeMetadataAddress addr) {
LinkEntity entity;
entity.Pointer = concreteType.getPointer();
entity.SecondaryPointer = nullptr;
entity.Data = LINKENTITY_SET_FIELD(Kind, unsigned(Kind::TypeMetadata))
| LINKENTITY_SET_FIELD(MetadataAddress, unsigned(addr));
entity.setForType(Kind::TypeMetadata, concreteType);
entity.Data |= LINKENTITY_SET_FIELD(MetadataAddress, unsigned(addr));
return entity;
}

Expand Down Expand Up @@ -1031,7 +1043,8 @@ class LinkEntity {
return ValueWitness(LINKENTITY_GET_FIELD(Data, ValueWitness));
}
TypeMetadataAddress getMetadataAddress() const {
assert(getKind() == Kind::TypeMetadata);
assert(getKind() == Kind::TypeMetadata ||
getKind() == Kind::ObjCResilientClassStub);
return (TypeMetadataAddress)LINKENTITY_GET_FIELD(Data, MetadataAddress);
}
bool isForeignTypeMetadataCandidate() const {
Expand Down
4 changes: 4 additions & 0 deletions include/swift/Option/FrontendOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -558,4 +558,8 @@ def type_info_dump_filter_EQ : Joined<["-"], "type-info-dump-filter=">,
Flags<[FrontendOption]>,
HelpText<"One of 'all', 'resilient' or 'fragile'">;

def enable_objc_resilient_class_stubs : Flag<["-"], "enable-resilient-objc-class-stubs">,
HelpText<"Emit Objective-C resilient class stubs for classes with "
"resiliently-sized metadata">;

} // end let Flags = [FrontendOption, NoDriverOption, HelpHidden]
Loading