Skip to content

Commit feba039

Browse files
Merge pull request swiftlang#30785 from nate-chandler/rdar60790020
[Runtime] Handle incomplete class metadata in _checkGenericRequirements.
2 parents 109ec5d + 2863f19 commit feba039

20 files changed

+588
-22
lines changed

include/swift/Runtime/Metadata.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -796,6 +796,10 @@ SWIFT_CC(swift)
796796
SWIFT_RUNTIME_STDLIB_INTERNAL
797797
const Metadata *_swift_class_getSuperclass(const Metadata *theClass);
798798

799+
SWIFT_CC(swift)
800+
SWIFT_RUNTIME_STDLIB_INTERNAL MetadataResponse
801+
getSuperclassMetadata(MetadataRequest request, const ClassMetadata *self);
802+
799803
#if !NDEBUG
800804
/// Verify that the given metadata pointer correctly roundtrips its
801805
/// mangled name through the demangler.

stdlib/public/runtime/Metadata.cpp

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2735,19 +2735,14 @@ initGenericObjCClass(ClassMetadata *self, size_t numFields,
27352735
#endif
27362736

27372737
SWIFT_CC(swift)
2738-
static std::pair<MetadataDependency, const ClassMetadata *>
2739-
getSuperclassMetadata(ClassMetadata *self, bool allowDependency) {
2738+
SWIFT_RUNTIME_STDLIB_INTERNAL MetadataResponse
2739+
getSuperclassMetadata(MetadataRequest request, const ClassMetadata *self) {
27402740
// If there is a mangled superclass name, demangle it to the superclass
27412741
// type.
2742-
const ClassMetadata *super = nullptr;
27432742
if (auto superclassNameBase = self->getDescription()->SuperclassType.get()) {
27442743
StringRef superclassName =
27452744
Demangle::makeSymbolicMangledNameStringRef(superclassNameBase);
27462745
SubstGenericParametersFromMetadata substitutions(self);
2747-
MetadataRequest request(allowDependency
2748-
? MetadataState::NonTransitiveComplete
2749-
: /*FIXME*/ MetadataState::Abstract,
2750-
/*non-blocking*/ allowDependency);
27512746
MetadataResponse response =
27522747
swift_getTypeByMangledName(request, superclassName,
27532748
substitutions.getGenericArgs(),
@@ -2765,22 +2760,42 @@ getSuperclassMetadata(ClassMetadata *self, bool allowDependency) {
27652760
superclassName.str().c_str());
27662761
}
27672762

2768-
// If the request isn't satisfied, we have a new dependency.
2769-
if (!request.isSatisfiedBy(response.State)) {
2770-
assert(allowDependency);
2771-
return {MetadataDependency(superclass, request.getState()),
2772-
cast<ClassMetadata>(superclass)};
2773-
}
2763+
return response;
2764+
} else {
2765+
return MetadataResponse();
2766+
}
2767+
}
2768+
2769+
SWIFT_CC(swift)
2770+
static std::pair<MetadataDependency, const ClassMetadata *>
2771+
getSuperclassMetadata(ClassMetadata *self, bool allowDependency) {
2772+
MetadataRequest request(allowDependency ? MetadataState::NonTransitiveComplete
2773+
: /*FIXME*/ MetadataState::Abstract,
2774+
/*non-blocking*/ allowDependency);
2775+
auto response = getSuperclassMetadata(request, self);
2776+
2777+
auto *superclass = response.Value;
2778+
if (!superclass)
2779+
return {MetadataDependency(), nullptr};
27742780

2781+
const ClassMetadata *second;
27752782
#if SWIFT_OBJC_INTEROP
2776-
if (auto objcWrapper = dyn_cast<ObjCClassWrapperMetadata>(superclass))
2777-
superclass = objcWrapper->Class;
2783+
if (auto objcWrapper = dyn_cast<ObjCClassWrapperMetadata>(superclass)) {
2784+
second = objcWrapper->Class;
2785+
} else {
2786+
second = cast<ClassMetadata>(superclass);
2787+
}
2788+
#else
2789+
second = cast<ClassMetadata>(superclass);
27782790
#endif
27792791

2780-
super = cast<ClassMetadata>(superclass);
2792+
// If the request isn't satisfied, we have a new dependency.
2793+
if (!request.isSatisfiedBy(response.State)) {
2794+
assert(allowDependency);
2795+
return {MetadataDependency(superclass, request.getState()), second};
27812796
}
27822797

2783-
return {MetadataDependency(), super};
2798+
return {MetadataDependency(), second};
27842799
}
27852800

27862801
// Suppress diagnostic about the availability of _objc_realizeClassFromSwift.

stdlib/public/runtime/ProtocolConformance.cpp

Lines changed: 133 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,137 @@ swift::_searchConformancesByMangledTypeName(Demangle::NodePointer node) {
665665
return nullptr;
666666
}
667667

668+
static MetadataState
669+
tryGetCompleteMetadataNonblocking(const Metadata *metadata) {
670+
return swift_checkMetadataState(
671+
MetadataRequest(MetadataState::Complete, /*isNonBlocking*/ true),
672+
metadata)
673+
.State;
674+
}
675+
676+
template <typename HandleObjc>
677+
bool isSwiftClassMetadataSubclass(const ClassMetadata *subclass,
678+
const ClassMetadata *superclass,
679+
HandleObjc handleObjc) {
680+
assert(subclass);
681+
assert(superclass);
682+
683+
MetadataState subclassState = tryGetCompleteMetadataNonblocking(subclass);
684+
685+
do {
686+
if (subclassState == MetadataState::Complete) {
687+
// The subclass metadata is complete. That means not just that its
688+
// Superclass field is valid, but that the Superclass field of the
689+
// referenced class metadata is valid, and the Superclass field of the
690+
// class metadata referenced there, and so on transitively.
691+
//
692+
// Scan the superclass chains in the ClassMetadata looking for a match.
693+
while ((subclass = subclass->Superclass)) {
694+
if (subclass == superclass)
695+
return true;
696+
}
697+
return false;
698+
}
699+
if (subclassState == MetadataState::NonTransitiveComplete) {
700+
// The subclass metadata is complete, but, unlike above, not transitively.
701+
// Its Superclass field is valid, so just read that field to get to the
702+
// superclass to proceed to the next step.
703+
subclass = subclass->Superclass;
704+
if (subclass->isPureObjC()) {
705+
return handleObjc(subclass, superclass);
706+
}
707+
subclassState = tryGetCompleteMetadataNonblocking(subclass);
708+
} else {
709+
// The subclass metadata is either LayoutComplete or Abstract, so the
710+
// Superclass field is not valid. To get to the superclass, make the
711+
// expensive call to getSuperclassMetadata which demangles the superclass
712+
// name from the nominal type descriptor to get the metadata for the
713+
// superclass.
714+
MetadataRequest request(MetadataState::Complete,
715+
/*non-blocking*/ true);
716+
auto response = getSuperclassMetadata(request, subclass);
717+
auto newMetadata = response.Value;
718+
if (auto newSubclass = dyn_cast<ClassMetadata>(newMetadata)) {
719+
subclass = newSubclass;
720+
subclassState = response.State;
721+
} else {
722+
return handleObjc(newMetadata, superclass);
723+
}
724+
}
725+
if (subclass == superclass)
726+
return true;
727+
} while (subclass);
728+
return false;
729+
}
730+
731+
// Whether the provided `subclass` is metadata for a subclass* of the superclass
732+
// whose metadata is specified.
733+
//
734+
// The function is robust against incomplete metadata for both subclass and
735+
// superclass. In the worst case, each intervening class between subclass and
736+
// superclass is demangled. Besides that slow path, there are a number of fast
737+
// paths:
738+
// - both classes are ObjC: swift_dynamicCastMetatype
739+
// - Complete subclass metadata: loop over Superclass fields
740+
// - NonTransitiveComplete: read the Superclass field once
741+
//
742+
// * A non-strict subclass; that is, given a class X, isSubclass(X.self, X.self)
743+
// is true.
744+
static bool isSubclass(const Metadata *subclass, const Metadata *superclass) {
745+
assert(subclass);
746+
assert(superclass);
747+
assert(subclass->isAnyClass());
748+
assert(superclass->isAnyClass());
749+
750+
if (subclass == superclass)
751+
return true;
752+
if (!isa<ClassMetadata>(subclass)) {
753+
if (!isa<ClassMetadata>(superclass)) {
754+
// Only ClassMetadata can be incomplete; when the class metadata is not
755+
// ClassMetadata, just use swift_dynamicCastMetatype.
756+
return swift_dynamicCastMetatype(subclass, superclass);
757+
} else {
758+
// subclass is ObjC, but superclass is not; since it is not possible for
759+
// any ObjC class to be a subclass of any Swift class, this subclass is
760+
// not a subclass of this superclass.
761+
return false;
762+
}
763+
}
764+
const ClassMetadata *swiftSubclass = cast<ClassMetadata>(subclass);
765+
if (auto *objcSuperclass = dyn_cast<ObjCClassWrapperMetadata>(superclass)) {
766+
// Walk up swiftSubclass's ancestors until we get to an ObjC class, then
767+
// kick over to swift_dynamicCastMetatype.
768+
return isSwiftClassMetadataSubclass(
769+
swiftSubclass, objcSuperclass->Class,
770+
[](const Metadata *intermediate, const Metadata *superclass) {
771+
// Intermediate is an ObjC class, and superclass is an ObjC class;
772+
// as above, just use swift_dynamicCastMetatype.
773+
return swift_dynamicCastMetatype(intermediate, superclass);
774+
});
775+
return false;
776+
}
777+
if (isa<ForeignClassMetadata>(superclass)) {
778+
// superclass is foreign, but subclass is not (if it were, the above
779+
// !isa<ClassMetadata> condition would have been entered). Since it is not
780+
// possible for any Swift class to be a subclass of any foreign superclass,
781+
// this subclass is not a subclass of this superclass.
782+
return false;
783+
}
784+
auto swiftSuperclass = cast<ClassMetadata>(superclass);
785+
return isSwiftClassMetadataSubclass(swiftSubclass, swiftSuperclass,
786+
[](const Metadata *, const Metadata *) {
787+
// Because (1) no ObjC classes inherit
788+
// from Swift classes and (2)
789+
// `superclass` is not ObjC, if some
790+
// ancestor of `subclass` is ObjC, then
791+
// `subclass` cannot descend from
792+
// `superclass` (otherwise at some point
793+
// some ObjC class would have to inherit
794+
// from a Swift class).
795+
return false;
796+
});
797+
}
798+
668799
bool swift::_checkGenericRequirements(
669800
llvm::ArrayRef<GenericRequirementDescriptor> requirements,
670801
SmallVectorImpl<const void *> &extraArguments,
@@ -738,11 +869,8 @@ bool swift::_checkGenericRequirements(
738869
substGenericParam, substWitnessTable).getMetadata();
739870
if (!baseType) return true;
740871

741-
// Check whether it's dynamically castable, which works as a superclass
742-
// check.
743-
// FIXME: We should be explicitly checking the superclass, so we
744-
// don't require the subject type to be complete.
745-
if (!swift_dynamicCastMetatype(subjectType, baseType)) return true;
872+
if (!isSubclass(subjectType, baseType))
873+
return true;
746874

747875
continue;
748876
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#import <Foundation/Foundation.h>
2+
3+
@interface Subclass : NSObject
4+
5+
@end
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#import <Foundation/Foundation.h>
2+
3+
@interface Subclass : NSObject
4+
5+
@end
6+
7+
@implementation Subclass
8+
@end

test/Runtime/Inputs/Superclass.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#import <Foundation/Foundation.h>
2+
3+
@interface Superclass : NSObject
4+
@end

test/Runtime/Inputs/Superclass.m

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#import <Foundation/Foundation.h>
2+
3+
@interface Superclass : NSObject
4+
@end
5+
6+
@implementation Superclass
7+
@end
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import Framework
2+
3+
open class Subclass1 : Superclass {
4+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// CHECK: Subclass
2+
print(Subclass().self)
3+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import Foundation
2+
3+
public struct Gen<Subclass : NSObject> {
4+
public init() {
5+
}
6+
}

0 commit comments

Comments
 (0)