[cxx-interop] Fix runtime crash when casting from an existential to a foreign reference type

When a C++ foreign reference type is conformed to a Swift protocol via a Swift extension, trying to cast `any MyProtocol` to the foreign reference type crashes the runtime.

This was because `selectCasterForDest` wasn't handling C++ foreign reference types, and we were hitting `swift_unreachable`.

This change makes sure the runtime doesn't crash for such casts.

Notably, Swift doesn't have enough metadata to determine if the conditional cast actually succeeded. This is also a problem for CF types. Casting CF types in a similar fashion triggers a typechecker diagnostic. That diagnostic will be amended in a follow-up patch to also trigger for foreign reference types.

rdar://141227849
This commit is contained in:
Egor Zhdan
2024-12-16 20:23:17 +00:00
parent c25e421fb5
commit 84ae5fbe8c
2 changed files with 21 additions and 0 deletions

View File

@@ -591,6 +591,16 @@ tryCastToForeignClass(
return DynamicCastResult::Failure; return DynamicCastResult::Failure;
} }
static DynamicCastResult tryCastToForeignReferenceType(
OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue,
const Metadata *srcType, const Metadata *&destFailureType,
const Metadata *&srcFailureType, bool takeOnSuccess, bool mayDeferChecks) {
assert(srcType != destType);
assert(destType->getKind() == MetadataKind::ForeignReferenceType);
return DynamicCastResult::Failure;
}
/******************************************************************************/ /******************************************************************************/
/***************************** Enum Destination *******************************/ /***************************** Enum Destination *******************************/
/******************************************************************************/ /******************************************************************************/
@@ -2187,6 +2197,8 @@ static tryCastFunctionType *selectCasterForDest(const Metadata *destType) {
return tryCastToOptional; return tryCastToOptional;
case MetadataKind::ForeignClass: case MetadataKind::ForeignClass:
return tryCastToForeignClass; return tryCastToForeignClass;
case MetadataKind::ForeignReferenceType:
return tryCastToForeignReferenceType;
case MetadataKind::Opaque: case MetadataKind::Opaque:
return tryCastToOpaque; return tryCastToOpaque;
case MetadataKind::Tuple: case MetadataKind::Tuple:

View File

@@ -61,6 +61,15 @@ WitnessTableTestSuite.test("As a Sequence") {
expectEqual(count, 3) expectEqual(count, 3)
} }
WitnessTableTestSuite.test("As an existential") {
let existential: any ListNode = makeLinkedList()
let cast: CxxLinkedList? = existential as? CxxLinkedList
expectNotNil(cast)
expectEqual(cast?.value, 0)
expectEqual(cast?.next()?.value, 1)
expectEqual(cast?.next()?.next()?.value, 2)
}
} }
runAllTests() runAllTests()