Skip to content

Commit 8107928

Browse files
committed
Fill in extended existential support for type(of:) and mirror
type(of:) now returns the dynamic type of the contents of an extended existential (just like it does for a regular existential) Mirror can now reflect fields of a value stored inside an extended existential (just like it can with a regular existential). This requires type(of:) support, since Mirror internals use that to determine how to reflect a value. Resolves rdar://102906195
1 parent 7d4f62f commit 8107928

File tree

4 files changed

+228
-11
lines changed

4 files changed

+228
-11
lines changed

stdlib/public/runtime/Casting.cpp

+34
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,40 @@ findDynamicValueAndType(OpaqueValue *value, const Metadata *type,
689689
}
690690
}
691691
}
692+
693+
case MetadataKind::ExtendedExistential: {
694+
auto *existentialType = cast<ExtendedExistentialTypeMetadata>(type);
695+
696+
switch (existentialType->Shape->Flags.getSpecialKind()) {
697+
case ExtendedExistentialTypeShape::SpecialKind::None: {
698+
auto opaqueContainer =
699+
reinterpret_cast<OpaqueExistentialContainer *>(value);
700+
auto innerValue = const_cast<OpaqueValue *>(opaqueContainer->projectValue());
701+
auto innerType = opaqueContainer->Type;
702+
return findDynamicValueAndType(innerValue, innerType,
703+
outValue, outType, inoutCanTake, false,
704+
isTargetExistentialMetatype);
705+
}
706+
case ExtendedExistentialTypeShape::SpecialKind::Class: {
707+
auto classContainer =
708+
reinterpret_cast<ClassExistentialContainer *>(value);
709+
outType = swift_getObjectType((HeapObject *)classContainer->Value);
710+
outValue = reinterpret_cast<OpaqueValue *>(&classContainer->Value);
711+
return;
712+
}
713+
case ExtendedExistentialTypeShape::SpecialKind::Metatype: {
714+
auto srcExistentialContainer =
715+
reinterpret_cast<ExistentialMetatypeContainer *>(value);
716+
outType = swift_getMetatypeMetadata(srcExistentialContainer->Value);
717+
outValue = reinterpret_cast<OpaqueValue *>(&srcExistentialContainer->Value);
718+
return;
719+
}
720+
case ExtendedExistentialTypeShape::SpecialKind::ExplicitLayout: {
721+
swift_unreachable("Extended Existential with explicit layout not yet implemented");
722+
return;
723+
}
724+
}
725+
}
692726

693727
case MetadataKind::Metatype:
694728
case MetadataKind::ExistentialMetatype: {

stdlib/public/runtime/ReflectionMirror.cpp

+44-11
Original file line numberDiff line numberDiff line change
@@ -90,18 +90,51 @@ unwrapExistential(const Metadata *T, OpaqueValue *Value) {
9090
// TODO: Should look through existential metatypes too, but it doesn't
9191
// really matter yet since we don't have any special mirror behavior for
9292
// concrete metatypes yet.
93-
while (T->getKind() == MetadataKind::Existential) {
94-
auto *existential
95-
= static_cast<const ExistentialTypeMetadata *>(T);
96-
97-
// Unwrap the existential container.
98-
T = existential->getDynamicType(Value);
99-
Value = existential->projectValue(Value);
100-
101-
// Existential containers can end up nested in some cases due to generic
102-
// abstraction barriers. Repeat in case we have a nested existential.
93+
for (;;) {
94+
switch (T->getKind()) {
95+
case MetadataKind::Existential: {
96+
auto *existential
97+
= static_cast<const ExistentialTypeMetadata *>(T);
98+
T = existential->getDynamicType(Value);
99+
Value = existential->projectValue(Value);
100+
break;
101+
}
102+
case MetadataKind::ExtendedExistential: {
103+
auto *existential
104+
= static_cast<const ExtendedExistentialTypeMetadata *>(T);
105+
switch (existential->Shape->Flags.getSpecialKind()) {
106+
case ExtendedExistentialTypeShape::SpecialKind::None: {
107+
auto opaqueContainer =
108+
reinterpret_cast<OpaqueExistentialContainer *>(Value);
109+
T = opaqueContainer->Type;
110+
Value = const_cast<OpaqueValue *>(opaqueContainer->projectValue());
111+
break;
112+
}
113+
case ExtendedExistentialTypeShape::SpecialKind::Class: {
114+
auto classContainer =
115+
reinterpret_cast<ClassExistentialContainer *>(Value);
116+
T = swift_getObjectType((HeapObject *)classContainer->Value);
117+
Value = reinterpret_cast<OpaqueValue *>(&classContainer->Value);
118+
break;
119+
}
120+
case ExtendedExistentialTypeShape::SpecialKind::Metatype: {
121+
auto srcExistentialContainer =
122+
reinterpret_cast<ExistentialMetatypeContainer *>(Value);
123+
T = swift_getMetatypeMetadata(srcExistentialContainer->Value);
124+
Value = reinterpret_cast<OpaqueValue *>(&srcExistentialContainer->Value);
125+
break;
126+
}
127+
case ExtendedExistentialTypeShape::SpecialKind::ExplicitLayout: {
128+
swift_unreachable("Extended Existential with explicit layout not supported");
129+
break;
130+
}
131+
}
132+
break;
133+
}
134+
default:
135+
return std::make_tuple(T, Value);
136+
}
103137
}
104-
return std::make_tuple(T, Value);
105138
}
106139

107140
static void copyWeakFieldContents(OpaqueValue *destContainer, const Metadata *type, OpaqueValue *fieldData) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// RUN: %empty-directory(%t)
2+
//
3+
// RUN: %target-build-swift -swift-version 5 -g -Onone -module-name a %s -o %t/a.swift5.Onone.out
4+
// RUN: %target-codesign %t/a.swift5.Onone.out
5+
// RUN: %target-run %t/a.swift5.Onone.out
6+
//
7+
// RUN: %target-build-swift -swift-version 6 -g -Onone -module-name a %s -o %t/a.swift6.Onone.out
8+
// RUN: %target-codesign %t/a.swift6.Onone.out
9+
// RUN: %target-run %t/a.swift6.Onone.out
10+
//
11+
// RUN: %target-build-swift -swift-version 5 -g -O -module-name a %s -o %t/a.swift5.O.out
12+
// RUN: %target-codesign %t/a.swift5.O.out
13+
// RUN: %target-run %t/a.swift5.O.out
14+
//
15+
// RUN: %target-build-swift -swift-version 6 -g -O -module-name a %s -o %t/a.swift6.O.out
16+
// RUN: %target-codesign %t/a.swift6.O.out
17+
// RUN: %target-run %t/a.swift6.O.out
18+
//
19+
// REQUIRES: executable_test
20+
// REQUIRES: concurrency
21+
// UNSUPPORTED: use_os_stdlib
22+
// UNSUPPORTED: back_deployment_runtime
23+
24+
import StdlibUnittest
25+
26+
func blackhole<T>(_ t: T) { }
27+
28+
private func runtimeCast<T,U>(_ from: T, to: U.Type) -> U? {
29+
return from as? U
30+
}
31+
32+
let CastsTests = TestSuite("Casts")
33+
34+
protocol Box<T> {
35+
associatedtype T
36+
var t: T { get }
37+
}
38+
39+
CastsTests.test("type(of:) should look through extended existentials (none)") {
40+
struct C<T>: Box { var t: T }
41+
func genericErase<T>(_ value: T) -> Any { value }
42+
let c: any Box<Int> = C(t: 42)
43+
if #available(macOS 13.0, *) {
44+
let x = genericErase(c)
45+
expectEqual("C<Int>", "\(type(of:x))")
46+
} else {
47+
expectEqual(0, 1)
48+
}
49+
}
50+
51+
protocol OBox<T> : AnyObject {
52+
associatedtype T
53+
var t: T { get }
54+
}
55+
56+
CastsTests.test("type(of:) should look through extended existentials (class)") {
57+
class C<T>: OBox {
58+
var t: T
59+
init(t: T) { self.t = t }
60+
}
61+
func genericErase<T>(_ value: T) -> Any { value }
62+
let c: any OBox<Int> = C(t: 42)
63+
if #available(macOS 13.0, *) {
64+
let x = genericErase(c)
65+
expectEqual("C<Int>", "\(type(of:x))")
66+
} else {
67+
expectEqual(0, 1)
68+
}
69+
}
70+
71+
72+
CastsTests.test("type(of:) should look through extended existentials (metatype)") {
73+
struct C<T>: Box { var t: T }
74+
func genericErase<T>(_ value: T) -> Any { value }
75+
let t: any Box<Int>.Type = C<Int>.self
76+
if #available(macOS 13.0, *) {
77+
let x = genericErase(t)
78+
expectEqual("C<Int>.Type", "\(type(of:x))")
79+
} else {
80+
expectEqual(0, 1)
81+
}
82+
}
83+
84+
runAllTests()

test/stdlib/Mirror.swift

+66
Original file line numberDiff line numberDiff line change
@@ -921,6 +921,72 @@ mirrors.test("class/Cluster") {
921921
//===--- Miscellaneous ----------------------------------------------------===//
922922
//===----------------------------------------------------------------------===//
923923

924+
protocol Box<Value> {
925+
associatedtype Value
926+
var value: Value {get}
927+
}
928+
929+
mirrors.test("Extended Existential (struct)") {
930+
struct Container<Value>: Box {
931+
var value: Value
932+
}
933+
func genericErase<T>(_ value: T) -> Any {
934+
value
935+
}
936+
let container: any Box<Int> = Container(value: 42)
937+
if #available(macOS 13, *) {
938+
let subject = genericErase(container)
939+
let mirror = Mirror(reflecting: subject)
940+
let children = mirror.children
941+
expectEqual(1, children.count)
942+
let first = children.first!
943+
expectEqual("value", first.label)
944+
expectEqual(42, first.value as! Int)
945+
}
946+
}
947+
948+
protocol OBox<Value>: AnyObject {
949+
associatedtype Value
950+
var value: Value {get}
951+
}
952+
953+
mirrors.test("Extended Existential (class)") {
954+
class Container<Value>: OBox {
955+
var value: Value
956+
init(value: Value) { self.value = value }
957+
}
958+
func genericErase<T>(_ value: T) -> Any {
959+
value
960+
}
961+
let container: any OBox<Int> = Container(value: 42)
962+
if #available(macOS 13, *) {
963+
let subject = genericErase(container)
964+
let mirror = Mirror(reflecting: subject)
965+
let children = mirror.children
966+
expectEqual(1, children.count)
967+
let first = children.first!
968+
expectEqual("value", first.label)
969+
expectEqual(42, first.value as! Int)
970+
}
971+
}
972+
973+
mirrors.test("Extended Existential (metatype)") {
974+
class Container<Value>: Box {
975+
var value: Value
976+
init(value: Value) { self.value = value }
977+
}
978+
func genericErase<T>(_ value: T) -> Any {
979+
value
980+
}
981+
let t: any Box<Int>.Type = Container<Int>.self
982+
if #available(macOS 13, *) {
983+
let subject = genericErase(t)
984+
let mirror = Mirror(reflecting: subject)
985+
let children = mirror.children
986+
expectEqual(0, children.count)
987+
}
988+
}
989+
924990
mirrors.test("Addressing") {
925991
let m0 = Mirror(reflecting: [1, 2, 3])
926992
expectEqual(1, m0.descendant(0) as? Int)

0 commit comments

Comments
 (0)