@@ -116,6 +116,11 @@ static bool unexpectedNullIsFatal() {
116116 return true ; // Placeholder for an upcoming check.
117117}
118118
119+ // / This issues a fatal error or warning if the srcValue contains a null object
120+ // / reference. It is used when the srcType is a non-nullable reference type, in
121+ // / which case it is dangerous to continue with a null reference. The null
122+ // / reference is returned if we're operating in backwards-compatibility mode, so
123+ // / callers still have to check for null.
119124static HeapObject * getNonNullSrcObject (OpaqueValue *srcValue,
120125 const Metadata *srcType,
121126 const Metadata *destType) {
@@ -130,11 +135,17 @@ static HeapObject * getNonNullSrcObject(OpaqueValue *srcValue,
130135 " while trying to cast value of type '%s' (%p)"
131136 " to '%s' (%p)%s\n " ;
132137 if (unexpectedNullIsFatal ()) {
138+ // By default, Swift 5.4 and later issue a fatal error.
133139 swift::fatalError (/* flags = */ 0 , msg,
134140 srcTypeName.c_str (), srcType,
135141 destTypeName.c_str (), destType,
136142 " " );
137143 } else {
144+ // In backwards compatibility mode, this code will warn and return the null
145+ // reference anyway: If you examine the calls to the function, you'll see
146+ // that most callers fail the cast in that case, but a few casts (e.g., with
147+ // Obj-C or CF destination type) sill succeed in that case. This is
148+ // dangerous, but necessary for compatibility.
138149 swift::warning (/* flags = */ 0 , msg,
139150 srcTypeName.c_str (), srcType,
140151 destTypeName.c_str (), destType,
@@ -325,7 +336,9 @@ tryCastFromClassToObjCBridgeable(
325336 _getBridgedObjectiveCType (MetadataState::Complete, destType,
326337 destBridgeWitness).Value ;
327338 void *srcObject = getNonNullSrcObject (srcValue, srcType, destType);
328- if (nullptr == swift_dynamicCastUnknownClass (srcObject, targetBridgedClass)) {
339+ // Note: srcObject can be null here in compatibility mode
340+ if (nullptr == srcObject
341+ || nullptr == swift_dynamicCastUnknownClass (srcObject, targetBridgedClass)) {
329342 destFailureType = targetBridgedClass;
330343 return DynamicCastResult::Failure;
331344 }
@@ -437,8 +450,12 @@ tryCastToSwiftClass(
437450 switch (srcType->getKind ()) {
438451 case MetadataKind::Class: // Swift class => Swift class
439452 case MetadataKind::ObjCClassWrapper: { // Obj-C class => Swift class
440- void *object = getNonNullSrcObject (srcValue, srcType, destType);
441- if (auto t = swift_dynamicCastClass (object, destClassType)) {
453+ void *srcObject = getNonNullSrcObject (srcValue, srcType, destType);
454+ // Note: srcObject can be null in compatibility mode.
455+ if (srcObject == nullptr ) {
456+ return DynamicCastResult::Failure;
457+ }
458+ if (auto t = swift_dynamicCastClass (srcObject, destClassType)) {
442459 auto castObject = const_cast <void *>(t);
443460 *(reinterpret_cast <void **>(destLocation)) = castObject;
444461 if (takeOnSuccess) {
@@ -480,6 +497,17 @@ tryCastToObjectiveCClass(
480497 case MetadataKind::ObjCClassWrapper: // Obj-C class => Obj-C class
481498 case MetadataKind::ForeignClass: { // CF class => Obj-C class
482499 auto srcObject = getNonNullSrcObject (srcValue, srcType, destType);
500+ // If object is null, then we're in the compatibility mode.
501+ // Earlier cast logic always succeeded `as!` casts of nil
502+ // class references but failed `as?` and `is`
503+ if (srcObject == nullptr ) {
504+ if (mayDeferChecks) {
505+ *reinterpret_cast <const void **>(destLocation) = nullptr ;
506+ return DynamicCastResult::SuccessViaCopy;
507+ } else {
508+ return DynamicCastResult::Failure;
509+ }
510+ }
483511 auto destObjCClass = destObjCType->Class ;
484512 if (auto resultObject
485513 = swift_dynamicCastObjCClass (srcObject, destObjCClass)) {
@@ -519,6 +547,19 @@ tryCastToForeignClass(
519547 case MetadataKind::ObjCClassWrapper: // Obj-C class => CF class
520548 case MetadataKind::ForeignClass: { // CF class => CF class
521549 auto srcObject = getNonNullSrcObject (srcValue, srcType, destType);
550+ // If srcObject is null, then we're in compatibility mode.
551+ // Earlier cast logic always succeeded `as!` casts of nil
552+ // class references. Yes, this is very dangerous, which
553+ // is why we no longer permit it.
554+ if (srcObject == nullptr ) {
555+ if (mayDeferChecks) {
556+ *reinterpret_cast <const void **>(destLocation) = nullptr ;
557+ return DynamicCastResult::SuccessViaCopy;
558+ } else {
559+ // `as?` and `is` checks always fail on nil sources
560+ return DynamicCastResult::Failure;
561+ }
562+ }
522563 if (auto resultObject
523564 = swift_dynamicCastForeignClass (srcObject, destClassType)) {
524565 *reinterpret_cast <const void **>(destLocation) = resultObject;
@@ -694,6 +735,10 @@ struct ObjCBridgeMemo {
694735 // Use the dynamic ISA type of the object always (Note that this
695736 // also implicitly gives us the ObjC type for a CF object.)
696737 void *srcObject = getNonNullSrcObject (srcValue, srcType, destType);
738+ // If srcObject is null, then we're in backwards compatibility mode.
739+ if (srcObject == nullptr ) {
740+ return DynamicCastResult::Failure;
741+ }
697742 Class srcObjCType = object_getClass ((id)srcObject);
698743 // Fail if the ObjC object is not a subclass of the bridge class.
699744 while (srcObjCType != targetBridgedObjCClass) {
@@ -1417,15 +1462,26 @@ tryCastToClassExistential(
14171462 case MetadataKind::ObjCClassWrapper:
14181463 case MetadataKind::Class:
14191464 case MetadataKind::ForeignClass: {
1420- auto object = getNonNullSrcObject (srcValue, srcType, destType);
1465+ auto srcObject = getNonNullSrcObject (srcValue, srcType, destType);
1466+ // If srcObject is null, then we're in compatibility mode.
1467+ // Earlier cast logic always succeeded `as!` casts of nil
1468+ // class references:
1469+ if (srcObject == nullptr ) {
1470+ if (mayDeferChecks) {
1471+ *reinterpret_cast <const void **>(destLocation) = nullptr ;
1472+ return DynamicCastResult::SuccessViaCopy;
1473+ } else {
1474+ return DynamicCastResult::Failure;
1475+ }
1476+ }
14211477 if (_conformsToProtocols (srcValue, srcType,
14221478 destExistentialType,
14231479 destExistentialLocation->getWitnessTables ())) {
1424- destExistentialLocation->Value = object ;
1480+ destExistentialLocation->Value = srcObject ;
14251481 if (takeOnSuccess) {
14261482 return DynamicCastResult::SuccessViaTake;
14271483 } else {
1428- swift_unknownObjectRetain (object );
1484+ swift_unknownObjectRetain (srcObject );
14291485 return DynamicCastResult::SuccessViaCopy;
14301486 }
14311487 }
@@ -1687,8 +1743,14 @@ tryCastToMetatype(
16871743 case MetadataKind::ObjCClassWrapper: {
16881744#if SWIFT_OBJC_INTEROP
16891745 // Some classes are actually metatypes
1690- void *object = getNonNullSrcObject (srcValue, srcType, destType);
1691- if (auto metatype = _getUnknownClassAsMetatype (object)) {
1746+ void *srcObject = getNonNullSrcObject (srcValue, srcType, destType);
1747+ // If object is null, then we're in compatibility mode.
1748+ // Continuing here at all is dangerous, but that's what the
1749+ // pre-Swift-5.4 casting logic did.
1750+ if (srcObject == nullptr ) {
1751+ return DynamicCastResult::Failure;
1752+ }
1753+ if (auto metatype = _getUnknownClassAsMetatype (srcObject)) {
16921754 auto srcInnerValue = reinterpret_cast <OpaqueValue *>(&metatype);
16931755 auto srcInnerType = swift_getMetatypeMetadata (metatype);
16941756 return tryCast (destLocation, destType, srcInnerValue, srcInnerType,
@@ -1798,6 +1860,12 @@ tryCastToExistentialMetatype(
17981860 // Some Obj-C classes are actually metatypes
17991861#if SWIFT_OBJC_INTEROP
18001862 void *srcObject = getNonNullSrcObject (srcValue, srcType, destType);
1863+ // If srcObject is null, we're in compatibility mode.
1864+ // Continuing here at al is dangerous, but that's what the
1865+ // pre-Swift-5.4 casting logic did.
1866+ if (srcObject == nullptr ) {
1867+ return DynamicCastResult::Failure;
1868+ }
18011869 if (auto metatype = _getUnknownClassAsMetatype (srcObject)) {
18021870 return _dynamicCastMetatypeToExistentialMetatype (
18031871 destLocation,
@@ -1962,14 +2030,19 @@ tryCast(
19622030 || srcKind == MetadataKind::ObjCClassWrapper
19632031 || srcKind == MetadataKind::ForeignClass) {
19642032 auto srcObject = getNonNullSrcObject (srcValue, srcType, destType);
1965- auto srcDynamicType = swift_getObjectType (srcObject);
1966- if (srcDynamicType != srcType) {
1967- srcFailureType = srcDynamicType;
1968- auto castResult = tryCastToDestType (
1969- destLocation, destType, srcValue, srcDynamicType,
1970- destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks);
1971- if (isSuccess (castResult)) {
1972- return castResult;
2033+ // If srcObject is null, we're in compability mode.
2034+ // But we can't lookup dynamic type for a null class reference, so
2035+ // just skip this in that case.
2036+ if (srcObject != nullptr ) {
2037+ auto srcDynamicType = swift_getObjectType (srcObject);
2038+ if (srcDynamicType != srcType) {
2039+ srcFailureType = srcDynamicType;
2040+ auto castResult = tryCastToDestType (
2041+ destLocation, destType, srcValue, srcDynamicType,
2042+ destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks);
2043+ if (isSuccess (castResult)) {
2044+ return castResult;
2045+ }
19732046 }
19742047 }
19752048 }
0 commit comments