@@ -110,6 +110,17 @@ extern "C" const StructDescriptor NOMINAL_TYPE_DESCR_SYM(Sh);
110
110
// / Nominal type descriptor for Swift.String.
111
111
extern " C" const StructDescriptor NOMINAL_TYPE_DESCR_SYM (SS);
112
112
113
+ // If this returns `true`, then we will call `fatalError` when we encounter a
114
+ // null reference in a storage locaation whose type does not allow null.
115
+ static bool unexpectedNullIsFatal () {
116
+ return true ; // Placeholder for an upcoming check.
117
+ }
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.
113
124
static HeapObject * getNonNullSrcObject (OpaqueValue *srcValue,
114
125
const Metadata *srcType,
115
126
const Metadata *destType) {
@@ -120,12 +131,27 @@ static HeapObject * getNonNullSrcObject(OpaqueValue *srcValue,
120
131
121
132
std::string srcTypeName = nameForMetadata (srcType);
122
133
std::string destTypeName = nameForMetadata (destType);
123
- swift::fatalError (/* flags = */ 0 ,
124
- " Found unexpected null pointer value"
134
+ const char *msg = " Found unexpected null pointer value"
125
135
" while trying to cast value of type '%s' (%p)"
126
- " to '%s' (%p)\n " ,
127
- srcTypeName.c_str (), srcType,
128
- destTypeName.c_str (), destType);
136
+ " to '%s' (%p)%s\n " ;
137
+ if (unexpectedNullIsFatal ()) {
138
+ // By default, Swift 5.4 and later issue a fatal error.
139
+ swift::fatalError (/* flags = */ 0 , msg,
140
+ srcTypeName.c_str (), srcType,
141
+ destTypeName.c_str (), destType,
142
+ " " );
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.
149
+ swift::warning (/* flags = */ 0 , msg,
150
+ srcTypeName.c_str (), srcType,
151
+ destTypeName.c_str (), destType,
152
+ " : Continuing with null object, but expect problems later." );
153
+ }
154
+ return object;
129
155
}
130
156
131
157
/* *****************************************************************************/
@@ -310,7 +336,9 @@ tryCastFromClassToObjCBridgeable(
310
336
_getBridgedObjectiveCType (MetadataState::Complete, destType,
311
337
destBridgeWitness).Value ;
312
338
void *srcObject = getNonNullSrcObject (srcValue, srcType, destType);
313
- 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)) {
314
342
destFailureType = targetBridgedClass;
315
343
return DynamicCastResult::Failure;
316
344
}
@@ -422,8 +450,12 @@ tryCastToSwiftClass(
422
450
switch (srcType->getKind ()) {
423
451
case MetadataKind::Class: // Swift class => Swift class
424
452
case MetadataKind::ObjCClassWrapper: { // Obj-C class => Swift class
425
- void *object = getNonNullSrcObject (srcValue, srcType, destType);
426
- 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)) {
427
459
auto castObject = const_cast <void *>(t);
428
460
*(reinterpret_cast <void **>(destLocation)) = castObject;
429
461
if (takeOnSuccess) {
@@ -465,6 +497,17 @@ tryCastToObjectiveCClass(
465
497
case MetadataKind::ObjCClassWrapper: // Obj-C class => Obj-C class
466
498
case MetadataKind::ForeignClass: { // CF class => Obj-C class
467
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
+ }
468
511
auto destObjCClass = destObjCType->Class ;
469
512
if (auto resultObject
470
513
= swift_dynamicCastObjCClass (srcObject, destObjCClass)) {
@@ -504,6 +547,19 @@ tryCastToForeignClass(
504
547
case MetadataKind::ObjCClassWrapper: // Obj-C class => CF class
505
548
case MetadataKind::ForeignClass: { // CF class => CF class
506
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
+ }
507
563
if (auto resultObject
508
564
= swift_dynamicCastForeignClass (srcObject, destClassType)) {
509
565
*reinterpret_cast <const void **>(destLocation) = resultObject;
@@ -679,6 +735,10 @@ struct ObjCBridgeMemo {
679
735
// Use the dynamic ISA type of the object always (Note that this
680
736
// also implicitly gives us the ObjC type for a CF object.)
681
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
+ }
682
742
Class srcObjCType = object_getClass ((id)srcObject);
683
743
// Fail if the ObjC object is not a subclass of the bridge class.
684
744
while (srcObjCType != targetBridgedObjCClass) {
@@ -1402,15 +1462,26 @@ tryCastToClassExistential(
1402
1462
case MetadataKind::ObjCClassWrapper:
1403
1463
case MetadataKind::Class:
1404
1464
case MetadataKind::ForeignClass: {
1405
- 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
+ }
1406
1477
if (_conformsToProtocols (srcValue, srcType,
1407
1478
destExistentialType,
1408
1479
destExistentialLocation->getWitnessTables ())) {
1409
- destExistentialLocation->Value = object ;
1480
+ destExistentialLocation->Value = srcObject ;
1410
1481
if (takeOnSuccess) {
1411
1482
return DynamicCastResult::SuccessViaTake;
1412
1483
} else {
1413
- swift_unknownObjectRetain (object );
1484
+ swift_unknownObjectRetain (srcObject );
1414
1485
return DynamicCastResult::SuccessViaCopy;
1415
1486
}
1416
1487
}
@@ -1672,8 +1743,14 @@ tryCastToMetatype(
1672
1743
case MetadataKind::ObjCClassWrapper: {
1673
1744
#if SWIFT_OBJC_INTEROP
1674
1745
// Some classes are actually metatypes
1675
- void *object = getNonNullSrcObject (srcValue, srcType, destType);
1676
- 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)) {
1677
1754
auto srcInnerValue = reinterpret_cast <OpaqueValue *>(&metatype);
1678
1755
auto srcInnerType = swift_getMetatypeMetadata (metatype);
1679
1756
return tryCast (destLocation, destType, srcInnerValue, srcInnerType,
@@ -1783,6 +1860,12 @@ tryCastToExistentialMetatype(
1783
1860
// Some Obj-C classes are actually metatypes
1784
1861
#if SWIFT_OBJC_INTEROP
1785
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
+ }
1786
1869
if (auto metatype = _getUnknownClassAsMetatype (srcObject)) {
1787
1870
return _dynamicCastMetatypeToExistentialMetatype (
1788
1871
destLocation,
@@ -1947,14 +2030,19 @@ tryCast(
1947
2030
|| srcKind == MetadataKind::ObjCClassWrapper
1948
2031
|| srcKind == MetadataKind::ForeignClass) {
1949
2032
auto srcObject = getNonNullSrcObject (srcValue, srcType, destType);
1950
- auto srcDynamicType = swift_getObjectType (srcObject);
1951
- if (srcDynamicType != srcType) {
1952
- srcFailureType = srcDynamicType;
1953
- auto castResult = tryCastToDestType (
1954
- destLocation, destType, srcValue, srcDynamicType,
1955
- destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks);
1956
- if (isSuccess (castResult)) {
1957
- 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
+ }
1958
2046
}
1959
2047
}
1960
2048
}
0 commit comments