Why isn't static checking for "can be cast" between two generic placeholders possible?

I do not like having to rely on something like this CastError. Why is it useful? Something to do with class inheritance?

/// An error that represents casting gone wrong. πŸ§™β€β™€οΈπŸ™€
public enum CastError: Error {
  /// An undesired cast is possible.
  case possible

  /// An desired cast is not possible.
  case impossible
}

public extension CastError {
  /// `nil` if  an `Instance` can be cast to `Desired`. Otherwise, `.impossible`.
  init?<Instance, Desired>(_: Instance, desired _: Desired.Type) {
    self.init(Instance.self, desired: Desired.self)
  }

  /// `nil` if  a `Source` can be cast to `Desired`. Otherwise, `.impossible`.
  init?<Source, Desired>(_: Source.Type, desired _: Desired.Type) {
    if Source.self is Desired.Type
    { return nil }

    self = .impossible
  }

  /// `nil` if  an `Instance` cannot be cast to `Undesired`. Otherwise, `.possible`.
  init?<Instance, Undesired>(_: Instance, undesired _: Undesired.Type) {
    self.init(Instance.self, undesired: Undesired.self)
  }

  /// `nil` if  a `Source` cannot be cast to `Undesired`. Otherwise, `.possible`.
  init?<Source, Undesired>(_: Source.Type, undesired _: Undesired.Type) {
    guard Source.self is Undesired.Type
    else { return nil }

    self = .possible
  }
}
final class CastErrorTestCase: XCTestCase {
  func test_Self() {
    XCTAssertNoThrow(
      try cast(0, to: Int.self)
    )
    XCTAssertThrowsError(
      try failCast(of: 0, to: Int.self)
    )

    XCTAssertNoThrow(
      try failCast(of: 0, to: Class.self)
    )
    XCTAssertThrowsError(
      try cast(0, to: Class.self)
    )

    XCTAssertThrowsError(
      try failCast(of: Class(), to: Class.self)
    )
    XCTAssertNoThrow( try failCast(of: Class(), to: Double.self) )
  }

  func test_protocol() {
    XCTAssertNoThrow(
      try cast(Class(), to: Protocol.self)
    )
    XCTAssertThrowsError(
      try failCast(of: Class(), to: Protocol.self)
    )
  }

  func test_AnyObject() {
    XCTAssertThrowsError(
      try cast(0, to: AnyObject.self)
    )
    XCTAssertNoThrow(
      try failCast(of: true, to: AnyObject.self)
    )

    XCTAssertThrowsError(
      try failCast(of: Class(), to: AnyObject.self)
    )
    XCTAssertNoThrow(
      try failCast(of: Class() as Protocol, to: AnyObject.self)
    )
    XCTAssertThrowsError(
      try cast(Class() as Protocol, to: AnyObject.self)
    )
  }

  func test_inheritance() {
    XCTAssertNoThrow(
      try failCast(of: SuperClass(), to: Class.self)
    )

    XCTAssertNoThrow(
      try cast(Class(), to: SuperClass.self)
    )
    XCTAssertThrowsError(
      try failCast(of: Class(), to: SuperClass.self)
    )
  }
}

private protocol Protocol { }
private class SuperClass { }
private final class Class: SuperClass, Protocol { }

private func cast<Instance, DesiredCast>(
  _ instance: Instance, to desiredCastType: DesiredCast.Type
) throws {
  if let error = CastError(instance, desired: desiredCastType)
  { throw error }
}

private func failCast<Instance, UndesiredCast>(
  of instance: Instance, to undesiredCastType: UndesiredCast.Type
) throws {
  if let error = CastError(instance, undesired: undesiredCastType)
  { throw error }
}
public extension Equatable {
  /// A closure that equates another instance to this intance.
  /// - Parameters:
  ///   - _: Use the metatype for `Castable` to avoid explicit typing.
  /// - Throws: `CastError.impossible` if a `Castable` can't be cast to `Self`.
  func getEquals<Castable>(_: Castable.Type = Castable.self) throws -> (Castable) -> Bool {
    if let error = CastError(self, desired: Castable.self)
    { throw error }

    return { self == $0 as? Self }
  }
}
/// A type-erased equatable value.
///
/// An `Equatable` instance is stored as a "`Cast`".
/// Only instances that can be cast to that type can be `==`'d with the `AnyEquatable`.
public struct AnyEquatable<Cast> {
  public init<Equatable: Swift.Equatable>(_ equatable: Equatable) throws {
    equals = try equatable.getEquals()
    cast = equatable as! Cast
  }

  private let equals: (Cast) -> Bool
  private let cast: Cast
}

extension AnyEquatable: Swift.Equatable {
  public static func == (equatable0: Self, equatable1: Self) -> Bool {
    equatable0 == equatable1.cast
  }
}

public extension AnyEquatable {
  static func == (equatable: Self, cast: Cast) -> Bool {
    equatable.equals(cast)
  }

  static func == (cast: Cast, equatable: Self) -> Bool {
    equatable.equals(cast)
  }
}
let cupcake = "🧁"
let notCake = 0xca_e

let cupcakeEquals: (Any) -> Bool = try cupcake.getEquals()
XCTAssert( cupcakeEquals(cupcake) )
XCTAssertFalse( cupcakeEquals(notCake) )

let notCakeEquals = try notCake.getEquals(Any.self)
XCTAssert( notCakeEquals(notCake) )
XCTAssertFalse( notCakeEquals(cupcake) )

XCTAssertThrowsError( try cupcake.getEquals(Int.self) )

let anyEquatable = try AnyEquatable<Any>(cupcake)
XCTAssertEqual( anyEquatable, try .init(cupcake) )
XCTAssert(anyEquatable == cupcake)
XCTAssertFalse(notCake == anyEquatable)

What are you actually trying to achieve?

It’s worth noting that your castable-testing does not match the behavior of Equatable:

class Super: Equatable {
  var n: Int
  
  init(_ n: Int) { self.n = n }
  
  static func == (lhs: Super, rhs: Super) -> Bool {
    return lhs.n == rhs.n
  }
}

class SubA: Super {}
class SubB: Super {}

print(SubA(1) == SubB(1))     // true
print(SubA(1) == SubB(2))     // false

The above code compiles and runs properly, however your code will throw an error when trying to check equality between SubA and SubB instances.

I am achieving various things. Various people have problems related to this. Please do not critique the example; I am not interested in critique of the example. (But I think Equatable.== is static and not class because it doesn't make sense in a class hierarchy.)

There are alternatives for defining CastErrors but the other ways allow for stuff to be considered AnyObject when it's a value type or protocol.

To be fair, I read it three times and I still don't know what you're trying to do. So I can't really help in that case.

Are you trying to do this?

enum CastError: Error {
  case impossible
}

public extension Equatable {
  /// A closure that equates another instance to this intance.
  /// - Parameters:
  ///   - _: Use the metatype for `Castable` to avoid explicit typing.
  /// - Throws: `CastError.Impossible` if a `Castable` can't be cast to `Self`.
  func getEquals<Castable>(_: Castable.Type = Castable.self) throws -> (Castable) -> Bool {
    if !(Self.self is Castable.Type) {
      throw CastError.impossible
    }

    return { self == $0 as? Self }
  }
}

Good improvement on the default there! Thanks! :smiley_cat:

But aside from that, you just moved the logic out of the type that should be part of the compiler.

Please stop not talking about the question though, or I will delete that example so as not to distract anybody.

Honestly, I just moved things around like you said. It would help to have some explanation about what syntax/api you're trying to achieve. That'd help more than some uncommented example. I have not the slightest idea what the thing that should be part of the compiler you're referring to is.

Ok, so you want to be able to write this?:

func equals<Source, Desired>(lhs: Source, rhs: Desired)
  where Source: Desired { ... }

That's not obvious from the post. Unfortunately, I don't have a good answer for that. Though it seems that it has to do with type inference instead of the constraint itself. Since compiler can pretty much set Desired to the common ancestor, making it more-or-less:

func equals<Ancestor>(lhs: Ancestor, rhs: Ancestor)

Can you clarify what you want, if not to discuss your example?