@@ -116,6 +116,11 @@ static bool unexpectedNullIsFatal() {
116
116
return true ; // Placeholder for an upcoming check.
117
117
}
118
118
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.
119
124
static HeapObject * getNonNullSrcObject (OpaqueValue *srcValue,
120
125
const Metadata *srcType,
121
126
const Metadata *destType) {
@@ -130,11 +135,17 @@ static HeapObject * getNonNullSrcObject(OpaqueValue *srcValue,
130
135
" while trying to cast value of type '%s' (%p)"
131
136
" to '%s' (%p)%s\n " ;
132
137
if (unexpectedNullIsFatal ()) {
138
+ // By default, Swift 5.4 and later issue a fatal error.
133
139
swift::fatalError (/* flags = */ 0 , msg,
134
140
srcTypeName.c_str (), srcType,
135
141
destTypeName.c_str (), destType,
136
142
" " );
137
143
} 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.
138
149
swift::warning (/* flags = */ 0 , msg,
139
150
srcTypeName.c_str (), srcType,
140
151
destTypeName.c_str (), destType,
@@ -325,7 +336,9 @@ tryCastFromClassToObjCBridgeable(
325
336
_getBridgedObjectiveCType (MetadataState::Complete, destType,
326
337
destBridgeWitness).Value ;
327
338
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)) {
329
342
destFailureType = targetBridgedClass;
330
343
return DynamicCastResult::Failure;
331
344
}
@@ -437,8 +450,12 @@ tryCastToSwiftClass(
437
450
switch (srcType->getKind ()) {
438
451
case MetadataKind::Class: // Swift class => Swift class
439
452
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)) {
442
459
auto castObject = const_cast <void *>(t);
443
460
*(reinterpret_cast <void **>(destLocation)) = castObject;
444
461
if (takeOnSuccess) {
@@ -480,6 +497,17 @@ tryCastToObjectiveCClass(
480
497
case MetadataKind::ObjCClassWrapper: // Obj-C class => Obj-C class
481
498
case MetadataKind::ForeignClass: { // CF class => Obj-C class
482
499
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
+ }
483
511
auto destObjCClass = destObjCType->Class ;
484
512
if (auto resultObject
485
513
= swift_dynamicCastObjCClass (srcObject, destObjCClass)) {
@@ -519,6 +547,19 @@ tryCastToForeignClass(
519
547
case MetadataKind::ObjCClassWrapper: // Obj-C class => CF class
520
548
case MetadataKind::ForeignClass: { // CF class => CF class
521
549
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
+ }
522
563
if (auto resultObject
523
564
= swift_dynamicCastForeignClass (srcObject, destClassType)) {
524
565
*reinterpret_cast <const void **>(destLocation) = resultObject;
@@ -694,6 +735,10 @@ struct ObjCBridgeMemo {
694
735
// Use the dynamic ISA type of the object always (Note that this
695
736
// also implicitly gives us the ObjC type for a CF object.)
696
737
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
+ }
697
742
Class srcObjCType = object_getClass ((id)srcObject);
698
743
// Fail if the ObjC object is not a subclass of the bridge class.
699
744
while (srcObjCType != targetBridgedObjCClass) {
@@ -1417,15 +1462,26 @@ tryCastToClassExistential(
1417
1462
case MetadataKind::ObjCClassWrapper:
1418
1463
case MetadataKind::Class:
1419
1464
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
+ }
1421
1477
if (_conformsToProtocols (srcValue, srcType,
1422
1478
destExistentialType,
1423
1479
destExistentialLocation->getWitnessTables ())) {
1424
- destExistentialLocation->Value = object ;
1480
+ destExistentialLocation->Value = srcObject ;
1425
1481
if (takeOnSuccess) {
1426
1482
return DynamicCastResult::SuccessViaTake;
1427
1483
} else {
1428
- swift_unknownObjectRetain (object );
1484
+ swift_unknownObjectRetain (srcObject );
1429
1485
return DynamicCastResult::SuccessViaCopy;
1430
1486
}
1431
1487
}
@@ -1687,8 +1743,14 @@ tryCastToMetatype(
1687
1743
case MetadataKind::ObjCClassWrapper: {
1688
1744
#if SWIFT_OBJC_INTEROP
1689
1745
// 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)) {
1692
1754
auto srcInnerValue = reinterpret_cast <OpaqueValue *>(&metatype);
1693
1755
auto srcInnerType = swift_getMetatypeMetadata (metatype);
1694
1756
return tryCast (destLocation, destType, srcInnerValue, srcInnerType,
@@ -1798,6 +1860,12 @@ tryCastToExistentialMetatype(
1798
1860
// Some Obj-C classes are actually metatypes
1799
1861
#if SWIFT_OBJC_INTEROP
1800
1862
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
+ }
1801
1869
if (auto metatype = _getUnknownClassAsMetatype (srcObject)) {
1802
1870
return _dynamicCastMetatypeToExistentialMetatype (
1803
1871
destLocation,
@@ -1962,14 +2030,19 @@ tryCast(
1962
2030
|| srcKind == MetadataKind::ObjCClassWrapper
1963
2031
|| srcKind == MetadataKind::ForeignClass) {
1964
2032
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
+ }
1973
2046
}
1974
2047
}
1975
2048
}
0 commit comments