Skip to content

Commit 241301b

Browse files
iampatbrownstephencelistgrapperon
authored
Added check for class constrained existential (#65)
* Added check for class constrained existential * Replaced XCTAssertIdentical with XCTAssertTrue * Update Tests/CasePathsTests/CasePathsTests.swift Co-authored-by: Thomas Grapperon <[email protected]> Co-authored-by: Stephen Celis <[email protected]> Co-authored-by: Thomas Grapperon <[email protected]>
1 parent f73f493 commit 241301b

File tree

2 files changed

+28
-2
lines changed

2 files changed

+28
-2
lines changed

Sources/CasePaths/EnumReflection.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,8 +281,8 @@ extension Strategy {
281281
}
282282
self.init(tag: tag, assumedAssociatedValueType: Value.self)
283283

284-
} else if ExistentialMetadata(avType) != nil {
285-
if avType == Error.self {
284+
} else if let avMetadata = ExistentialMetadata(avType) {
285+
if avType == Error.self || avMetadata.isClassConstrained {
286286
// For Objective-C interop, the Error existential is a pointer to an NSError-compatible
287287
// (and thus AnyObject-compatible) object.
288288
let strategy = Strategy<Enum, AnyObject>(nonExistentialTag: tag)
@@ -540,6 +540,10 @@ private struct ExistentialMetadata: Metadata {
540540
self.ptr = unsafeBitCast(type, to: UnsafeRawPointer.self)
541541
guard self.kind == .existential else { return nil }
542542
}
543+
544+
var isClassConstrained: Bool {
545+
self.ptr.advanced(by: pointerSize).load(as: UInt32.self) & 0x8000_0000 == 0
546+
}
543547
}
544548

545549
private struct FieldDescriptor {

Tests/CasePathsTests/CasePathsTests.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ private struct UnexpectedNil: Error {}
1111
protocol TestProtocol {}
1212
extension Int: TestProtocol {}
1313

14+
protocol TestClassProtocol: AnyObject {}
15+
1416
final class CasePathsTests: XCTestCase {
1517
func testSimplePayload() {
1618
enum Enum { case payload(Int) }
@@ -359,6 +361,26 @@ final class CasePathsTests: XCTestCase {
359361
}
360362
}
361363

364+
func testClassConstrainedExistential() {
365+
class Class: TestClassProtocol {}
366+
enum Enum {
367+
case proto(TestClassProtocol)
368+
case int(Int)
369+
}
370+
let protoPath = /Enum.proto
371+
let intPath = /Enum.int
372+
373+
let object = Class()
374+
375+
for _ in 1...2 {
376+
XCTAssertNil(protoPath.extract(from: .int(100)))
377+
XCTAssertTrue(protoPath.extract(from: .proto(object)) === object)
378+
379+
XCTAssertNil(intPath.extract(from: .proto(object)))
380+
XCTAssertEqual(intPath.extract(from: .int(100)), 100)
381+
}
382+
}
383+
362384
func testContravariantEmbed() {
363385
enum Enum {
364386
// associated value type is TestProtocol existential

0 commit comments

Comments
 (0)