Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Match type upper bounds lead to unexpected compiler behavior #15816

Open
Bersier opened this issue Aug 3, 2022 · 4 comments
Open

Match type upper bounds lead to unexpected compiler behavior #15816

Bersier opened this issue Aug 3, 2022 · 4 comments

Comments

@Bersier
Copy link
Contributor

Bersier commented Aug 3, 2022

Compiler version

3.1.3

Minimized code

sealed trait IntT
sealed trait NatT extends IntT
final case class Zero() extends NatT
final case class Succ[+N <: NatT](n: N) extends NatT
final case class Minus[+N <: Succ[NatT]](n: N) extends IntT

type NatDif[X <: NatT, Y <: NatT] <: IntT = Y match
  case _0 => X
  case Succ[y] => X match
    case _0 => Minus[Y]
    case Succ[x] => NatDif[x, y]

type NatDivH[X <: NatT, Y <: Succ[NatT], Z <: NatT] <: (NatT, NatT) = NatDif[X, Y] match
  case Minus[_] => (Z, X)
  case _0 => (Succ[Z], _0)
  case Succ[x] => NatDivH[Succ[x], Y, Succ[Z]]

type NatDiv[X <: NatT, Y <: Succ[NatT]] <: (NatT, NatT) = Y match
  case _1 => (X, _0)
  case _ => NatDivH[X, Y, _0]

type Second[P] = P match
  case (_, y) => y

type NatRem[X <: NatT, Y <: Succ[NatT]] = Second[NatDiv[X, Y]]

type NatRem2[X <: NatT, Y <: Succ[NatT]] <: NatT = NatDiv[X, Y] match
  case (_, y) => y

@main def main(): Unit =
  _1: Second[NatDiv[_3, _2]] // No error, as expected.
  _1: Second[NatDiv[_3, _1]] // Error, as expected.
  _1: NatRem[_3, _1] // No error, unexpected.
  _1: NatRem2[_3, _2] // Error, unexpected.

type _0 = Zero
type _1 = Succ[_0]
type _2 = Succ[_1]
type _3 = Succ[_2]

val _0: _0 = Zero()
val _1: _1 = Succ(_0)

Output

See here.

The unexpected error for _1: NatRem2[_3, _2]:

Match type reduction failed since selector  (Succ[_0], Succ[Zero])
matches none of the cases

    case (_, y) => y

Expectation

Removing the type bounds from the match type definitions fixes the unexpected errors or lack thereof. It is unexpected that the behavior of the code should change based on redundant type bounds. Moreover, the error messages seem misleading at best. See this thread.

@Bersier Bersier added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels Aug 3, 2022
@dwijnand dwijnand added the stat:needs minimization Needs a self contained minimization label Aug 3, 2022
@WojciechMazur WojciechMazur added area:typer and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels Aug 4, 2022
@Bersier
Copy link
Contributor Author

Bersier commented Aug 4, 2022

I managed to minimize the code further (it should also be easier to follow):

sealed trait NatT
final case class Zero() extends NatT
final case class Succ[+N <: NatT](n: N) extends NatT

type ZeroAndSecondNat[X <: NatT, Y <: Succ[NatT]] <: (NatT, NatT) = Y match
  case Succ[_] => (_0, Y)

type Second[P] = P match
  case (_, y) => y

type SecondNat[X <: NatT, Y <: Succ[NatT]] = Second[ZeroAndSecondNat[X, Y]]

type SecondNat2[X <: NatT, Y <: Succ[NatT]] <: NatT = ZeroAndSecondNat[X, Y] match
  case (_, y) => y

@main def main(): Unit =
  _2: Second[ZeroAndSecondNat[_1, _2]] // No error, as expected.
  _2: Second[ZeroAndSecondNat[_1, _1]] // Error, as expected.
  _2: SecondNat[_1, _1]                // No error, unexpected.
  _2: SecondNat2[_1, _2]               // Error, unexpected.

type _0 = Zero
type _1 = Succ[_0]
type _2 = Succ[_1]

val _0: _0 = Zero()
val _1: _1 = Succ(_0)
val _2: _2 = Succ(_1)

@dwijnand Is that small enough?

@dwijnand dwijnand removed the stat:needs minimization Needs a self contained minimization label Aug 5, 2022
@dwijnand
Copy link
Member

dwijnand commented Aug 5, 2022

Yep, thank you.

@dwijnand dwijnand self-assigned this Aug 5, 2022
@dwijnand
Copy link
Member

Likely the same cause as #15926.

@dwijnand dwijnand removed their assignment Oct 25, 2022
@Bersier
Copy link
Contributor Author

Bersier commented Feb 12, 2024

I tried this with Scala 3.4.0-RC4, and the minimized code now behaves as expected. However, the original code still behaves oddly. Here is some new minimized code:

sealed trait NatT
final case class Zero() extends NatT
final case class Succ[+N <: NatT](n: N) extends NatT

type NatDiv[X <: NatT, Y <: Succ[NatT]] <: (NatT, NatT) = Y match
  case _1 => (X, _0)
  case _ => (_1, _1)

type NatRem2[X <: NatT, Y <: Succ[NatT]] = NatDiv[X, Y] match
  case (_, y) => y

@main def main(): Unit =
  _1: NatRem2[_3, _2] // Error, unexpected.

type _0 = Zero
type _1 = Succ[_0]
type _2 = Succ[_1]
type _3 = Succ[_2]

val _0: _0 = Zero()
val _1: _1 = Succ(_0)

The compiler error:

Found:    (_1 : _1)
Required: NatRem2[_3, _2]

Note: a match type could not be fully reduced:

  trying to reduce  NatRem2[_3, _2]
  failed since selector (_1, _1)
  is uninhabited (there are no values of that type).

@sjrd

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants