mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Conditionalize the fix for SR-14635 (#40822)
* Refactor Bincompat Organize everything around internal functions that test for a particular OS version. Correctly handle cases where we don't know the version of the app. Make all bincompat functions consistently return `true` for the legacy semantics, `false` for new semantics. Consistently name them all to reflect this. * Conditionalize the support for SR-14635 SR-14635 pointed out a hole in the updated dynamic casting logic that allowed certain casts that should have been illegal. In particular, when casting certain types to Obj-C protocols, the Swift value gets boxed; we would permit the cast to succeed whenever the resulting box satisfied the protocol. For example, this allowed any Swift value to be cast to `NSCopying` regardless of whether or not it implemented the required `copy(with:)` method. This was fixed in #37683 to reject such casts but of course some folks were depending on this behavior to pass Swift data into Obj-C functions. (The properly supported approach for passing arbitrary Swift data into Obj-C functions is to cast the Swift value to `AnyObject`.) This change makes that new behavior conditional. For now, the legacy semantics are enabled on Apple platforms and the new semantics are in use everywhere else. This will allow us to gradually enable enforcement of the new behavior over time. * Just skip this test on Apple platforms, since it is inconsistently implemented there (and is therefore not really testable)
This commit is contained in:
@@ -129,13 +129,7 @@ static HeapObject * getNonNullSrcObject(OpaqueValue *srcValue,
|
||||
const char * const msg = "Found unexpected null pointer value"
|
||||
" while trying to cast value of type '%s' (%p)"
|
||||
" to '%s' (%p)%s\n";
|
||||
if (runtime::bincompat::unexpectedObjCNullWhileCastingIsFatal()) {
|
||||
// By default, Swift 5.4 and later issue a fatal error.
|
||||
swift::fatalError(/* flags = */ 0, msg,
|
||||
srcTypeName.c_str(), srcType,
|
||||
destTypeName.c_str(), destType,
|
||||
"");
|
||||
} else {
|
||||
if (runtime::bincompat::useLegacyPermissiveObjCNullSemanticsInCasting()) {
|
||||
// In backwards compatibility mode, this code will warn and return the null
|
||||
// reference anyway: If you examine the calls to the function, you'll see
|
||||
// that most callers fail the cast in that case, but a few casts (e.g., with
|
||||
@@ -145,6 +139,12 @@ static HeapObject * getNonNullSrcObject(OpaqueValue *srcValue,
|
||||
srcTypeName.c_str(), srcType,
|
||||
destTypeName.c_str(), destType,
|
||||
": Continuing with null object, but expect problems later.");
|
||||
} else {
|
||||
// By default, Swift 5.4 and later issue a fatal error.
|
||||
swift::fatalError(/* flags = */ 0, msg,
|
||||
srcTypeName.c_str(), srcType,
|
||||
destTypeName.c_str(), destType,
|
||||
"");
|
||||
}
|
||||
return object;
|
||||
}
|
||||
@@ -1092,7 +1092,7 @@ tryCastUnwrappingOptionalBoth(
|
||||
srcValue, /*emptyCases=*/1);
|
||||
auto sourceIsNil = (sourceEnumCase != 0);
|
||||
if (sourceIsNil) {
|
||||
if (runtime::bincompat::useLegacyOptionalNilInjection()) {
|
||||
if (runtime::bincompat::useLegacyOptionalNilInjectionInCasting()) {
|
||||
auto destInnerType = cast<EnumMetadata>(destType)->getGenericArgs()[0];
|
||||
// Set .none at the outer level
|
||||
destInnerType->vw_storeEnumTagSinglePayload(destLocation, 1, 1);
|
||||
@@ -1554,30 +1554,47 @@ tryCastToClassExistentialViaSwiftValue(
|
||||
}
|
||||
|
||||
default: {
|
||||
// We can always box when the destination is a simple
|
||||
// (unconstrained) `AnyObject`.
|
||||
if (destExistentialType->NumProtocols != 0) {
|
||||
// The destination is a class-constrained protocol type
|
||||
// and the source is not a class, so....
|
||||
return DynamicCastResult::Failure;
|
||||
} else {
|
||||
// This is a simple (unconstrained) `AnyObject` so we can populate
|
||||
// it by stuffing a non-class instance into a __SwiftValue box
|
||||
#if SWIFT_OBJC_INTEROP
|
||||
auto object = bridgeAnythingToSwiftValueObject(
|
||||
srcValue, srcType, takeOnSuccess);
|
||||
destExistentialLocation->Value = object;
|
||||
if (takeOnSuccess) {
|
||||
return DynamicCastResult::SuccessViaTake;
|
||||
} else {
|
||||
return DynamicCastResult::SuccessViaCopy;
|
||||
// But if there are constraints...
|
||||
if (!runtime::bincompat::useLegacyObjCBoxingInCasting()) {
|
||||
// ... never box if we're not supporting legacy semantics.
|
||||
return DynamicCastResult::Failure;
|
||||
}
|
||||
// Legacy behavior: We used to permit casts to a constrained (existential)
|
||||
// type if the resulting `__SwiftValue` box conformed to the target type.
|
||||
// This is no longer supported, since it caused `x is NSCopying` to be
|
||||
// true even when x does not in fact implement the requirements of
|
||||
// `NSCopying`.
|
||||
#if SWIFT_OBJC_INTEROP
|
||||
if (!findSwiftValueConformances(
|
||||
destExistentialType, destExistentialLocation->getWitnessTables())) {
|
||||
return DynamicCastResult::Failure;
|
||||
}
|
||||
#else
|
||||
if (!swift_swiftValueConformsTo(destType, destType)) {
|
||||
return DynamicCastResult::Failure;
|
||||
}
|
||||
# else
|
||||
// Note: Code below works correctly on both Obj-C and non-Obj-C platforms,
|
||||
// but the code above is slightly faster on Obj-C platforms.
|
||||
auto object = _bridgeAnythingToObjectiveC(srcValue, srcType);
|
||||
destExistentialLocation->Value = object;
|
||||
return DynamicCastResult::SuccessViaCopy;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if SWIFT_OBJC_INTEROP
|
||||
auto object = bridgeAnythingToSwiftValueObject(
|
||||
srcValue, srcType, takeOnSuccess);
|
||||
destExistentialLocation->Value = object;
|
||||
if (takeOnSuccess) {
|
||||
return DynamicCastResult::SuccessViaTake;
|
||||
} else {
|
||||
return DynamicCastResult::SuccessViaCopy;
|
||||
}
|
||||
# else
|
||||
// Note: Code below works correctly on both Obj-C and non-Obj-C platforms,
|
||||
// but the code above is slightly faster on Obj-C platforms.
|
||||
auto object = _bridgeAnythingToObjectiveC(srcValue, srcType);
|
||||
destExistentialLocation->Value = object;
|
||||
return DynamicCastResult::SuccessViaCopy;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user