Skip to content

Commit c633ea6

Browse files
author
ematejska
authored
Merge pull request #4416 from rjmccall/dynamic-casts-anyobject-3.0
Dynamic casts to AnyObject should succeed for all types via SwiftValue
2 parents 338f75a + 0f5b7b0 commit c633ea6

File tree

6 files changed

+86
-19
lines changed

6 files changed

+86
-19
lines changed

stdlib/public/core/ArrayCast.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public func _arrayForceCast<SourceElement, TargetElement>(
4747
return Array(_immutableCocoaArray: source._buffer._asCocoaArray())
4848
}
4949
#endif
50-
return _arrayConditionalCast(source)!
50+
return source.map { $0 as! TargetElement }
5151
}
5252

5353
internal struct _UnwrappingFailed : Error {}

stdlib/public/core/HashedCollections.swift.gyb

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1278,15 +1278,6 @@ internal func _stdlib_NSSet_allObjects(_ nss: _NSSet) ->
12781278

12791279
//===--- Compiler conversion/casting entry points for Set<Element> --------===//
12801280

1281-
func _impossible<T>(_:T.Type) -> T {
1282-
Builtin.unreachable()
1283-
}
1284-
1285-
func _unsafeUpcast<T, U>(_ x: T, to: U.Type) -> U {
1286-
_sanityCheck(x is U)
1287-
return x as? U ?? _impossible(U.self)
1288-
}
1289-
12901281
/// Perform a non-bridged upcast that always succeeds.
12911282
///
12921283
/// - Precondition: `BaseValue` is a base class or base `@objc`
@@ -1295,7 +1286,7 @@ public func _setUpCast<DerivedValue, BaseValue>(_ source: Set<DerivedValue>)
12951286
-> Set<BaseValue> {
12961287
var builder = _SetBuilder<BaseValue>(count: source.count)
12971288
for x in source {
1298-
builder.add(member: _unsafeUpcast(x, to: BaseValue.self))
1289+
builder.add(member: x as! BaseValue)
12991290
}
13001291
return builder.take()
13011292
}
@@ -2220,8 +2211,7 @@ public func _dictionaryUpCast<DerivedKey, DerivedValue, BaseKey, BaseValue>(
22202211
var result = Dictionary<BaseKey, BaseValue>(minimumCapacity: source.count)
22212212

22222213
for (k, v) in source {
2223-
result[_unsafeUpcast(k, to: BaseKey.self)]
2224-
= _unsafeUpcast(v, to: BaseValue.self)
2214+
result[k as! BaseKey] = v as! BaseValue
22252215
}
22262216
return result
22272217
}

stdlib/public/runtime/Casting.cpp

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,6 +1049,26 @@ static bool _dynamicCastToExistential(OpaqueValue *dest,
10491049
MetadataKind kind =
10501050
srcDynamicType ? srcDynamicType->getKind() : MetadataKind::Class;
10511051

1052+
// A fallback to use if we don't have a more specialized approach
1053+
// for a non-class type.
1054+
auto fallbackForNonClass = [&] {
1055+
#if SWIFT_OBJC_INTEROP
1056+
// If the destination type is a set of protocols that SwiftValue
1057+
// implements, we're fine.
1058+
if (findSwiftValueConformances(targetType->Protocols,
1059+
destExistential->getWitnessTables())) {
1060+
bool consumeValue = dynamicFlags & DynamicCastFlags::TakeOnSuccess;
1061+
destExistential->Value =
1062+
bridgeAnythingToSwiftValueObject(srcDynamicValue, srcDynamicType,
1063+
consumeValue);
1064+
maybeDeallocateSource(true);
1065+
return true;
1066+
}
1067+
#endif
1068+
1069+
return _fail(src, srcType, targetType, flags);
1070+
};
1071+
10521072
// If the source type is a value type, it cannot possibly conform
10531073
// to a class-bounded protocol.
10541074
switch (kind) {
@@ -1068,14 +1088,15 @@ static bool _dynamicCastToExistential(OpaqueValue *dest,
10681088
}
10691089
#endif
10701090
// Otherwise, metatypes aren't class objects.
1071-
return _fail(src, srcType, targetType, flags);
1091+
return fallbackForNonClass();
10721092
}
10731093

10741094
case MetadataKind::Class:
10751095
case MetadataKind::ObjCClassWrapper:
10761096
case MetadataKind::ForeignClass:
10771097
case MetadataKind::Existential:
1078-
// Handle these cases below.
1098+
// Handle the class cases below. Note that opaque existentials
1099+
// shouldn't get here because we should have drilled into them above.
10791100
break;
10801101

10811102
case MetadataKind::Struct:
@@ -1105,16 +1126,15 @@ static bool _dynamicCastToExistential(OpaqueValue *dest,
11051126
return success;
11061127
}
11071128
#endif
1108-
break;
1129+
SWIFT_FALLTHROUGH;
11091130

11101131
case MetadataKind::Function:
11111132
case MetadataKind::HeapLocalVariable:
11121133
case MetadataKind::HeapGenericLocalVariable:
11131134
case MetadataKind::ErrorObject:
11141135
case MetadataKind::Opaque:
11151136
case MetadataKind::Tuple:
1116-
// Will never succeed.
1117-
return _fail(src, srcType, targetType, flags);
1137+
return fallbackForNonClass();
11181138
}
11191139

11201140
// Check for protocol conformances and fill in the witness tables. If

stdlib/public/runtime/SwiftValue.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
#if SWIFT_OBJC_INTEROP
2525
#include <objc/runtime.h>
26+
#include "swift/Runtime/Metadata.h"
2627

2728
// _SwiftValue is an Objective-C class, but we shouldn't interface with it
2829
// directly as such. Keep the type opaque.
@@ -51,6 +52,12 @@ getValueFromSwiftValue(_SwiftValue *v);
5152
/// or nil if it is not.
5253
_SwiftValue *getAsSwiftValue(id object);
5354

55+
/// Find conformances for SwiftValue to the given list of protocols.
56+
///
57+
/// Returns true if SwiftValue does conform to all the protocols.
58+
bool findSwiftValueConformances(const ProtocolDescriptorList &protocols,
59+
const WitnessTable **tablesBuffer);
60+
5461
} // namespace swift
5562
#endif
5663

stdlib/public/runtime/SwiftValue.mm

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,49 @@ static size_t getSwiftValuePayloadAlignMask(const Metadata *type) {
212212
return nil;
213213
}
214214

215+
bool
216+
swift::findSwiftValueConformances(const ProtocolDescriptorList &protocols,
217+
const WitnessTable **tablesBuffer) {
218+
Class cls = nullptr;
219+
220+
// Note that currently we never modify tablesBuffer because
221+
// _SwiftValue doesn't conform to any protocols that need witness tables.
222+
223+
for (size_t i = 0, e = protocols.NumProtocols; i != e; ++i) {
224+
auto protocol = protocols[i];
225+
226+
// _SwiftValue does conform to AnyObject.
227+
switch (protocol->Flags.getSpecialProtocol()) {
228+
case SpecialProtocol::AnyObject:
229+
continue;
230+
231+
case SpecialProtocol::Error:
232+
return false;
233+
234+
case SpecialProtocol::None:
235+
break;
236+
}
237+
238+
// Otherwise, it only conforms to ObjC protocols. We specifically
239+
// don't want to say that _SwiftValue conforms to the Swift protocols
240+
// that NSObject conforms to because that would create a situation
241+
// where arguably an arbitrary type would conform to those protocols
242+
// by way of coercion through _SwiftValue. Eventually we want to
243+
// change _SwiftValue to not be an NSObject subclass at all.
244+
245+
if (protocol->Flags.getDispatchStrategy() != ProtocolDispatchStrategy::ObjC)
246+
return false;
247+
248+
if (!cls) cls = _getSwiftValueClass();
249+
250+
// Check whether the class conforms to the protocol.
251+
if (![cls conformsToProtocol: (Protocol*) protocol])
252+
return false;
253+
}
254+
255+
return true;
256+
}
257+
215258
@implementation _SwiftValue
216259

217260
+ (instancetype)allocWithZone:(NSZone *)zone {

test/Interpreter/generic_casts.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,15 @@ print(allToAll(type(of: C()), AnyObject.self)) // CHECK-NEXT: true
158158
// Bridging
159159
print(allToAll(0, AnyObject.self)) // CHECK-NEXT: true
160160

161+
// This will get bridged using _SwiftValue.
161162
struct NotBridged { var x: Int }
162-
print(allToAll(NotBridged(x: 0), AnyObject.self)) // CHECK-NEXT: false
163+
print(allToAll(NotBridged(x: 0), AnyObject.self)) // CHECK-NEXT: true
164+
print(allToAll(NotBridged(x: 0), NSCopying.self)) // CHECK-NEXT: true
165+
166+
// These casts fail (intentionally) even though _SwiftValue does
167+
// technically conform to these protocols through NSObject.
168+
print(allToAll(NotBridged(x: 0), CustomStringConvertible.self)) // CHECK-NEXT: false
169+
print(allToAll(NotBridged(x: 0), (AnyObject & CustomStringConvertible).self)) // CHECK-NEXT: false
163170

164171
//
165172
// rdar://problem/19482567

0 commit comments

Comments
 (0)