scala / scala3

The Scala 3 compiler, also known as Dotty.
https://dotty.epfl.ch
Apache License 2.0
5.88k stars 1.06k forks source link

Inconsistent handling of match types on dependent types when overriding #21265

Open m8nmueller opened 4 months ago

m8nmueller commented 4 months ago

Compiler version

3.4.2

Minimized code

trait P:
  type Q

type Qaux[QQ] = P { type Q = QQ }
type QP[P] = P match
  case Qaux[q] => q

trait PString extends P:
  type Q = String

trait User[X <: P]:
  def get(x: X): QP[x.type]
  def use(f: (x: X) => QP[x.type] => String): String

class StringUser extends User[PString]:
  def get(x: PString): QP[x.type] = ???
  def use(f: (x: PString) => QP[x.type] => String): String = ???

Output

-- Error: test.scala:16:6 ------------------------------------------------------
16 |class StringUser extends User[PString]:
   |      ^
   |class StringUser needs to be abstract, since def use(f: (x: X) => QP[x.type] => String): String in trait User is not defined 
   |(Note that
   | parameter (x: X) => QP[x.type] => String in def use(f: (x: X) => QP[x.type] => String): String in trait User does not match
   | parameter (x: PString) => String => String in def use(f: (x: PString) => String => String): String in class StringUser
   | )
1 error found

Expectation

The compiler should realize that the two function types are exactly the result of replacing the type parameter X with the type PString, as it does for the non-function type in get.

natsukagami commented 4 months ago

This also happens with paths instead of match types

//> using scala 3.6.0-RC1-bin-SNAPSHOT

trait P:
  type Q

trait PString extends P:
  type Q = String

trait User[X <: P]:
  def get(x: X): x.Q
  def use(f: (x: X) => x.Q => String): String

class StringUser extends User[PString]:
  def get(x: PString): x.Q = ???
  def use(f: (x: PString) => x.Q => String): String = ???

I suspect it's something to do with variance...

natsukagami commented 4 months ago
//> using scala 3.6.0-RC1-bin-SNAPSHOT

trait P:
  type Q

trait PString extends P:
  type Q = String

type F[X <: P] = (x: X) => x.Q => String

val u = summon[F[PString] <:< ((x: PString) => x.Q => String)]

The compiler cannot prove this

EugeneFlesselle commented 3 months ago

Being invariant is enough:

type Inv[X]
type F[X <: P] = (x: X) => Inv[x.Q]

val v0: F[PString] = ???
val v2: (x: PString) => Inv[x.Q] = v0 // error