Overriding the equals method leads to values being assigned an incorrect type

I’ve encountered something that feels like a bug, but might also just be one of these dark corners coming from Java and known to be better left alone because we can’t work around them.

Using Scala 3.6.2, take the following code:

enum Type:
  case Num
  case Bool

enum TypeRepr[A <: Type]:
  case Num extends TypeRepr[Type.Num.type]
  case Bool extends TypeRepr[Type.Bool.type]

  override def equals(other: Any) = true

def to[A <: Type](repr: TypeRepr[A]): A = repr match
  case TypeRepr.Bool => Type.Bool
  case TypeRepr.Num  => Type.Num

@main def run =
  val nonsense: Type.Num.type = to(TypeRepr.Num)

  println(nonsense)

nonsense, of type Type.Num.type, is in fact Type.Bool, of an incompatible type.

My understanding of this is that the compiler trusts that if two values are equal, then they are of the same type, and reasons off of that premise. And my implementation provides an incorrect equals, thus leading the compiler to incorrect conclusions.

Is this something known, and if so, is there a list of such corner cases somewhere? Or should I report that as a bug?

1 Like

That is a bug. There was an ancient Scala 2 ticket on this question of what type can be inferred from equality.

The correcter code is:

def to[A <: Type](repr: TypeRepr[A]): A = repr match
  case _: TypeRepr.Bool.type => Type.Bool
  case _: TypeRepr.Num.type  => Type.Num

Your ticket has the same issue, where I would expect

case class Typing[A <: Type](expr: TypedExpr[A], tpe: A):
  def cast[B <: Type](to: B): Either[String, TypedExpr[B]] = this match
    case Typing(expr, _: to.type) => Right(expr)
    case _                  => Left("Failed")

though that doesn’t meet your expectation there.

Matching on x.type uses eq instead of equals.

1 Like

You mean _: to.type, right?

I didn’t think of using type comparisons, although that’s not always possible - sometime I actually need the value, and I need it well typed. But thanks, that’s a good workaround at least in this scenario!

I’m a little confused by the eq comment though. Surely matching on x.type uses asInstanceOf and not eq?

thanks, fixed.

https://scala-lang.org/files/archive/spec/2.13/08-pattern-matching.html#type-patterns

A singleton type pp.type. This type pattern matches only the value denoted by the path pp (the eq method is used to compare the matched value to pp).

And don’t call me Shirley.

… shirley? what?

Now then, Herr Snytt… at least he didn’t call you Abbie Ant Toe.

Regards, Mr Meaner.

it’s a meme, in modern parlance.

1 Like

Oh i thought I’d offended you somehow :slight_smile:

1 Like