Skip to content

Commit 524cfae

Browse files
authored
[Dynamic Casting] Overhauled Runtime (#33561)
* Dynamic Cast Rework: Runtime This is a completely refactored version of the core swift_dynamicCast runtime method. This fixes a number of bugs, especially in the handling of multiply-wrapped types such as Optional within Any. The result should be much closer to the behavior specified by `docs/DynamicCasting.md`. Most of the type-specific logic is simply copied over from the earlier implementation, but the overall structure has been changed to be uniformly recursive. In particular, this provides uniform handling of Optional, existentials, Any and other common "box" types along all paths. The consistent structure should also be easier to update in the future with new general types. Benchmarking does not show any noticable performance implications. **Temporarily**, the old implementation is still available. Setting the environment variable `SWIFT_OLD_DYNAMIC_CAST_RUNTIME` before launching a program will use the old runtime implementation. This is only to facilitate testing; once the new implementation is stable, I expect to completely remove the old implementation.
1 parent 102d638 commit 524cfae

File tree

11 files changed

+3417
-268
lines changed

11 files changed

+3417
-268
lines changed

stdlib/public/core/DebuggerSupport.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,14 @@ public enum _DebuggerSupport {
152152
// yes, a type can lie and say it's a class when it's not since we only
153153
// check the displayStyle - but then the type would have a custom Mirror
154154
// anyway, so there's that...
155-
let willExpand = mirror.displayStyle != .`class` || value is CustomReflectable?
155+
let isNonClass = mirror.displayStyle != .`class`
156+
let isCustomReflectable: Bool
157+
if let value = value {
158+
isCustomReflectable = value is CustomReflectable
159+
} else {
160+
isCustomReflectable = true
161+
}
162+
let willExpand = isNonClass || isCustomReflectable
156163

157164
let count = mirror._children.count
158165
let bullet = isRoot && (count == 0 || !willExpand) ? ""

stdlib/public/runtime/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ set(swift_runtime_sources
3333
CompatibilityOverride.cpp
3434
CygwinPort.cpp
3535
Demangle.cpp
36+
DynamicCast.cpp
3637
Enum.cpp
3738
EnvironmentVariables.cpp
3839
ErrorObjectCommon.cpp

stdlib/public/runtime/Casting.cpp

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,15 @@ _dynamicCastClassMetatype(const ClassMetadata *sourceType,
339339
return nullptr;
340340
}
341341

342+
#if !SWIFT_OBJC_INTEROP // __SwiftValue is a native class
343+
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL
344+
bool swift_unboxFromSwiftValueWithType(OpaqueValue *source,
345+
OpaqueValue *result,
346+
const Metadata *destinationType);
347+
/// Nominal type descriptor for Swift.__SwiftValue
348+
extern "C" const ClassDescriptor NOMINAL_TYPE_DESCR_SYM(s12__SwiftValueC);
349+
#endif
350+
342351
/// Dynamically cast a class instance to a Swift class type.
343352
static const void *swift_dynamicCastClassImpl(const void *object,
344353
const ClassMetadata *targetType) {
@@ -351,10 +360,28 @@ static const void *swift_dynamicCastClassImpl(const void *object,
351360
}
352361
#endif
353362

354-
auto isa = _swift_getClassOfAllocated(object);
363+
auto srcType = _swift_getClassOfAllocated(object);
355364

356-
if (_dynamicCastClassMetatype(isa, targetType))
365+
if (_dynamicCastClassMetatype(srcType, targetType))
357366
return object;
367+
368+
#if !SWIFT_OBJC_INTEROP // __SwiftValue is a native class on Linux
369+
if (srcType->getKind() == MetadataKind::Class
370+
&& targetType->getKind() == MetadataKind::Class) {
371+
auto srcClassType = cast<ClassMetadata>(srcType);
372+
auto srcDescr = srcClassType->getDescription();
373+
if (srcDescr == &NOMINAL_TYPE_DESCR_SYM(s12__SwiftValueC)) {
374+
auto srcValue = reinterpret_cast<OpaqueValue *>(&object);
375+
void *result;
376+
auto destLocation = reinterpret_cast<OpaqueValue *>(&result);
377+
if (swift_unboxFromSwiftValueWithType(srcValue, destLocation, targetType)) {
378+
swift_unknownObjectRelease(const_cast<void *>(object));
379+
return result;
380+
}
381+
}
382+
}
383+
#endif
384+
358385
return nullptr;
359386
}
360387

@@ -3262,3 +3289,30 @@ HeapObject *_swift_bridgeToObjectiveCUsingProtocolIfPossible(
32623289
#define OVERRIDE_CASTING COMPATIBILITY_OVERRIDE
32633290
#include "CompatibilityOverride.def"
32643291

3292+
// XXX TODO XXX REMOVE XXX TRANSITION SHIM XXX
3293+
// XXX TODO XXX REMOVE XXX TRANSITION SHIM XXX
3294+
// XXX TODO XXX REMOVE XXX TRANSITION SHIM XXX
3295+
3296+
// A way for the new implementation to call the old one, so we
3297+
// can support switching between the two until the new one is
3298+
// fully settled.
3299+
3300+
// XXX TODO XXX Once the new implementation is stable, remove the following,
3301+
// swift_dynamicCastImpl above, and all the other code above that only exists to
3302+
// support that. (Don't forget _dynamicCastToExistential!!) This file should
3303+
// be only ~1400 lines when you're done.
3304+
3305+
extern "C" {
3306+
bool swift_dynamicCast_OLD(OpaqueValue *destLocation,
3307+
OpaqueValue *srcValue,
3308+
const Metadata *srcType,
3309+
const Metadata *destType,
3310+
DynamicCastFlags flags)
3311+
{
3312+
return swift_dynamicCastImpl(destLocation, srcValue, srcType, destType, flags);
3313+
}
3314+
}
3315+
3316+
// XXX TODO XXX REMOVE XXX TRANSITION SHIM XXX
3317+
// XXX TODO XXX REMOVE XXX TRANSITION SHIM XXX
3318+
// XXX TODO XXX REMOVE XXX TRANSITION SHIM XXX

stdlib/public/runtime/CompatibilityOverride.def

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#ifdef OVERRIDE
4141
# define OVERRIDE_METADATALOOKUP OVERRIDE
4242
# define OVERRIDE_CASTING OVERRIDE
43+
# define OVERRIDE_DYNAMICCASTING OVERRIDE
4344
# define OVERRIDE_OBJC OVERRIDE
4445
# define OVERRIDE_FOREIGN OVERRIDE
4546
# define OVERRIDE_PROTOCOLCONFORMANCE OVERRIDE
@@ -52,6 +53,9 @@
5253
# ifndef OVERRIDE_CASTING
5354
# define OVERRIDE_CASTING(...)
5455
# endif
56+
# ifndef OVERRIDE_DYNAMICCASTING
57+
# define OVERRIDE_DYNAMICCASTING(...)
58+
# endif
5559
# ifndef OVERRIDE_OBJC
5660
# define OVERRIDE_OBJC(...)
5761
# endif
@@ -69,12 +73,12 @@
6973
# endif
7074
#endif
7175

72-
OVERRIDE_CASTING(dynamicCast, bool, , , swift::,
73-
(OpaqueValue *dest, OpaqueValue *src,
74-
const Metadata *srcType,
75-
const Metadata *targetType,
76-
DynamicCastFlags flags),
77-
(dest, src, srcType, targetType, flags))
76+
OVERRIDE_DYNAMICCASTING(dynamicCast, bool, , , swift::,
77+
(OpaqueValue *dest, OpaqueValue *src,
78+
const Metadata *srcType,
79+
const Metadata *targetType,
80+
DynamicCastFlags flags),
81+
(dest, src, srcType, targetType, flags))
7882

7983

8084
OVERRIDE_CASTING(dynamicCastClass, const void *, , , swift::,
@@ -212,6 +216,7 @@ OVERRIDE_FOREIGN(dynamicCastForeignClassUnconditional, const void *, , , swift::
212216
#undef OVERRIDE
213217
#undef OVERRIDE_METADATALOOKUP
214218
#undef OVERRIDE_CASTING
219+
#undef OVERRIDE_DYNAMICCASTING
215220
#undef OVERRIDE_OBJC
216221
#undef OVERRIDE_FOREIGN
217222
#undef OVERRIDE_PROTOCOLCONFORMANCE

0 commit comments

Comments
 (0)