Skip to content

Commit 232a314

Browse files
committed
Teach the dynamic-cast machinery how to cast collection element types.
1 parent 9870b8e commit 232a314

File tree

6 files changed

+412
-16
lines changed

6 files changed

+412
-16
lines changed

include/swift/SIL/DynamicCasts.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@ enum class DynamicCastFeasibility {
3939
WillFail,
4040
};
4141

42+
static inline DynamicCastFeasibility
43+
atWorst(DynamicCastFeasibility feasibility, DynamicCastFeasibility worstCase) {
44+
return (feasibility > worstCase ? worstCase : feasibility);
45+
}
46+
47+
static inline DynamicCastFeasibility
48+
atBest(DynamicCastFeasibility feasibility, DynamicCastFeasibility bestCase) {
49+
return (feasibility < bestCase ? bestCase : feasibility);
50+
}
51+
4252
/// Classify the feasibility of a dynamic cast. The source and target
4353
/// types should be unlowered formal types.
4454
DynamicCastFeasibility classifyDynamicCast(

lib/SIL/DynamicCasts.cpp

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,6 @@
1919
using namespace swift;
2020
using namespace Lowering;
2121

22-
static DynamicCastFeasibility weakenSuccess(DynamicCastFeasibility v) {
23-
if (v == DynamicCastFeasibility::WillSucceed)
24-
return DynamicCastFeasibility::MaySucceed;
25-
return v;
26-
}
27-
2822
static unsigned getAnyMetatypeDepth(CanType type) {
2923
unsigned depth = 0;
3024
while (auto metatype = dyn_cast<AnyMetatypeType>(type)) {
@@ -183,6 +177,14 @@ bool swift::isError(Module *M, CanType Ty) {
183177
return false;
184178
}
185179

180+
/// Given that a type is not statically known to be an optional type, check whether
181+
/// it might dynamically be an optional type.
182+
static bool canDynamicallyBeOptionalType(CanType type) {
183+
assert(!type.getAnyOptionalObjectType());
184+
return (isa<ArchetypeType>(type) || type.isExistentialType())
185+
&& !type.isAnyClassReferenceType();
186+
}
187+
186188
/// Try to classify the dynamic-cast relationship between two types.
187189
DynamicCastFeasibility
188190
swift::classifyDynamicCast(Module *M,
@@ -195,21 +197,30 @@ swift::classifyDynamicCast(Module *M,
195197
auto sourceObject = source.getAnyOptionalObjectType();
196198
auto targetObject = target.getAnyOptionalObjectType();
197199

198-
// A common level of optionality doesn't affect the feasibility.
200+
// A common level of optionality doesn't affect the feasibility,
201+
// except that we can't fold things to failure because nil inhabits
202+
// both types.
199203
if (sourceObject && targetObject) {
200-
return classifyDynamicCast(M, sourceObject, targetObject);
204+
return atWorst(classifyDynamicCast(M, sourceObject, targetObject),
205+
DynamicCastFeasibility::MaySucceed);
201206

202-
// Nor does casting to a more optional type.
207+
// Casting to a more optional type follows the same rule unless we
208+
// know that the source cannot dynamically be an optional value,
209+
// in which case we'll always just cast and inject into an optional.
203210
} else if (targetObject) {
204-
return classifyDynamicCast(M, source, targetObject,
205-
/* isSourceTypeExact */ false,
206-
isWholeModuleOpts);
211+
auto result = classifyDynamicCast(M, source, targetObject,
212+
/* isSourceTypeExact */ false,
213+
isWholeModuleOpts);
214+
if (canDynamicallyBeOptionalType(source))
215+
result = atWorst(result, DynamicCastFeasibility::MaySucceed);
216+
return result;
207217

208218
// Casting to a less-optional type can always fail.
209219
} else if (sourceObject) {
210-
return weakenSuccess(classifyDynamicCast(M, sourceObject, target,
211-
/* isSourceTypeExact */ false,
212-
isWholeModuleOpts));
220+
return atBest(classifyDynamicCast(M, sourceObject, target,
221+
/* isSourceTypeExact */ false,
222+
isWholeModuleOpts),
223+
DynamicCastFeasibility::MaySucceed);
213224
}
214225
assert(!sourceObject && !targetObject);
215226

@@ -472,6 +483,40 @@ swift::classifyDynamicCast(Module *M,
472483
return DynamicCastFeasibility::MaySucceed;
473484
}
474485

486+
// Check for a viable collection cast.
487+
if (auto sourceStruct = dyn_cast<BoundGenericStructType>(source)) {
488+
if (auto targetStruct = dyn_cast<BoundGenericStructType>(target)) {
489+
490+
// Both types have to be the same kind of collection.
491+
auto typeDecl = sourceStruct->getDecl();
492+
if (typeDecl == targetStruct->getDecl()) {
493+
auto sourceArgs = sourceStruct.getGenericArgs();
494+
auto targetArgs = targetStruct.getGenericArgs();
495+
496+
// Note that we can never say that a collection cast is impossible:
497+
// a cast can always succeed on an empty collection.
498+
499+
// Arrays and sets.
500+
if (typeDecl == M->getASTContext().getArrayDecl() ||
501+
typeDecl == M->getASTContext().getSetDecl()) {
502+
auto valueFeasibility =
503+
classifyDynamicCast(M, sourceArgs[0], targetArgs[0]);
504+
return atWorst(valueFeasibility,
505+
DynamicCastFeasibility::MaySucceed);
506+
507+
// Dictionaries.
508+
} else if (typeDecl == M->getASTContext().getDictionaryDecl()) {
509+
auto keyFeasibility =
510+
classifyDynamicCast(M, sourceArgs[0], targetArgs[0]);
511+
auto valueFeasibility =
512+
classifyDynamicCast(M, sourceArgs[1], targetArgs[1]);
513+
return atWorst(atBest(keyFeasibility, valueFeasibility),
514+
DynamicCastFeasibility::MaySucceed);
515+
}
516+
}
517+
}
518+
}
519+
475520
return DynamicCastFeasibility::WillFail;
476521
}
477522

stdlib/public/core/ArrayCast.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@
1616
//
1717
//===----------------------------------------------------------------------===//
1818

19+
@_silgen_name("_swift_arrayDownCastIndirect")
20+
public func _arrayDownCastIndirect<SourceValue, TargetValue>(
21+
_ source: UnsafePointer<Array<SourceValue>>,
22+
_ target: UnsafeMutablePointer<Array<TargetValue>>) {
23+
target.initialize(to: _arrayForceCast(source.pointee))
24+
}
25+
1926
/// Implements `source as! [TargetElement]`.
2027
///
2128
/// - Note: When SourceElement and TargetElement are both bridged verbatim, type
@@ -52,6 +59,18 @@ extension Optional {
5259
}
5360
}
5461

62+
@_silgen_name("_swift_arrayDownCastConditionalIndirect")
63+
public func _arrayDownCastConditionalIndirect<SourceValue, TargetValue>(
64+
_ source: UnsafePointer<Array<SourceValue>>,
65+
_ target: UnsafeMutablePointer<Array<TargetValue>>
66+
) -> Bool {
67+
if let result: Array<TargetValue> = _arrayConditionalCast(source.pointee) {
68+
target.initialize(to: result)
69+
return true
70+
}
71+
return false
72+
}
73+
5574
/// Implements `source as? [TargetElement]`: convert each element of
5675
/// `source` to a `TargetElement` and return the resulting array, or
5776
/// return `nil` if any element fails to convert.

stdlib/public/core/HashedCollections.swift.gyb

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,6 +1313,13 @@ public func _setBridgeToObjectiveC<SwiftValue, ObjCValue>(
13131313

13141314
#endif
13151315

1316+
@_silgen_name("_swift_setDownCastIndirect")
1317+
public func _setDownCastIndirect<SourceValue, TargetValue>(
1318+
_ source: UnsafePointer<Set<SourceValue>>,
1319+
_ target: UnsafeMutablePointer<Set<TargetValue>>) {
1320+
target.initialize(to: _setDownCast(source.pointee))
1321+
}
1322+
13161323
/// Implements a forced downcast. This operation should have O(1) complexity.
13171324
///
13181325
/// The cast can fail if bridging fails. The actual checks and bridging can be
@@ -1342,6 +1349,18 @@ public func _setDownCast<BaseValue, DerivedValue>(_ source: Set<BaseValue>)
13421349
return _setDownCastConditional(source)!
13431350
}
13441351

1352+
@_silgen_name("_swift_setDownCastConditionalIndirect")
1353+
public func _setDownCastConditionalIndirect<SourceValue, TargetValue>(
1354+
_ source: UnsafePointer<Set<SourceValue>>,
1355+
_ target: UnsafeMutablePointer<Set<TargetValue>>
1356+
) -> Bool {
1357+
if let result: Set<TargetValue> = _setDownCastConditional(source.pointee) {
1358+
target.initialize(to: result)
1359+
return true
1360+
}
1361+
return false
1362+
}
1363+
13451364
/// Implements a conditional downcast.
13461365
///
13471366
/// If the cast fails, the function returns `nil`. All checks should be
@@ -2249,6 +2268,14 @@ public func _dictionaryBridgeToObjectiveC<
22492268
}
22502269
#endif
22512270

2271+
@_silgen_name("_swift_dictionaryDownCastIndirect")
2272+
public func _dictionaryDownCastIndirect<SourceKey, SourceValue,
2273+
TargetKey, TargetValue>(
2274+
_ source: UnsafePointer<Dictionary<SourceKey, SourceValue>>,
2275+
_ target: UnsafeMutablePointer<Dictionary<TargetKey, TargetValue>>) {
2276+
target.initialize(to: _dictionaryDownCast(source.pointee))
2277+
}
2278+
22522279
/// Implements a forced downcast. This operation should have O(1) complexity.
22532280
///
22542281
/// The cast can fail if bridging fails. The actual checks and bridging can be
@@ -2293,6 +2320,20 @@ public func _dictionaryDownCast<BaseKey, BaseValue, DerivedKey, DerivedValue>(
22932320
return _dictionaryDownCastConditional(source)!
22942321
}
22952322

2323+
@_silgen_name("_swift_dictionaryDownCastConditionalIndirect")
2324+
public func _dictionaryDownCastConditionalIndirect<SourceKey, SourceValue,
2325+
TargetKey, TargetValue>(
2326+
_ source: UnsafePointer<Dictionary<SourceKey, SourceValue>>,
2327+
_ target: UnsafeMutablePointer<Dictionary<TargetKey, TargetValue>>
2328+
) -> Bool {
2329+
if let result: Dictionary<TargetKey, TargetValue>
2330+
= _dictionaryDownCastConditional(source.pointee) {
2331+
target.initialize(to: result)
2332+
return true
2333+
}
2334+
return false
2335+
}
2336+
22962337
/// Implements a conditional downcast.
22972338
///
22982339
/// If the cast fails, the function returns `nil`. All checks should be

stdlib/public/runtime/Casting.cpp

Lines changed: 137 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2148,6 +2148,133 @@ static bool tryDynamicCastBoxedSwiftValue(OpaqueValue *dest,
21482148
}
21492149
#endif
21502150

2151+
/// Nominal type descriptor for Swift.Array.
2152+
extern "C" const NominalTypeDescriptor _TMnSa;
2153+
2154+
/// Nominal type descriptor for Swift.Dictionary.
2155+
extern "C" const NominalTypeDescriptor _TMnVs10Dictionary;
2156+
2157+
/// Nominal type descriptor for Swift.Set.
2158+
extern "C" const NominalTypeDescriptor _TMnVs3Set;
2159+
2160+
SWIFT_CC(swift)
2161+
extern "C"
2162+
void _swift_arrayDownCastIndirect(OpaqueValue *destination,
2163+
OpaqueValue *source,
2164+
const Metadata *sourceValueType,
2165+
const Metadata *targetValueType);
2166+
2167+
SWIFT_CC(swift)
2168+
extern "C"
2169+
bool _swift_arrayDownCastConditionalIndirect(OpaqueValue *destination,
2170+
OpaqueValue *source,
2171+
const Metadata *sourceValueType,
2172+
const Metadata *targetValueType);
2173+
2174+
SWIFT_CC(swift)
2175+
extern "C"
2176+
void _swift_setDownCastIndirect(OpaqueValue *destination,
2177+
OpaqueValue *source,
2178+
const Metadata *sourceValueType,
2179+
const Metadata *targetValueType);
2180+
2181+
SWIFT_CC(swift)
2182+
extern "C"
2183+
bool _swift_setDownCastConditionalIndirect(OpaqueValue *destination,
2184+
OpaqueValue *source,
2185+
const Metadata *sourceValueType,
2186+
const Metadata *targetValueType);
2187+
2188+
SWIFT_CC(swift)
2189+
extern "C"
2190+
void _swift_dictionaryDownCastIndirect(OpaqueValue *destination,
2191+
OpaqueValue *source,
2192+
const Metadata *sourceKeyType,
2193+
const Metadata *sourceValueType,
2194+
const Metadata *targetKeyType,
2195+
const Metadata *targetValueType);
2196+
2197+
SWIFT_CC(swift)
2198+
extern "C"
2199+
bool _swift_dictionaryDownCastConditionalIndirect(OpaqueValue *destination,
2200+
OpaqueValue *source,
2201+
const Metadata *sourceKeyType,
2202+
const Metadata *sourceValueType,
2203+
const Metadata *targetKeyType,
2204+
const Metadata *targetValueType);
2205+
2206+
static bool _dynamicCastStructToStruct(OpaqueValue *destination,
2207+
OpaqueValue *source,
2208+
const StructMetadata *sourceType,
2209+
const StructMetadata *targetType,
2210+
DynamicCastFlags flags) {
2211+
if (sourceType == targetType)
2212+
return _succeed(destination, source, sourceType, flags);
2213+
2214+
// The two types have to be instantiations of the same type.
2215+
auto descriptor = sourceType->Description.get();
2216+
if (descriptor != targetType->Description.get())
2217+
return _fail(source, sourceType, targetType, flags);
2218+
2219+
auto sourceArgs = sourceType->getGenericArgs();
2220+
auto targetArgs = targetType->getGenericArgs();
2221+
2222+
bool result;
2223+
2224+
// Arrays.
2225+
if (descriptor == &_TMnSa) {
2226+
if (flags & DynamicCastFlags::Unconditional) {
2227+
_swift_arrayDownCastIndirect(source, destination,
2228+
sourceArgs[0], targetArgs[0]);
2229+
result = true;
2230+
} else {
2231+
result =
2232+
_swift_arrayDownCastConditionalIndirect(source, destination,
2233+
sourceArgs[0], targetArgs[0]);
2234+
}
2235+
2236+
// Dictionaries.
2237+
} else if (descriptor == &_TMnVs10Dictionary) {
2238+
if (flags & DynamicCastFlags::Unconditional) {
2239+
_swift_dictionaryDownCastIndirect(source, destination,
2240+
sourceArgs[0], sourceArgs[1],
2241+
targetArgs[0], targetArgs[1]);
2242+
result = true;
2243+
} else {
2244+
result =
2245+
_swift_dictionaryDownCastConditionalIndirect(source, destination,
2246+
sourceArgs[0], sourceArgs[1],
2247+
targetArgs[0], targetArgs[1]);
2248+
}
2249+
2250+
// Sets.
2251+
} else if (descriptor == &_TMnVs10Dictionary) {
2252+
if (flags & DynamicCastFlags::Unconditional) {
2253+
_swift_setDownCastIndirect(source, destination,
2254+
sourceArgs[0], targetArgs[0]);
2255+
result = true;
2256+
} else {
2257+
result =
2258+
_swift_setDownCastConditionalIndirect(source, destination,
2259+
sourceArgs[0], targetArgs[0]);
2260+
}
2261+
2262+
// Other struct types don't support dynamic covariance for now.
2263+
} else {
2264+
return _fail(source, sourceType, targetType, flags);
2265+
}
2266+
2267+
// The intrinsics above never consume the source value.
2268+
bool shouldDestroySource =
2269+
result ? flags & DynamicCastFlags::TakeOnSuccess
2270+
: flags & DynamicCastFlags::DestroyOnFailure;
2271+
if (shouldDestroySource) {
2272+
sourceType->vw_destroy(source);
2273+
}
2274+
2275+
return result;
2276+
}
2277+
21512278
/// Perform a dynamic cast to an arbitrary type.
21522279
SWIFT_RT_ENTRY_VISIBILITY
21532280
bool swift::swift_dynamicCast(OpaqueValue *dest,
@@ -2307,6 +2434,16 @@ bool swift::swift_dynamicCast(OpaqueValue *dest,
23072434
break;
23082435
}
23092436

2437+
case MetadataKind::Struct:
2438+
// Collection casts.
2439+
if (targetType->getKind() == MetadataKind::Struct) {
2440+
return _dynamicCastStructToStruct(dest, src,
2441+
cast<StructMetadata>(srcType),
2442+
cast<StructMetadata>(targetType),
2443+
flags);
2444+
}
2445+
break;
2446+
23102447
case MetadataKind::ExistentialMetatype:
23112448
case MetadataKind::Enum:
23122449
case MetadataKind::Optional:
@@ -2316,7 +2453,6 @@ bool swift::swift_dynamicCast(OpaqueValue *dest,
23162453
case MetadataKind::ErrorObject:
23172454
case MetadataKind::Metatype:
23182455
case MetadataKind::Opaque:
2319-
case MetadataKind::Struct:
23202456
case MetadataKind::Tuple:
23212457
break;
23222458
}

0 commit comments

Comments
 (0)