From 43df05a89c0618d4e89583e03f56a1eb53585b51 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 26 Mar 2025 22:29:12 -0700 Subject: [PATCH] [SE-0470] Prohibit isolated conformances in dynamic casts marked as such Certain dynamic casts cannot work safely with isolated conformances, regardless of what executor the code runs on. For such cases, reject all attempts to conform to the type. --- include/swift/ABI/MetadataValues.h | 5 + lib/IRGen/GenCast.cpp | 24 ++- lib/IRGen/GenCast.h | 5 +- lib/IRGen/IRGenSIL.cpp | 11 +- stdlib/public/runtime/Casting.cpp | 24 ++- stdlib/public/runtime/DynamicCast.cpp | 159 +++++++++++------- stdlib/public/runtime/Private.h | 3 +- .../Runtime/isolated_conformance.swift | 55 +++++- 8 files changed, 202 insertions(+), 84 deletions(-) diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index f258ffcc7f5..c9ef96b4e52 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -313,6 +313,11 @@ enum class DynamicCastFlags : size_t { /// True if the cast should destroy the source value on failure; /// false if the value should be left in place. DestroyOnFailure = 0x4, + + /// True if the cast should prohibit the use of any isolated conformances, + /// for example because there is a Sendable constraint on the existential + /// type we're casting to. + ProhibitIsolatedConformances = 0x8, }; inline bool operator&(DynamicCastFlags a, DynamicCastFlags b) { return (size_t(a) & size_t(b)) != 0; diff --git a/lib/IRGen/GenCast.cpp b/lib/IRGen/GenCast.cpp index 22fd39ef9aa..b15002ea7a1 100644 --- a/lib/IRGen/GenCast.cpp +++ b/lib/IRGen/GenCast.cpp @@ -42,8 +42,11 @@ using namespace swift; using namespace irgen; /// Compute the flags to pass to swift_dynamicCast. -static DynamicCastFlags getDynamicCastFlags(CastConsumptionKind consumptionKind, - CheckedCastMode mode) { +static DynamicCastFlags getDynamicCastFlags( + CastConsumptionKind consumptionKind, + CheckedCastMode mode, + CastingIsolatedConformances isolatedConformances +) { DynamicCastFlags flags = DynamicCastFlags::Default; if (mode == CheckedCastMode::Unconditional) @@ -53,6 +56,14 @@ static DynamicCastFlags getDynamicCastFlags(CastConsumptionKind consumptionKind, if (shouldTakeOnSuccess(consumptionKind)) flags |= DynamicCastFlags::TakeOnSuccess; + switch (isolatedConformances) { + case CastingIsolatedConformances::Allow: + break; + case CastingIsolatedConformances::Prohibit: + flags |= DynamicCastFlags::ProhibitIsolatedConformances; + break; + } + return flags; } @@ -63,10 +74,12 @@ llvm::Value *irgen::emitCheckedCast(IRGenFunction &IGF, Address dest, CanType targetType, CastConsumptionKind consumptionKind, - CheckedCastMode mode) { + CheckedCastMode mode, + CastingIsolatedConformances isolatedConformances) { // TODO: attempt to specialize this based on the known types. - DynamicCastFlags flags = getDynamicCastFlags(consumptionKind, mode); + DynamicCastFlags flags = getDynamicCastFlags(consumptionKind, mode, + isolatedConformances); // Cast both addresses to opaque pointer type. dest = IGF.Builder.CreateElementBitCast(dest, IGF.IGM.OpaqueTy); @@ -847,6 +860,7 @@ void irgen::emitScalarCheckedCast(IRGenFunction &IGF, SILType targetLoweredType, CanType targetFormalType, CheckedCastMode mode, + CastingIsolatedConformances isolatedConformances, Explosion &out) { assert(sourceLoweredType.isObject()); assert(targetLoweredType.isObject()); @@ -976,7 +990,7 @@ void irgen::emitScalarCheckedCast(IRGenFunction &IGF, src, sourceFormalType, dest, targetFormalType, CastConsumptionKind::TakeAlways, - mode); + mode, isolatedConformances); llvm::Value *successResult = IGF.Builder.CreateLoad(dest); llvm::Value *failureResult = llvm::ConstantPointerNull::get(destPtrType); llvm::Value *result = IGF.Builder.CreateSelect(success, successResult, failureResult); diff --git a/lib/IRGen/GenCast.h b/lib/IRGen/GenCast.h index a9a8002fd25..3529b466709 100644 --- a/lib/IRGen/GenCast.h +++ b/lib/IRGen/GenCast.h @@ -28,6 +28,7 @@ namespace swift { class SILType; class ProtocolDecl; enum class CastConsumptionKind : unsigned char; + enum class CastingIsolatedConformances: uint8_t; namespace irgen { class Address; @@ -46,7 +47,8 @@ namespace irgen { Address dest, CanType toType, CastConsumptionKind consumptionKind, - CheckedCastMode mode); + CheckedCastMode mode, + CastingIsolatedConformances isolatedConformances); void emitScalarCheckedCast(IRGenFunction &IGF, Explosion &value, SILType sourceLoweredType, @@ -54,6 +56,7 @@ namespace irgen { SILType targetLoweredType, CanType targetFormalType, CheckedCastMode mode, + CastingIsolatedConformances isolatedConformances, Explosion &out); llvm::Value *emitFastClassCastIfPossible( diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 086da5d307e..3aabe05c04b 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -7186,7 +7186,8 @@ visitUncheckedRefCastAddrInst(swift::UncheckedRefCastAddrInst *i) { src, i->getSourceFormalType(), dest, i->getTargetFormalType(), CastConsumptionKind::TakeAlways, - CheckedCastMode::Unconditional); + CheckedCastMode::Unconditional, + CastingIsolatedConformances::Allow); } void IRGenSILFunction::visitUncheckedAddrCastInst( @@ -7416,6 +7417,7 @@ void IRGenSILFunction::visitUnconditionalCheckedCastInst( i->getTargetLoweredType(), i->getTargetFormalType(), CheckedCastMode::Unconditional, + i->getIsolatedConformances(), ex); setLoweredExplosion(i, ex); } @@ -7604,7 +7606,8 @@ void IRGenSILFunction::visitUnconditionalCheckedCastAddrInst( src, i->getSourceFormalType(), dest, i->getTargetFormalType(), CastConsumptionKind::TakeAlways, - CheckedCastMode::Unconditional); + CheckedCastMode::Unconditional, + i->getIsolatedConformances()); } void IRGenSILFunction::visitCheckedCastBranchInst( @@ -7625,6 +7628,7 @@ void IRGenSILFunction::visitCheckedCastBranchInst( i->getTargetLoweredType(), i->getTargetFormalType(), CheckedCastMode::Conditional, + i->getIsolatedConformances(), ex); auto val = ex.claimNext(); castResult.casted = val; @@ -7662,7 +7666,8 @@ void IRGenSILFunction::visitCheckedCastAddrBranchInst( emitCheckedCast(*this, src, i->getSourceFormalType(), dest, i->getTargetFormalType(), - i->getConsumptionKind(), CheckedCastMode::Conditional); + i->getConsumptionKind(), CheckedCastMode::Conditional, + i->getIsolatedConformances()); Builder.CreateCondBr(castSucceeded, getLoweredBB(i->getSuccessBB()).bb, getLoweredBB(i->getFailureBB()).bb); diff --git a/stdlib/public/runtime/Casting.cpp b/stdlib/public/runtime/Casting.cpp index 8125662ebad..f495c03d0c0 100644 --- a/stdlib/public/runtime/Casting.cpp +++ b/stdlib/public/runtime/Casting.cpp @@ -614,12 +614,19 @@ bool swift::_conformsToProtocolInContext( const OpaqueValue *value, const Metadata *type, ProtocolDescriptorRef protocol, - const WitnessTable **conformance) { + const WitnessTable **conformance, + bool prohibitIsolatedConformances) { ConformanceExecutionContext context; if (!_conformsToProtocol(value, type, protocol, conformance, &context)) return false; + // If we aren't allowed to use isolated conformances and we ended up with + // one, fail. + if (prohibitIsolatedConformances && + context.globalActorIsolationType) + return false; + if (!swift_isInConformanceExecutionContext(type, &context)) return false; @@ -631,7 +638,8 @@ bool swift::_conformsToProtocolInContext( static bool _conformsToProtocols(const OpaqueValue *value, const Metadata *type, const ExistentialTypeMetadata *existentialType, - const WitnessTable **conformances) { + const WitnessTable **conformances, + bool prohibitIsolatedConformances) { if (auto *superclass = existentialType->getSuperclassConstraint()) { if (!swift_dynamicCastMetatype(type, superclass)) return false; @@ -644,7 +652,7 @@ static bool _conformsToProtocols(const OpaqueValue *value, for (auto protocol : existentialType->getProtocols()) { if (!_conformsToProtocolInContext( - value, type, protocol, conformances)) + value, type, protocol, conformances, prohibitIsolatedConformances)) return false; if (conformances != nullptr && protocol.needsWitnessTable()) { assert(*conformances != nullptr); @@ -1050,9 +1058,10 @@ swift_dynamicCastMetatypeImpl(const Metadata *sourceType, } static const Metadata * -swift_dynamicCastMetatypeUnconditionalImpl(const Metadata *sourceType, - const Metadata *targetType, - const char *file, unsigned line, unsigned column) { +swift_dynamicCastMetatypeUnconditionalImpl( + const Metadata *sourceType, + const Metadata *targetType, + const char *file, unsigned line, unsigned column) { auto origSourceType = sourceType; // Identical types always succeed @@ -1138,7 +1147,8 @@ swift_dynamicCastMetatypeUnconditionalImpl(const Metadata *sourceType, case MetadataKind::Existential: { auto targetTypeAsExistential = static_cast(targetType); - if (_conformsToProtocols(nullptr, sourceType, targetTypeAsExistential, nullptr)) + if (_conformsToProtocols(nullptr, sourceType, targetTypeAsExistential, + nullptr, /*prohibitIsolatedConformances=*/false)) return origSourceType; swift_dynamicCastFailure(sourceType, targetType); } diff --git a/stdlib/public/runtime/DynamicCast.cpp b/stdlib/public/runtime/DynamicCast.cpp index 237fc7e006d..f9aefd9489a 100644 --- a/stdlib/public/runtime/DynamicCast.cpp +++ b/stdlib/public/runtime/DynamicCast.cpp @@ -87,7 +87,7 @@ typedef DynamicCastResult (tryCastFunctionType)( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances ); // Forward-declare the main top-level `tryCast()` function @@ -401,7 +401,7 @@ tryCastUnwrappingObjCSwiftValueSource( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { id srcObject; memcpy(&srcObject, srcValue, sizeof(id)); @@ -422,7 +422,7 @@ tryCastUnwrappingObjCSwiftValueSource( destLocation, destType, const_cast(srcInnerValue), srcInnerType, destFailureType, srcFailureType, - /*takeOnSuccess=*/ false, mayDeferChecks); + /*takeOnSuccess=*/ false, mayDeferChecks, prohibitIsolatedConformances); } #else static DynamicCastResult @@ -430,7 +430,7 @@ tryCastUnwrappingSwiftValueSource( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { assert(srcType->getKind() == MetadataKind::Class); @@ -452,7 +452,7 @@ tryCastToSwiftClass( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { assert(srcType != destType); assert(destType->getKind() == MetadataKind::Class); @@ -496,7 +496,7 @@ tryCastToObjectiveCClass( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { assert(srcType != destType); assert(destType->getKind() == MetadataKind::ObjCClassWrapper); @@ -546,7 +546,7 @@ tryCastToForeignClass( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { #if SWIFT_OBJC_INTEROP assert(srcType != destType); @@ -594,7 +594,8 @@ tryCastToForeignClass( static DynamicCastResult tryCastToForeignReferenceType( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, - const Metadata *&srcFailureType, bool takeOnSuccess, bool mayDeferChecks) { + const Metadata *&srcFailureType, bool takeOnSuccess, bool mayDeferChecks, + bool prohibitIsolatedConformances) { assert(srcType != destType); assert(destType->getKind() == MetadataKind::ForeignReferenceType); @@ -610,7 +611,7 @@ tryCastToEnum( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { assert(srcType != destType); // Note: Optional is handled elsewhere @@ -783,7 +784,7 @@ tryCastToAnyHashable( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { assert(srcType != destType); assert(destType->getKind() == MetadataKind::Struct); @@ -859,7 +860,7 @@ tryCastToArray( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { assert(srcType != destType); assert(destType->getKind() == MetadataKind::Struct); @@ -899,7 +900,7 @@ tryCastToDictionary( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { assert(srcType != destType); assert(destType->getKind() == MetadataKind::Struct); @@ -940,7 +941,7 @@ tryCastToSet( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { assert(srcType != destType); assert(destType->getKind() == MetadataKind::Struct); @@ -982,7 +983,7 @@ tryCastToString( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { assert(srcType != destType); assert(destType->getKind() == MetadataKind::Struct); @@ -1013,7 +1014,7 @@ tryCastToStruct( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { assert(srcType != destType); assert(destType->getKind() == MetadataKind::Struct); @@ -1036,7 +1037,7 @@ tryCastToOptional( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { assert(srcType != destType); assert(destType->getKind() == MetadataKind::Optional); @@ -1110,7 +1111,7 @@ tryCastUnwrappingOptionalBoth( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { assert(destType->getKind() == MetadataKind::Optional); assert(srcType->getKind() == MetadataKind::Optional); @@ -1134,7 +1135,8 @@ tryCastUnwrappingOptionalBoth( auto destInnerLocation = destLocation; // Single-payload enum layout auto subcastResult = tryCast( destInnerLocation, destInnerType, srcValue, srcInnerType, - destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks, + prohibitIsolatedConformances); if (isSuccess(subcastResult)) { destInnerType->vw_storeEnumTagSinglePayload( destLocation, /*case*/ 0, /*emptyCases*/ 1); @@ -1153,7 +1155,7 @@ tryCastUnwrappingOptionalDestination( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { assert(destType->getKind() == MetadataKind::Optional); @@ -1162,7 +1164,8 @@ tryCastUnwrappingOptionalDestination( auto destInnerLocation = destLocation; // Single-payload enum layout auto subcastResult = tryCast( destInnerLocation, destInnerType, srcValue, srcType, - destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks, + prohibitIsolatedConformances); if (isSuccess(subcastResult)) { destInnerType->vw_storeEnumTagSinglePayload( destLocation, /*case*/ 0, /*emptyCases*/ 1); @@ -1179,7 +1182,7 @@ tryCastUnwrappingOptionalSource( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { assert(srcType->getKind() == MetadataKind::Optional); @@ -1190,7 +1193,8 @@ tryCastUnwrappingOptionalSource( if (nonNil) { // Recurse with unwrapped source return tryCast(destLocation, destType, srcValue, srcInnerType, - destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks, + prohibitIsolatedConformances); } return DynamicCastResult::Failure; } @@ -1208,7 +1212,7 @@ tryCastToTuple( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { assert(srcType != destType); assert(destType->getKind() == MetadataKind::Tuple); @@ -1284,7 +1288,8 @@ tryCastToTuple( auto subcastResult = tryCast(destElt.findIn(destLocation), destElt.Type, srcElt.findIn(srcValue), srcElt.Type, destFailureType, srcFailureType, - false, mayDeferChecks); + false, mayDeferChecks, + prohibitIsolatedConformances); if (subcastResult == DynamicCastResult::Failure) { for (unsigned k = 0; k != j; ++k) { const auto &elt = destTupleType->getElement(k); @@ -1310,7 +1315,7 @@ tryCastToFunction( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { assert(srcType != destType); assert(destType->getKind() == MetadataKind::Function); @@ -1372,7 +1377,8 @@ tryCastToFunction( static bool _conformsToProtocols(const OpaqueValue *value, const Metadata *type, const ExistentialTypeMetadata *existentialType, - const WitnessTable **conformances) { + const WitnessTable **conformances, + bool prohibitIsolatedConformances) { if (auto *superclass = existentialType->getSuperclassConstraint()) { if (!swift_dynamicCastMetatype(type, superclass)) return false; @@ -1385,7 +1391,7 @@ static bool _conformsToProtocols(const OpaqueValue *value, for (auto protocol : existentialType->getProtocols()) { if (!swift::_conformsToProtocolInContext( - value, type, protocol, conformances)) + value, type, protocol, conformances, prohibitIsolatedConformances)) return false; if (conformances != nullptr && protocol.needsWitnessTable()) { assert(*conformances != nullptr); @@ -1402,7 +1408,7 @@ tryCastToUnconstrainedOpaqueExistential( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { assert(srcType != destType); assert(destType->getKind() == MetadataKind::Existential); @@ -1429,7 +1435,7 @@ tryCastToConstrainedOpaqueExistential( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { assert(srcType != destType); assert(destType->getKind() == MetadataKind::Existential); @@ -1443,10 +1449,12 @@ tryCastToConstrainedOpaqueExistential( // TODO (rdar://17033499) If the source is an existential, we should // be able to compare the protocol constraints more efficiently than this. if (_conformsToProtocols(srcValue, srcType, destExistentialType, - destExistential->getWitnessTables())) { + destExistential->getWitnessTables(), + prohibitIsolatedConformances)) { return tryCastToUnconstrainedOpaqueExistential( destLocation, destType, srcValue, srcType, - destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks, + prohibitIsolatedConformances); } else { return DynamicCastResult::Failure; } @@ -1457,7 +1465,7 @@ tryCastToClassExistential( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { assert(srcType != destType); assert(destType->getKind() == MetadataKind::Existential); @@ -1480,7 +1488,8 @@ tryCastToClassExistential( auto value = reinterpret_cast(&tmp); auto type = reinterpret_cast(tmp); if (_conformsToProtocols(value, type, destExistentialType, - destExistentialLocation->getWitnessTables())) { + destExistentialLocation->getWitnessTables(), + prohibitIsolatedConformances)) { auto object = *(reinterpret_cast(value)); destExistentialLocation->Value = object; if (takeOnSuccess) { @@ -1529,7 +1538,8 @@ tryCastToClassExistential( } if (_conformsToProtocols(srcValue, srcType, destExistentialType, - destExistentialLocation->getWitnessTables())) { + destExistentialLocation->getWitnessTables(), + prohibitIsolatedConformances)) { destExistentialLocation->Value = srcObject; if (takeOnSuccess) { return DynamicCastResult::SuccessViaTake; @@ -1646,7 +1656,7 @@ tryCastToErrorExistential( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { assert(srcType != destType); assert(destType->getKind() == MetadataKind::Existential); @@ -1665,7 +1675,8 @@ tryCastToErrorExistential( assert(destExistentialType->NumProtocols == 1); const WitnessTable *errorWitness; if (_conformsToProtocols( - srcValue, srcType, destExistentialType, &errorWitness)) { + srcValue, srcType, destExistentialType, &errorWitness, + prohibitIsolatedConformances)) { #if SWIFT_OBJC_INTEROP // If it already holds an NSError, just use that. if (auto embedded = getErrorEmbeddedNSErrorIndirect( @@ -1697,7 +1708,7 @@ tryCastUnwrappingExistentialSource( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { assert(srcType != destType); assert(srcType->getKind() == MetadataKind::Existential); @@ -1738,13 +1749,14 @@ tryCastUnwrappingExistentialSource( srcInnerValue, srcInnerType, destFailureType, srcFailureType, takeOnSuccess && (srcInnerValue == srcValue), - mayDeferChecks); + mayDeferChecks, prohibitIsolatedConformances); } static DynamicCastResult tryCastUnwrappingExtendedExistentialSource( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, - const Metadata *&srcFailureType, bool takeOnSuccess, bool mayDeferChecks) { + const Metadata *&srcFailureType, bool takeOnSuccess, bool mayDeferChecks, + bool prohibitIsolatedConformances) { assert(srcType != destType); assert(srcType->getKind() == MetadataKind::ExtendedExistential); @@ -1784,7 +1796,8 @@ static DynamicCastResult tryCastUnwrappingExtendedExistentialSource( srcFailureType = srcInnerType; return tryCast(destLocation, destType, srcInnerValue, srcInnerType, destFailureType, srcFailureType, - takeOnSuccess && (srcInnerValue == srcValue), mayDeferChecks); + takeOnSuccess && (srcInnerValue == srcValue), mayDeferChecks, + prohibitIsolatedConformances); } static DynamicCastResult @@ -1792,7 +1805,7 @@ tryCastUnwrappingExistentialMetatypeSource( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { assert(srcType != destType); assert(srcType->getKind() == MetadataKind::ExistentialMetatype); @@ -1807,14 +1820,15 @@ tryCastUnwrappingExistentialMetatypeSource( srcInnerValue, srcInnerType, destFailureType, srcFailureType, takeOnSuccess && (srcInnerValue == srcValue), - mayDeferChecks); + mayDeferChecks, prohibitIsolatedConformances); } static DynamicCastResult tryCastToExtendedExistential( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, - const Metadata *&srcFailureType, bool takeOnSuccess, bool mayDeferChecks) { + const Metadata *&srcFailureType, bool takeOnSuccess, bool mayDeferChecks, + bool prohibitIsolatedConformances) { assert(srcType != destType); assert(destType->getKind() == MetadataKind::ExtendedExistential); @@ -1886,6 +1900,10 @@ static DynamicCastResult tryCastToExtendedExistential( if (error) return DynamicCastResult::Failure; + if (prohibitIsolatedConformances && + context.globalActorIsolationType) + return DynamicCastResult::Failure; + if (!swift_isInConformanceExecutionContext(selfType, &context)) return DynamicCastResult::Failure; } @@ -1951,7 +1969,7 @@ tryCastToOpaque( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { assert(srcType != destType); assert(destType->getKind() == MetadataKind::Opaque); @@ -1990,7 +2008,7 @@ tryCastToMetatype( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { assert(srcType != destType); assert(destType->getKind() == MetadataKind::Metatype); @@ -2023,7 +2041,8 @@ tryCastToMetatype( auto srcInnerValue = reinterpret_cast(&metatype); auto srcInnerType = swift_getMetatypeMetadata(metatype); return tryCast(destLocation, destType, srcInnerValue, srcInnerType, - destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks, + prohibitIsolatedConformances); } #endif return DynamicCastResult::Failure; @@ -2040,7 +2059,7 @@ _dynamicCastMetatypeToExistentialMetatype( OpaqueValue *destLocation, const ExistentialMetatypeMetadata *destType, const Metadata *srcMetatype, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { // The instance type of an existential metatype must be either an // existential or an existential metatype. @@ -2057,7 +2076,7 @@ _dynamicCastMetatypeToExistentialMetatype( = destMetatype ? destMetatype->getWitnessTables() : nullptr; if (!_conformsToProtocols(nullptr, srcMetatype, targetInstanceTypeAsExistential, - conformance)) { + conformance, prohibitIsolatedConformances)) { return DynamicCastResult::Failure; } @@ -2096,7 +2115,7 @@ _dynamicCastMetatypeToExistentialMetatype( srcInstanceType, destFailureType, srcFailureType, - takeOnSuccess, mayDeferChecks); + takeOnSuccess, mayDeferChecks, prohibitIsolatedConformances); } // "ExistentialMetatype" is the metatype for an existential type. @@ -2105,7 +2124,7 @@ tryCastToExistentialMetatype( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { assert(srcType != destType); assert(destType->getKind() == MetadataKind::ExistentialMetatype); @@ -2122,7 +2141,7 @@ tryCastToExistentialMetatype( srcMetatype, destFailureType, srcFailureType, - takeOnSuccess, mayDeferChecks); + takeOnSuccess, mayDeferChecks, prohibitIsolatedConformances); } case MetadataKind::ObjCClassWrapper: { @@ -2142,7 +2161,7 @@ tryCastToExistentialMetatype( metatype, destFailureType, srcFailureType, - takeOnSuccess, mayDeferChecks); + takeOnSuccess, mayDeferChecks, prohibitIsolatedConformances); } #endif return DynamicCastResult::Failure; @@ -2262,7 +2281,7 @@ tryCast( OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue, const Metadata *srcType, const Metadata *&destFailureType, const Metadata *&srcFailureType, - bool takeOnSuccess, bool mayDeferChecks) + bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances) { destFailureType = destType; srcFailureType = srcType; @@ -2295,7 +2314,8 @@ tryCast( return DynamicCastResult::Failure; } auto castResult = tryCastToDestType(destLocation, destType, srcValue, - srcType, destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + srcType, destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks, + prohibitIsolatedConformances); if (isSuccess(castResult)) { return castResult; } @@ -2312,7 +2332,8 @@ tryCast( srcFailureType = srcDynamicType; auto castResult = tryCastToDestType( destLocation, destType, srcValue, srcDynamicType, - destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks, + prohibitIsolatedConformances); if (isSuccess(castResult)) { return castResult; } @@ -2345,7 +2366,8 @@ tryCast( // Try unwrapping Obj-C __SwiftValue implementation auto subcastResult = tryCastUnwrappingObjCSwiftValueSource( destLocation, destType, srcValue, srcType, - destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks, + prohibitIsolatedConformances); if (isSuccess(subcastResult)) { return subcastResult; } @@ -2379,7 +2401,8 @@ tryCast( case MetadataKind::Existential: { auto subcastResult = tryCastUnwrappingExistentialSource( destLocation, destType, srcValue, srcType, - destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks, + prohibitIsolatedConformances); if (isSuccess(subcastResult)) { return subcastResult; } @@ -2389,7 +2412,8 @@ tryCast( case MetadataKind::ExistentialMetatype: { auto subcastResult = tryCastUnwrappingExistentialMetatypeSource( destLocation, destType, srcValue, srcType, - destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks, + prohibitIsolatedConformances); if (isSuccess(subcastResult)) { return subcastResult; } @@ -2399,7 +2423,8 @@ tryCast( case MetadataKind::ExtendedExistential: { auto subcastResult = tryCastUnwrappingExtendedExistentialSource( destLocation, destType, srcValue, srcType, destFailureType, - srcFailureType, takeOnSuccess, mayDeferChecks); + srcFailureType, takeOnSuccess, mayDeferChecks, + prohibitIsolatedConformances); if (isSuccess(subcastResult)) { return subcastResult; } @@ -2423,14 +2448,16 @@ tryCast( if (srcKind == MetadataKind::Optional) { auto subcastResult = tryCastUnwrappingOptionalBoth( destLocation, destType, srcValue, srcType, - destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks, + prohibitIsolatedConformances); if (isSuccess(subcastResult)) { return subcastResult; } } auto subcastResult = tryCastUnwrappingOptionalDestination( destLocation, destType, srcValue, srcType, - destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks, + prohibitIsolatedConformances); if (isSuccess(subcastResult)) { return subcastResult; } @@ -2439,7 +2466,8 @@ tryCast( if (srcKind == MetadataKind::Optional) { auto subcastResult = tryCastUnwrappingOptionalSource( destLocation, destType, srcValue, srcType, - destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks, + prohibitIsolatedConformances); if (isSuccess(subcastResult)) { return subcastResult; } @@ -2583,6 +2611,11 @@ swift_dynamicCastImpl(OpaqueValue *destLocation, // actually accessed. bool mayDeferChecks = flags & DynamicCastFlags::Unconditional; + // Whether the compiler told us that we aren't allowed to use *any* isolated + // conformances, regardless of whether we are in that isolation domain. + bool prohibitIsolatedConformances = + flags & DynamicCastFlags::ProhibitIsolatedConformances; + // Attempt the cast... const Metadata *destFailureType = destType; const Metadata *srcFailureType = srcType; @@ -2590,7 +2623,7 @@ swift_dynamicCastImpl(OpaqueValue *destLocation, destLocation, destType, srcValue, srcType, destFailureType, srcFailureType, - takeOnSuccess, mayDeferChecks); + takeOnSuccess, mayDeferChecks, prohibitIsolatedConformances); switch (result) { case DynamicCastResult::Failure: diff --git a/stdlib/public/runtime/Private.h b/stdlib/public/runtime/Private.h index 2ef15f450a2..58f4783e9b2 100644 --- a/stdlib/public/runtime/Private.h +++ b/stdlib/public/runtime/Private.h @@ -721,7 +721,8 @@ public: const OpaqueValue *value, const Metadata *type, ProtocolDescriptorRef protocol, - const WitnessTable **conformance); + const WitnessTable **conformance, + bool prohibitIsolatedConformances); /// Construct type metadata for the given protocol. const Metadata * diff --git a/test/Concurrency/Runtime/isolated_conformance.swift b/test/Concurrency/Runtime/isolated_conformance.swift index ecda8b34b3b..ca3a3534404 100644 --- a/test/Concurrency/Runtime/isolated_conformance.swift +++ b/test/Concurrency/Runtime/isolated_conformance.swift @@ -20,6 +20,10 @@ protocol Q { func g() } +protocol R: Sendable { + func h() +} + nonisolated class MyClass: @MainActor P { func f() { print("MyClass.f()") @@ -45,6 +49,12 @@ extension MyClass: @SomeGlobalActor Q { } } +extension MyClass: nonisolated R { + nonisolated func h() { + print("MyClass.h()") + } +} + struct Wrapper { var wrapped: T } @@ -63,6 +73,13 @@ extension Wrapper: Q where T: Q { } } +extension Wrapper: R where T: R { + func h() { + print("Wrapper for ", terminator: "") + wrapped.h() + } +} + @available(SwiftStdlib 5.9, *) struct WrapMany { var wrapped: (repeat each T) @@ -82,14 +99,23 @@ extension WrapMany: Q where repeat each T: Q { } } -extension Int: P, Q { - func f() { } - func g() { } +@available(SwiftStdlib 5.9, *) +extension WrapMany: R where repeat each T: R { + func h() { + print("Wrapper for many") + } } -extension String: P, Q { +extension Int: P, Q, R { func f() { } func g() { } + func h() { } +} + +extension String: P, Q, R { + func f() { } + func g() { } + func h() { } } func tryCastToP(_ value: any Sendable) -> Bool { @@ -102,6 +128,16 @@ func tryCastToP(_ value: any Sendable) -> Bool { return false } +func tryCastToPAndR(_ value: any Sendable) -> Bool { + if let p = value as? any P & R { + p.f() + return true + } + + print("Conformance did not match") + return false +} + func tryCastToQ(_ value: any Sendable) -> Bool { if let q = value as? any Q { q.g() @@ -134,6 +170,11 @@ await Task.detached { @MainActor in precondition(tryCastToP(mc)) precondition(tryCastToP(wrappedMC)) + // Cannot cast to P & R because the conformance to P is isolated, but R + // is Sendable. + precondition(!tryCastToPAndR(mc)) + precondition(!tryCastToPAndR(wrappedMC)) + if #available(SwiftStdlib 5.9, *) { let wrappedMany = WrapMany(wrapped: (17, mc, "Pack")) precondition(tryCastToP(wrappedMany)) @@ -149,6 +190,11 @@ await Task.detached { @SomeGlobalActor in precondition(tryCastToQ(mc)) precondition(tryCastToQ(wrappedMC)) + // Cannot cast to P & R because the conformance to P is isolated, but R + // is Sendable. + precondition(!tryCastToPAndR(mc)) + precondition(!tryCastToPAndR(wrappedMC)) + if #available(SwiftStdlib 5.9, *) { let wrappedMany = WrapMany(wrapped: (17, mc, "Pack")) precondition(tryCastToQ(wrappedMany)) @@ -176,5 +222,6 @@ await Task.detached { } }.value + // Ensure that we access mc later print(mc)