Repository: pointfreeco/swift-case-paths (swift)

Issue:
EXC_BAD_ACCESS extracting existential
This was partially solved in https://github.com/pointfreeco/swift-case-paths/issues/53 but it seems that the fix is needed for all object-bound existential types.

Here's a rudimentary case that reproduces the crash (tested with Xcode 13.1, iOS 13.5 simulator):
```swift
private protocol TestObjectProtocol: AnyObject {}

func testObjectBoundExistentialPayload() {
  class Class: TestObjectProtocol {}
  enum Enum {
    case int(Int)
    case objectBoundExistential(TestObjectProtocol)
  }
  let intPath = /Enum.int
  let object = Class()
  for _ in 1...2 {
      XCTAssertNil(intPath.extract(from: .objectBoundExistential(object)))
  }
}
```

Pull request description:
Possible option to fix #63

Patch:
diff --git a/Sources/CasePaths/EnumReflection.swift b/Sources/CasePaths/EnumReflection.swift
index 344cba40..9f50fb36 100644
--- a/Sources/CasePaths/EnumReflection.swift
+++ b/Sources/CasePaths/EnumReflection.swift
@@ -281,8 +281,8 @@ extension Strategy {
       }
       self.init(tag: tag, assumedAssociatedValueType: Value.self)
 
-    } else if ExistentialMetadata(avType) != nil {
-      if avType == Error.self {
+    } else if let avMetadata = ExistentialMetadata(avType) {
+      if avType == Error.self || avMetadata.isClassConstrained {
         // For Objective-C interop, the Error existential is a pointer to an NSError-compatible
         // (and thus AnyObject-compatible) object.
         let strategy = Strategy<Enum, AnyObject>(nonExistentialTag: tag)
@@ -540,6 +540,10 @@ private struct ExistentialMetadata: Metadata {
     self.ptr = unsafeBitCast(type, to: UnsafeRawPointer.self)
     guard self.kind == .existential else { return nil }
   }
+
+  var isClassConstrained: Bool {
+    self.ptr.advanced(by: pointerSize).load(as: UInt32.self) & 0x8000_0000 == 0
+  }
 }
 
 private struct FieldDescriptor {


Test patch:
diff --git a/Tests/CasePathsTests/CasePathsTests.swift b/Tests/CasePathsTests/CasePathsTests.swift
index 628baeca..8f654844 100644
--- a/Tests/CasePathsTests/CasePathsTests.swift
+++ b/Tests/CasePathsTests/CasePathsTests.swift
@@ -11,6 +11,8 @@ private struct UnexpectedNil: Error {}
 protocol TestProtocol {}
 extension Int: TestProtocol {}
 
+protocol TestClassProtocol: AnyObject {}
+
 final class CasePathsTests: XCTestCase {
   func testSimplePayload() {
     enum Enum { case payload(Int) }
@@ -359,6 +361,26 @@ final class CasePathsTests: XCTestCase {
     }
   }
 
+  func testClassConstrainedExistential() {
+    class Class: TestClassProtocol {}
+    enum Enum {
+      case proto(TestClassProtocol)
+      case int(Int)
+    }
+    let protoPath = /Enum.proto
+    let intPath = /Enum.int
+
+    let object = Class()
+
+    for _ in 1...2 {
+      XCTAssertNil(protoPath.extract(from: .int(100)))
+      XCTAssertTrue(protoPath.extract(from: .proto(object)) === object)
+
+      XCTAssertNil(intPath.extract(from: .proto(object)))
+      XCTAssertEqual(intPath.extract(from: .int(100)), 100)
+    }
+  }
+
   func testContravariantEmbed() {
     enum Enum {
       // associated value type is TestProtocol existential