Skip to content

Commit ba1ec90

Browse files
committed
Generate IR for @_objcImpls
This commit begins to generate correct metadata for @_objcImplementation extensions: • Swift-specific metadata and symbols are not generated. • For main-class @_objcImpls, we visit the class to emit metadata, but visit the extension’s members. • Includes both IR tests and executable tests, including coverage of same-module @objc subclasses, different-module @objc subclasses, and clang subclasses. The test cases do not yet cover stored properties.
1 parent ecf0ee6 commit ba1ec90

18 files changed

+555
-38
lines changed

include/swift/AST/TypeMemberVisitor.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,16 @@ class TypeMemberVisitor : public DeclVisitor<ImplClass, RetTy> {
5959
asImpl().visit(member);
6060
}
6161
}
62+
63+
/// A convenience method to visit all the members in the implementation
64+
/// context.
65+
///
66+
/// \seealso IterableDeclContext::getImplementationContext()
67+
void visitImplementationMembers(NominalTypeDecl *D) {
68+
for (Decl *member : D->getImplementationContext()->getMembers()) {
69+
asImpl().visit(member);
70+
}
71+
}
6272
};
6373

6474
template<typename ImplClass, typename RetTy = void>
@@ -70,6 +80,10 @@ class ClassMemberVisitor : public TypeMemberVisitor<ImplClass, RetTy> {
7080
void visitMembers(ClassDecl *D) {
7181
TypeMemberVisitor<ImplClass, RetTy>::visitMembers(D);
7282
}
83+
84+
void visitImplementationMembers(ClassDecl *D) {
85+
TypeMemberVisitor<ImplClass, RetTy>::visitImplementationMembers(D);
86+
}
7387
};
7488

7589
#undef BAD_MEMBER

include/swift/IRGen/Linking.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,7 @@ class LinkEntity {
810810

811811
static LinkEntity forTypeMetadata(CanType concreteType,
812812
TypeMetadataAddress addr) {
813+
assert(!isObjCImplementation(concreteType));
813814
LinkEntity entity;
814815
entity.setForType(Kind::TypeMetadata, concreteType);
815816
entity.Data |= LINKENTITY_SET_FIELD(MetadataAddress, unsigned(addr));
@@ -872,12 +873,14 @@ class LinkEntity {
872873
}
873874

874875
static LinkEntity forNominalTypeDescriptor(NominalTypeDecl *decl) {
876+
assert(!isObjCImplementation(decl));
875877
LinkEntity entity;
876878
entity.setForDecl(Kind::NominalTypeDescriptor, decl);
877879
return entity;
878880
}
879881

880882
static LinkEntity forNominalTypeDescriptorRecord(NominalTypeDecl *decl) {
883+
assert(!isObjCImplementation(decl));
881884
LinkEntity entity;
882885
entity.setForDecl(Kind::NominalTypeDescriptorRecord, decl);
883886
return entity;
@@ -1553,6 +1556,16 @@ class LinkEntity {
15531556
bool isAlwaysSharedLinkage() const;
15541557
#undef LINKENTITY_GET_FIELD
15551558
#undef LINKENTITY_SET_FIELD
1559+
1560+
private:
1561+
static bool isObjCImplementation(NominalTypeDecl *NTD) {
1562+
if (NTD)
1563+
return NTD->getObjCImplementationDecl();
1564+
return false;
1565+
}
1566+
static bool isObjCImplementation(CanType ty) {
1567+
return isObjCImplementation(ty->getClassOrBoundGenericClass());
1568+
}
15561569
};
15571570

15581571
struct IRLinkage {

lib/IRGen/ClassLayout.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,11 @@ enum class ClassMetadataFlags {
9494
/// or the field offset vector in the metadata, and the Objective-C runtime
9595
/// will slide offsets based on the actual superclass size, which is not
9696
/// known at compile time.
97-
ClassHasObjCAncestry = (1 << 6)
97+
ClassHasObjCAncestry = (1 << 6),
98+
99+
/// Is the class implemented by an \c @_objcImplementation extension? If so,
100+
/// we should generate pure ObjC-compatible metadata.
101+
ClassHasObjCImplementation = (1 << 7)
98102
};
99103

100104
using ClassMetadataOptions = OptionSet<ClassMetadataFlags>;
@@ -211,6 +215,12 @@ class ClassLayout {
211215

212216
return std::make_pair(AllFieldAccesses[index], AllElements[index]);
213217
}
218+
219+
/// Returns true if the class is implemented by an \c @_objcImplementation
220+
/// extension, and therefore should not have any Swift-specific metadata.
221+
bool hasObjCImplementation() const {
222+
return Options.contains(ClassMetadataFlags::ClassHasObjCImplementation);
223+
}
214224
};
215225

216226
} // end namespace irgen

lib/IRGen/GenClass.cpp

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@ namespace {
165165
auto theClass = classType.getClassOrBoundGenericClass();
166166
assert(theClass);
167167

168+
if (theClass->getObjCImplementationDecl())
169+
Options |= ClassMetadataFlags::ClassHasObjCImplementation;
170+
168171
if (theClass->isGenericContext() && !theClass->hasClangNode())
169172
Options |= ClassMetadataFlags::ClassIsGeneric;
170173

@@ -1116,6 +1119,15 @@ namespace {
11161119
return pair.second;
11171120
}
11181121

1122+
Optional<StringRef> getObjCImplCategoryName() const {
1123+
if (!TheExtension || !TheExtension->isObjCImplementation())
1124+
return None;
1125+
if (auto ident = TheExtension->getCategoryNameForObjCImplementation()) {
1126+
assert(!ident->empty());
1127+
return ident->str();
1128+
}
1129+
return None;
1130+
}
11191131
bool isBuildingClass() const {
11201132
return TheEntity.isa<ClassUnion>() && !TheExtension;
11211133
}
@@ -1210,11 +1222,11 @@ namespace {
12101222
const ClassLayout &fieldLayout)
12111223
: IGM(IGM), TheEntity(theUnion), TheExtension(nullptr),
12121224
FieldLayout(&fieldLayout) {
1213-
visitConformances(getClass());
1225+
visitConformances(getClass()->getImplementationContext());
12141226

12151227
if (getClass()->isRootDefaultActor())
12161228
Ivars.push_back(Field::DefaultActorStorage);
1217-
visitMembers(getClass());
1229+
visitImplementationMembers(getClass());
12181230

12191231
if (Lowering::usesObjCAllocator(getClass())) {
12201232
addIVarInitializer();
@@ -1228,9 +1240,9 @@ namespace {
12281240
FieldLayout(nullptr) {
12291241
buildCategoryName(CategoryName);
12301242

1231-
visitConformances(theExtension);
1243+
visitConformances(theExtension->getImplementationContext());
12321244

1233-
for (Decl *member : TheExtension->getMembers())
1245+
for (Decl *member : TheExtension->getImplementationContext()->getMembers())
12341246
visit(member);
12351247
}
12361248

@@ -1379,6 +1391,12 @@ namespace {
13791391
private:
13801392
void buildCategoryName(SmallVectorImpl<char> &s) {
13811393
llvm::raw_svector_ostream os(s);
1394+
1395+
if (auto implementationCategoryName = getObjCImplCategoryName()) {
1396+
os << *implementationCategoryName;
1397+
return;
1398+
}
1399+
13821400
// Find the module the extension is declared in.
13831401
ModuleDecl *TheModule = TheExtension->getParentModule();
13841402

@@ -1392,7 +1410,10 @@ namespace {
13921410
llvm::Constant *getClassMetadataRef() {
13931411
auto *theClass = getClass();
13941412

1395-
if (theClass->hasClangNode())
1413+
// If this is truly an imported ObjC class, with no @_objcImplementation,
1414+
// someone else will emit the ObjC metadata symbol and we simply want to
1415+
// use it.
1416+
if (theClass->hasClangNode() && !theClass->getObjCImplementationDecl())
13961417
return IGM.getAddrOfObjCClass(theClass, NotForDefinition);
13971418

13981419
// Note that getClassMetadataStrategy() will return

lib/IRGen/GenDecl.cpp

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4731,8 +4731,11 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(
47314731
SmallVector<std::pair<Size, SILDeclRef>, 8> vtableEntries) {
47324732
assert(init);
47334733

4734-
auto isPrespecialized = concreteType->getAnyGeneric() &&
4735-
concreteType->getAnyGeneric()->isGenericContext();
4734+
auto concreteTypeDecl = concreteType->getAnyGeneric();
4735+
auto isPrespecialized = concreteTypeDecl &&
4736+
concreteTypeDecl->isGenericContext();
4737+
bool isObjCImpl = concreteTypeDecl &&
4738+
concreteTypeDecl->getObjCImplementationDecl();
47364739

47374740
if (isPattern) {
47384741
assert(isConstant && "Type metadata patterns must be constant");
@@ -4748,8 +4751,11 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(
47484751
*this, concreteType))
47494752
? LinkEntity::forNoncanonicalSpecializedGenericTypeMetadata(
47504753
concreteType)
4751-
: LinkEntity::forTypeMetadata(concreteType,
4752-
TypeMetadataAddress::FullMetadata);
4754+
: (isObjCImpl
4755+
? LinkEntity::forObjCClass(
4756+
concreteType->getClassOrBoundGenericClass())
4757+
: LinkEntity::forTypeMetadata(
4758+
concreteType, TypeMetadataAddress::FullMetadata));
47534759

47544760
auto DbgTy = DebugTypeInfo::getMetadata(MetatypeType::get(concreteType),
47554761
entity.getDefaultDeclarationType(*this)->getPointerTo(),
@@ -4777,12 +4783,14 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(
47774783
unsigned adjustmentIndex = MetadataAdjustmentIndex::ValueType;
47784784

47794785
if (auto nominal = concreteType->getAnyNominal()) {
4780-
// Keep type metadata around for all types.
4781-
addRuntimeResolvableType(nominal);
4782-
4783-
// Don't define the alias for foreign type metadata or prespecialized
4784-
// generic metadata, since neither is ABI.
4785-
if (requiresForeignTypeMetadata(nominal) || isPrespecialized)
4786+
// Keep type metadata around for all types (except @_objcImplementation,
4787+
// since we're using ObjC metadata for that).
4788+
if (!isObjCImpl)
4789+
addRuntimeResolvableType(nominal);
4790+
4791+
// Don't define the alias for foreign type metadata, prespecialized
4792+
// generic metadata, or @_objcImplementation classes, since they're not ABI.
4793+
if (requiresForeignTypeMetadata(nominal) || isPrespecialized || isObjCImpl)
47864794
return var;
47874795

47884796
// Native Swift class metadata has a destructor before the address point.
@@ -4842,9 +4850,12 @@ IRGenModule::getAddrOfTypeMetadata(CanType concreteType,
48424850
} else if (nominal) {
48434851
// The symbol for native non-generic nominal type metadata is generated at
48444852
// the aliased address point (see defineTypeMetadata() above).
4845-
assert(!nominal->hasClangNode());
4846-
4847-
defaultVarTy = TypeMetadataStructTy;
4853+
if (nominal->getObjCImplementationDecl()) {
4854+
defaultVarTy = ObjCClassStructTy;
4855+
} else {
4856+
assert(!nominal->hasClangNode());
4857+
defaultVarTy = TypeMetadataStructTy;
4858+
}
48484859
adjustmentIndex = 0;
48494860
} else {
48504861
// FIXME: Non-nominal metadata provided by the C++ runtime is exported
@@ -4874,11 +4885,17 @@ IRGenModule::getAddrOfTypeMetadata(CanType concreteType,
48744885
DebugTypeInfo DbgTy;
48754886

48764887
switch (canonicality) {
4877-
case TypeMetadataCanonicality::Canonical:
4878-
entity = LinkEntity::forTypeMetadata(
4879-
concreteType, fullMetadata ? TypeMetadataAddress::FullMetadata
4880-
: TypeMetadataAddress::AddressPoint);
4888+
case TypeMetadataCanonicality::Canonical: {
4889+
auto classDecl = concreteType->getClassOrBoundGenericClass();
4890+
if (classDecl && classDecl->getObjCImplementationDecl()) {
4891+
entity = LinkEntity::forObjCClass(classDecl);
4892+
} else {
4893+
entity = LinkEntity::forTypeMetadata(
4894+
concreteType, fullMetadata ? TypeMetadataAddress::FullMetadata
4895+
: TypeMetadataAddress::AddressPoint);
4896+
}
48814897
break;
4898+
}
48824899
case TypeMetadataCanonicality::Noncanonical:
48834900
entity =
48844901
LinkEntity::forNoncanonicalSpecializedGenericTypeMetadata(concreteType);
@@ -5340,6 +5357,11 @@ void IRGenModule::emitNestedTypeDecls(DeclRange members) {
53405357
}
53415358

53425359
static bool shouldEmitCategory(IRGenModule &IGM, ExtensionDecl *ext) {
5360+
if (ext->isObjCImplementation()) {
5361+
assert(ext->getCategoryNameForObjCImplementation() != Identifier());
5362+
return true;
5363+
}
5364+
53435365
for (auto conformance : ext->getLocalConformances()) {
53445366
if (conformance->getProtocol()->isObjC())
53455367
return true;
@@ -5376,7 +5398,12 @@ void IRGenModule::emitExtension(ExtensionDecl *ext) {
53765398
if (!origClass)
53775399
return;
53785400

5379-
if (shouldEmitCategory(*this, ext)) {
5401+
if (ext->isObjCImplementation()
5402+
&& ext->getCategoryNameForObjCImplementation() == Identifier()) {
5403+
// This is the @_objcImplementation for the class--generate its class
5404+
// metadata.
5405+
emitClassDecl(origClass);
5406+
} else if (shouldEmitCategory(*this, ext)) {
53805407
assert(origClass && !origClass->isForeign() &&
53815408
"foreign types cannot have categories emitted");
53825409
llvm::Constant *category = emitCategoryData(*this, ext);

0 commit comments

Comments
 (0)