Skip to content

Commit a4aa054

Browse files
committed
IRGen: Make type(of:) behavior consistent in ObjC bridged contexts.
When we use type(of: x) on a class in an ObjC bridged context, the optimizer turns this into a SIL `value_metatype @objc` operation, which is supposed to get the dynamic type of the object as an ObjC class. This was previously lowered by IRGen into a `object_getClass` call, which extracts the isa pointer from the object, but is inconsistent with the `-class` method in ObjC or with the Swift-native behavior, which both look through artificial subclasses, proxies, and so on. This inconsistency led to observably different behavior between debug and release builds and between ObjC-bridged and native entry points, so provide an alternative runtime entry point that replicates the behavior of getting a native Swift class. Fixes SR-7258.
1 parent 88b785c commit a4aa054

File tree

10 files changed

+84
-45
lines changed

10 files changed

+84
-45
lines changed

include/swift/Runtime/Metadata.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4154,6 +4154,10 @@ swift_getObjCClassMetadata(const ClassMetadata *theClass);
41544154
SWIFT_RUNTIME_EXPORT
41554155
const ClassMetadata *
41564156
swift_getObjCClassFromMetadata(const Metadata *theClass);
4157+
4158+
SWIFT_RUNTIME_EXPORT
4159+
const ClassMetadata *
4160+
swift_getObjCClassFromObject(HeapObject *object);
41574161
#endif
41584162

41594163
/// \brief Fetch a unique type metadata object for a foreign type.

include/swift/Runtime/RuntimeFunctions.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,12 @@ FUNCTION(GetObjCClassFromMetadata, swift_getObjCClassFromMetadata, C_CC,
869869
ARGS(TypeMetadataPtrTy),
870870
ATTRS(NoUnwind, ReadNone))
871871

872+
// Metadata *swift_getObjCClassFromObject(id object);
873+
FUNCTION(GetObjCClassFromObject, swift_getObjCClassFromObject, C_CC,
874+
RETURNS(ObjCClassPtrTy),
875+
ARGS(ObjCPtrTy),
876+
ATTRS(NoUnwind, ReadNone))
877+
872878
// MetadataResponse swift_getTupleTypeMetadata(MetadataRequest request,
873879
// TupleTypeFlags flags,
874880
// Metadata * const *elts,

lib/IRGen/GenCast.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -718,7 +718,9 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF,
718718
}
719719
} else {
720720
// Get the type metadata for the instance.
721-
metadataValue = emitDynamicTypeOfHeapObject(IGF, value, srcType);
721+
metadataValue = emitDynamicTypeOfHeapObject(IGF, value,
722+
MetatypeRepresentation::Thick,
723+
srcType);
722724
}
723725

724726
// Look up witness tables for the protocols that need them.

lib/IRGen/GenExistential.cpp

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1881,15 +1881,8 @@ void irgen::emitMetatypeOfClassExistential(IRGenFunction &IGF, Explosion &value,
18811881
assert((IGF.IGM.ObjCInterop || repr != MetatypeRepresentation::ObjC) &&
18821882
"Class metatypes should not have ObjC representation without runtime");
18831883

1884-
if (repr == MetatypeRepresentation::Thick) {
1885-
auto dynamicType = emitDynamicTypeOfOpaqueHeapObject(IGF, instance);
1886-
out.add(dynamicType);
1887-
} else if (repr == MetatypeRepresentation::ObjC) {
1888-
auto dynamicType = emitHeapMetadataRefForUnknownHeapObject(IGF, instance);
1889-
out.add(dynamicType);
1890-
} else {
1891-
llvm_unreachable("Unknown metatype representation");
1892-
}
1884+
auto dynamicType = emitDynamicTypeOfOpaqueHeapObject(IGF, instance, repr);
1885+
out.add(dynamicType);
18931886

18941887
// Get the witness tables.
18951888
out.add(tablesAndValue.first);
@@ -1926,7 +1919,8 @@ irgen::emitClassExistentialProjection(IRGenFunction &IGF,
19261919
ArrayRef<llvm::Value*> wtables;
19271920
llvm::Value *value;
19281921
std::tie(wtables, value) = baseTI.getWitnessTablesAndValue(base);
1929-
auto metadata = emitDynamicTypeOfOpaqueHeapObject(IGF, value);
1922+
auto metadata = emitDynamicTypeOfOpaqueHeapObject(IGF, value,
1923+
MetatypeRepresentation::Thick);
19301924
IGF.bindArchetype(openedArchetype, metadata, MetadataState::Complete,
19311925
wtables);
19321926

@@ -2265,7 +2259,8 @@ Address irgen::emitOpaqueBoxedExistentialProjection(
22652259
IGF.getTypeInfo(existentialTy).as<ClassExistentialTypeInfo>();
22662260
auto valueAddr = baseTI.projectValue(IGF, base);
22672261
auto value = IGF.Builder.CreateLoad(valueAddr);
2268-
auto metadata = emitDynamicTypeOfOpaqueHeapObject(IGF, value);
2262+
auto metadata = emitDynamicTypeOfOpaqueHeapObject(IGF, value,
2263+
MetatypeRepresentation::Thick);
22692264

22702265
// If we are projecting into an opened archetype, capture the
22712266
// witness tables.

lib/IRGen/GenHeap.cpp

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1796,7 +1796,8 @@ llvm::Value *IRGenFunction::getLocalSelfMetadata() {
17961796
case ObjCMetatype:
17971797
return emitObjCMetadataRefForMetadata(*this, LocalSelf);
17981798
case ObjectReference:
1799-
return emitDynamicTypeOfOpaqueHeapObject(*this, LocalSelf);
1799+
return emitDynamicTypeOfOpaqueHeapObject(*this, LocalSelf,
1800+
MetatypeRepresentation::Thick);
18001801
}
18011802

18021803
llvm_unreachable("Not a valid LocalSelfKind.");
@@ -1916,11 +1917,26 @@ llvm::Value *irgen::emitHeapMetadataRefForHeapObject(IRGenFunction &IGF,
19161917
/// Given an opaque class instance pointer, produce the type metadata reference
19171918
/// as a %type*.
19181919
llvm::Value *irgen::emitDynamicTypeOfOpaqueHeapObject(IRGenFunction &IGF,
1919-
llvm::Value *object) {
1920+
llvm::Value *object,
1921+
MetatypeRepresentation repr) {
19201922
object = IGF.Builder.CreateBitCast(object, IGF.IGM.ObjCPtrTy);
1921-
auto metadata = IGF.Builder.CreateCall(IGF.IGM.getGetObjectTypeFn(),
1922-
object,
1923-
object->getName() + ".Type");
1923+
llvm::CallInst *metadata;
1924+
1925+
switch (repr) {
1926+
case MetatypeRepresentation::ObjC:
1927+
metadata = IGF.Builder.CreateCall(IGF.IGM.getGetObjCClassFromObjectFn(),
1928+
object,
1929+
object->getName() + ".Type");
1930+
break;
1931+
case MetatypeRepresentation::Thick:
1932+
metadata = IGF.Builder.CreateCall(IGF.IGM.getGetObjectTypeFn(),
1933+
object,
1934+
object->getName() + ".Type");
1935+
break;
1936+
case MetatypeRepresentation::Thin:
1937+
llvm_unreachable("class metadata can't be thin");
1938+
}
1939+
19241940
metadata->setDoesNotThrow();
19251941
metadata->setOnlyReadsMemory();
19261942
return metadata;
@@ -1944,18 +1960,18 @@ emitHeapMetadataRefForUnknownHeapObject(IRGenFunction &IGF,
19441960
/// as a %type*.
19451961
llvm::Value *irgen::emitDynamicTypeOfHeapObject(IRGenFunction &IGF,
19461962
llvm::Value *object,
1963+
MetatypeRepresentation repr,
19471964
SILType objectType,
19481965
bool suppressCast) {
1949-
// If it is known to have swift metadata, just load.
1966+
// If it is known to have swift metadata, just load. A swift class is both
1967+
// heap metadata and type metadata.
19501968
if (hasKnownSwiftMetadata(IGF.IGM, objectType.getSwiftRValueType())) {
19511969
return emitLoadOfHeapMetadataRef(IGF, object,
19521970
getIsaEncodingForType(IGF.IGM, objectType.getSwiftRValueType()),
19531971
suppressCast);
19541972
}
19551973

1956-
// Okay, ask the runtime for the type metadata of this
1957-
// potentially-ObjC object.
1958-
return emitDynamicTypeOfOpaqueHeapObject(IGF, object);
1974+
return emitDynamicTypeOfOpaqueHeapObject(IGF, object, repr);
19591975
}
19601976

19611977
static ClassDecl *getRootClass(ClassDecl *theClass) {

lib/IRGen/GenHeap.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,12 +143,14 @@ emitAllocateExistentialBoxInBuffer(IRGenFunction &IGF, SILType boxedType,
143143
/// Given an opaque class instance pointer, produce the type
144144
/// metadata reference as a %type*.
145145
llvm::Value *emitDynamicTypeOfOpaqueHeapObject(IRGenFunction &IGF,
146-
llvm::Value *object);
146+
llvm::Value *object,
147+
MetatypeRepresentation rep);
147148

148149
/// Given a heap-object instance, with some heap-object type,
149150
/// produce a reference to its type metadata.
150151
llvm::Value *emitDynamicTypeOfHeapObject(IRGenFunction &IGF,
151152
llvm::Value *object,
153+
MetatypeRepresentation rep,
152154
SILType objectType,
153155
bool suppressCast = false);
154156

lib/IRGen/GenProto.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,9 @@ bindParameterSource(SILParameterInfo param, unsigned paramIndex,
627627
llvm::Value *instanceRef = getParameter(paramIndex);
628628
SILType instanceType = SILType::getPrimitiveObjectType(paramType);
629629
llvm::Value *metadata =
630-
emitDynamicTypeOfHeapObject(IGF, instanceRef, instanceType);
630+
emitDynamicTypeOfHeapObject(IGF, instanceRef,
631+
MetatypeRepresentation::Thick,
632+
instanceType);
631633
IGF.bindLocalTypeDataFromTypeMetadata(paramType, IsInexact, metadata,
632634
MetadataState::Complete);
633635
return;
@@ -686,7 +688,9 @@ void BindPolymorphicParameter::emit(Explosion &nativeParam, unsigned paramIndex)
686688
llvm::Value *instanceRef = nativeParam.getAll()[0];
687689
SILType instanceType = SILType::getPrimitiveObjectType(paramType);
688690
llvm::Value *metadata =
689-
emitDynamicTypeOfHeapObject(IGF, instanceRef, instanceType);
691+
emitDynamicTypeOfHeapObject(IGF, instanceRef,
692+
MetatypeRepresentation::Thick,
693+
instanceType);
690694
IGF.bindLocalTypeDataFromTypeMetadata(paramType, IsInexact, metadata,
691695
MetadataState::Complete);
692696
}

lib/IRGen/IRGenSIL.cpp

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1914,24 +1914,6 @@ static llvm::Value *getClassBaseValue(IRGenSILFunction &IGF,
19141914
return e.claimNext();
19151915
}
19161916

1917-
static llvm::Value *getClassMetatype(IRGenFunction &IGF,
1918-
llvm::Value *baseValue,
1919-
MetatypeRepresentation repr,
1920-
SILType instanceType) {
1921-
switch (repr) {
1922-
case MetatypeRepresentation::Thin:
1923-
llvm_unreachable("Class metatypes are never thin");
1924-
1925-
case MetatypeRepresentation::Thick:
1926-
return emitDynamicTypeOfHeapObject(IGF, baseValue, instanceType);
1927-
1928-
case MetatypeRepresentation::ObjC:
1929-
return emitHeapMetadataRefForHeapObject(IGF, baseValue, instanceType);
1930-
}
1931-
1932-
llvm_unreachable("Not a valid MetatypeRepresentation.");
1933-
}
1934-
19351917
void IRGenSILFunction::visitValueMetatypeInst(swift::ValueMetatypeInst *i) {
19361918
SILType instanceTy = i->getOperand()->getType();
19371919
auto metaTy = i->getType().castTo<MetatypeType>();
@@ -1945,12 +1927,12 @@ void IRGenSILFunction::visitValueMetatypeInst(swift::ValueMetatypeInst *i) {
19451927
Explosion e;
19461928

19471929
if (instanceTy.getClassOrBoundGenericClass()) {
1948-
e.add(getClassMetatype(*this,
1930+
e.add(emitDynamicTypeOfHeapObject(*this,
19491931
getClassBaseValue(*this, i->getOperand()),
19501932
metaTy->getRepresentation(), instanceTy));
19511933
} else if (auto arch = instanceTy.getAs<ArchetypeType>()) {
19521934
if (arch->requiresClass()) {
1953-
e.add(getClassMetatype(*this,
1935+
e.add(emitDynamicTypeOfHeapObject(*this,
19541936
getClassBaseValue(*this, i->getOperand()),
19551937
metaTy->getRepresentation(), instanceTy));
19561938
} else {

stdlib/public/runtime/SwiftObject.mm

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,34 @@ static Class _swift_getObjCClassOfAllocated(const void *object) {
9696
return class_const_cast(_swift_getClassOfAllocated(object));
9797
}
9898

99+
/// \brief Fetch the ObjC class object associated with the formal dynamic
100+
/// type of the given (possibly Objective-C) object. The formal
101+
/// dynamic type ignores dynamic subclasses such as those introduced
102+
/// by KVO.
103+
///
104+
/// The object pointer may be a tagged pointer, but cannot be null.
105+
const ClassMetadata *swift::swift_getObjCClassFromObject(HeapObject *object) {
106+
auto classAsMetadata = _swift_getClass(object);
107+
108+
// Walk up the superclass chain skipping over artifical Swift classes.
109+
// If we find a non-Swift class use the result of [object class] instead.
110+
111+
while (classAsMetadata && classAsMetadata->isTypeMetadata()) {
112+
if (!classAsMetadata->isArtificialSubclass())
113+
return classAsMetadata;
114+
classAsMetadata = classAsMetadata->Superclass;
115+
}
116+
117+
id objcObject = reinterpret_cast<id>(object);
118+
Class objcClass = [objcObject class];
119+
if (objcObjectIsClass(objcObject)) {
120+
// Original object is a class. We want a
121+
// metaclass but +class doesn't give that to us.
122+
objcClass = object_getClass(objcClass);
123+
}
124+
classAsMetadata = reinterpret_cast<const ClassMetadata *>(objcClass);
125+
return classAsMetadata;
126+
}
99127
#endif
100128

101129
/// \brief Fetch the type metadata associated with the formal dynamic

test/IRGen/metatype.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ entry:
6767

6868
// CHECK-LABEL: define{{( protected)?}} swiftcc %objc_class* @existential_objc_metatype(%objc_object*) {{.*}} {
6969
// CHECK: entry:
70-
// CHECK-NEXT: [[METATYPE:%.*]] = call %objc_class* @object_getClass(%objc_object* %0) {{#[0-9]+}}
70+
// CHECK-NEXT: [[METATYPE:%.*]] = call %objc_class* @swift_getObjCClassFromObject(%objc_object* %0) {{#[0-9]+}}
7171
// CHECK-NEXT: ret %objc_class* [[METATYPE]]
7272
// CHECK-NEXT: }
7373
sil @existential_objc_metatype : $@convention(thin) (AnyObject) -> (@objc_metatype AnyObject.Type) {

0 commit comments

Comments
 (0)