Skip to content

Commit 1615915

Browse files
committed
Runtime: Dynamic casts to subclass existentials
1 parent e032d91 commit 1615915

File tree

5 files changed

+94
-23
lines changed

5 files changed

+94
-23
lines changed

stdlib/public/runtime/Casting.cpp

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -411,8 +411,19 @@ static bool _conformsToProtocol(const OpaqueValue *value,
411411
/// list of conformances.
412412
static bool _conformsToProtocols(const OpaqueValue *value,
413413
const Metadata *type,
414-
const ProtocolDescriptorList &protocols,
414+
const ExistentialTypeMetadata *existentialType,
415415
const WitnessTable **conformances) {
416+
if (auto *superclass = existentialType->getSuperclassConstraint()) {
417+
if (!swift_dynamicCastMetatype(type, superclass))
418+
return false;
419+
}
420+
421+
if (existentialType->isClassBounded()) {
422+
if (!Metadata::isAnyKindOfClass(type->getKind()))
423+
return false;
424+
}
425+
426+
auto &protocols = existentialType->Protocols;
416427
for (unsigned i = 0, n = protocols.NumProtocols; i != n; ++i) {
417428
const ProtocolDescriptor *protocol = protocols[i];
418429
if (!_conformsToProtocol(value, type, protocol, conformances))
@@ -876,7 +887,7 @@ static bool _dynamicCastToExistential(OpaqueValue *dest,
876887
#if SWIFT_OBJC_INTEROP
877888
// If the destination type is a set of protocols that SwiftValue
878889
// implements, we're fine.
879-
if (findSwiftValueConformances(targetType->Protocols,
890+
if (findSwiftValueConformances(targetType,
880891
destExistential->getWitnessTables())) {
881892
bool consumeValue = dynamicFlags & DynamicCastFlags::TakeOnSuccess;
882893
destExistential->Value =
@@ -963,7 +974,7 @@ static bool _dynamicCastToExistential(OpaqueValue *dest,
963974
// container with a class instance to AnyObject. In this case no check is
964975
// necessary.
965976
if (srcDynamicType && !_conformsToProtocols(srcDynamicValue, srcDynamicType,
966-
targetType->Protocols,
977+
targetType,
967978
destExistential->getWitnessTables()))
968979
return fallbackForNonDirectConformance();
969980

@@ -981,7 +992,7 @@ static bool _dynamicCastToExistential(OpaqueValue *dest,
981992

982993
// Check for protocol conformances and fill in the witness tables.
983994
if (!_conformsToProtocols(srcDynamicValue, srcDynamicType,
984-
targetType->Protocols,
995+
targetType,
985996
destExistential->getWitnessTables()))
986997
return fallbackForNonDirectConformance();
987998

@@ -1015,7 +1026,7 @@ static bool _dynamicCastToExistential(OpaqueValue *dest,
10151026
assert(targetType->Protocols.NumProtocols == 1);
10161027
const WitnessTable *errorWitness;
10171028
if (!_conformsToProtocols(srcDynamicValue, srcDynamicType,
1018-
targetType->Protocols,
1029+
targetType,
10191030
&errorWitness))
10201031
return fallbackForNonDirectConformance();
10211032

@@ -1052,6 +1063,8 @@ static bool _dynamicCastToExistential(OpaqueValue *dest,
10521063
static const void *
10531064
_dynamicCastUnknownClassToExistential(const void *object,
10541065
const ExistentialTypeMetadata *targetType) {
1066+
// FIXME: check superclass constraint here.
1067+
10551068
for (unsigned i = 0, e = targetType->Protocols.NumProtocols; i < e; ++i) {
10561069
const ProtocolDescriptor *protocol = targetType->Protocols[i];
10571070

@@ -1848,18 +1861,14 @@ static bool _dynamicCastMetatypeToExistentialMetatype(OpaqueValue *dest,
18481861
dyn_cast<ExistentialTypeMetadata>(targetInstanceType)) {
18491862
// Check for conformance to all the protocols.
18501863
// TODO: collect the witness tables.
1851-
auto &protocols = targetInstanceTypeAsExistential->Protocols;
18521864
const WitnessTable **conformance
18531865
= writeDestMetatype ? destMetatype->getWitnessTables() : nullptr;
1854-
for (unsigned i = 0, n = protocols.NumProtocols; i != n; ++i) {
1855-
const ProtocolDescriptor *protocol = protocols[i];
1856-
if (!_conformsToProtocol(nullptr, srcMetatype, protocol, conformance)) {
1857-
if (flags & DynamicCastFlags::Unconditional)
1858-
swift_dynamicCastFailure(srcMetatype, targetType);
1859-
return false;
1860-
}
1861-
if (conformance && protocol->Flags.needsWitnessTable())
1862-
++conformance;
1866+
if (!_conformsToProtocols(nullptr, srcMetatype,
1867+
targetInstanceTypeAsExistential,
1868+
conformance)) {
1869+
if (flags & DynamicCastFlags::Unconditional)
1870+
swift_dynamicCastFailure(srcMetatype, targetType);
1871+
return false;
18631872
}
18641873

18651874
if (writeDestMetatype)

stdlib/public/runtime/SwiftValue.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ getValueFromSwiftValue(_SwiftValue *v);
5252
/// or nil if it is not.
5353
_SwiftValue *getAsSwiftValue(id object);
5454

55-
/// Find conformances for SwiftValue to the given list of protocols.
55+
/// Find conformances for SwiftValue to the given existential type.
5656
///
5757
/// Returns true if SwiftValue does conform to all the protocols.
58-
bool findSwiftValueConformances(const ProtocolDescriptorList &protocols,
58+
bool findSwiftValueConformances(const ExistentialTypeMetadata *existentialType,
5959
const WitnessTable **tablesBuffer);
6060

6161
} // namespace swift

stdlib/public/runtime/SwiftValue.mm

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,14 @@ static size_t getSwiftValuePayloadAlignMask(const Metadata *type) {
213213
}
214214

215215
bool
216-
swift::findSwiftValueConformances(const ProtocolDescriptorList &protocols,
216+
swift::findSwiftValueConformances(const ExistentialTypeMetadata *existentialType,
217217
const WitnessTable **tablesBuffer) {
218+
// _SwiftValue never satisfies a superclass constraint.
219+
if (existentialType->getSuperclassConstraint() != nullptr)
220+
return false;
221+
222+
auto &protocols = existentialType->Protocols;
223+
218224
Class cls = nullptr;
219225

220226
// Note that currently we never modify tablesBuffer because

test/Interpreter/subclass_existentials.swift

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -444,25 +444,23 @@ SubclassExistentialsTestSuite.test("Failing scalar downcast to subclass existent
444444
}
445445
}
446446

447-
// FIXME
448-
449447
SubclassExistentialsTestSuite.test("Failing dynamic downcast to subclass existential") {
450448
do {
451449
let baseInt: Base<Int> = Base<Int>(x: 123, y: 321)
452450

453-
//expectNil(cast(baseInt, to: ((Base<Int>) & P).self))
451+
expectNil(cast(baseInt, to: ((Base<Int>) & P).self))
454452
}
455453

456454
do {
457455
let r: R = Base<Int>(x: 123, y: 321)
458456

459-
//expectNil(cast(r, to: ((Base<Int>) & P).self))
457+
expectNil(cast(r, to: ((Base<Int>) & P).self))
460458
}
461459

462460
do {
463461
let conformsToP = ConformsToP(protocolInit: ())
464462

465-
//expectNil(cast(conformsToP, to: ((Base<Int>) & P).self))
463+
expectNil(cast(conformsToP, to: ((Base<Int>) & P).self))
466464
}
467465
}
468466

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//===--- subclass_existentials.swift --------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// RUN: rm -rf %t
14+
// RUN: mkdir -p %t
15+
// RUN: %target-build-swift %s -o %t/a.out -Xfrontend -enable-experimental-subclass-existentials
16+
// RUN: %target-run %t/a.out
17+
// REQUIRES: executable_test
18+
// REQUIRES: objc_interop
19+
//
20+
21+
import StdlibUnittest
22+
import Foundation
23+
24+
// FIXME: Actually write proper tests here.
25+
26+
func cast<T, U>(_ t: T, to: U.Type) -> U? {
27+
return t as? U
28+
}
29+
30+
protocol CP : class {}
31+
@objc protocol OP {}
32+
33+
class C {}
34+
35+
class D : C, OP {}
36+
37+
var SubclassExistentialsTestSuite = TestSuite("SubclassExistentials")
38+
39+
SubclassExistentialsTestSuite.test("Metatype self-conformance") {
40+
expectFalse(CP.self is AnyObject.Type)
41+
expectEqual(OP.self, OP.self as AnyObject.Type)
42+
43+
expectNil(cast(CP.self, to: AnyObject.Type.self))
44+
// FIXME
45+
expectNil(cast(OP.self, to: AnyObject.Type.self))
46+
47+
// FIXME: Sema says 'always true', runtime says false.
48+
//
49+
// Runtime code had a FIXME in it already, so I think Sema is right.
50+
expectFalse(OP.self is AnyObject.Type)
51+
expectFalse((C & OP).self is AnyObject.Type)
52+
expectFalse((C & OP).self is OP.Type)
53+
54+
expectTrue(D.self is (C & OP).Type)
55+
expectFalse(OP.self is (C & OP).Type)
56+
}
57+
58+
runAllTests()

0 commit comments

Comments
 (0)