scala / scala3

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

Regression in `foldables-io/skunk-tables` for match types #21295

Open WojciechMazur opened 1 month ago

WojciechMazur commented 1 month ago

Compiler version

Last good release: 3.6.0-RC1-bin-20240719-af933c4-NIGHTLY First bad release: 3.6.0-RC1-bin-20240723-46ff151-NIGHTLY Bisect points to b7846c497b4dae18b7a2484fe5ea1acf55a16487

Minimized code

import scala.compiletime.constValueTuple

trait IsColumn[A]

final case class TypedColumn[N <: Singleton, A, T, C <: Tuple](name: N, primitive: IsColumn[A])
object TypedColumn:
  enum Constraint:
    case Default
    case Nullable

  final class Insert[N <: Singleton, A, C <: Tuple, In](name: N, isColumn: IsColumn[A])
  final case class Op[A](a: A)

type IfIn[T <: Tuple, A, True, False] <: True | False = T match
  case EmptyTuple => False
  case A *: t     => True
  case ? *: t     => IfIn[t, A, True, False]

type IfInM[T <: Tuple, A <: Tuple, True, False] <: True | False =
  A match
    case EmptyTuple => False
    case a *: tail  => IfIn[T, a, True, IfInM[T, tail, True, False]]

type NonRequiredConstraints =
  (TypedColumn.Constraint.Nullable.type, TypedColumn.Constraint.Default.type)

type Required[C <: NonEmptyTuple] <: Tuple =
  C match
    case TypedColumn.Insert[n, ?, c, ?] *: t =>
      RequiredGo[t, IfInM[c, NonRequiredConstraints, EmptyTuple, n *: EmptyTuple]]
type RequiredGo[C <: Tuple, A <: Tuple] <: Tuple =
  C match
    case EmptyTuple => A
    case TypedColumn.Insert[n, ?, c, ?] *: t =>
      RequiredGo[t, IfInM[c, NonRequiredConstraints, A, scala.Tuple.Append[A, n]]]

@main def Test = {
    type Columns = (TypedColumn.Insert["one", Boolean, EmptyTuple, Nothing],
                  TypedColumn.Insert["two", Int, EmptyTuple, Nothing],
                  TypedColumn.Insert["three", String, EmptyTuple, Nothing])

    assert(constValueTuple[Required[Columns]] == ("one", "two", "three"))
}

Output

[error] ./src/main/scala/usage.scala:42:12
[error] Values of types Required[Columns] and (String, String, String) cannot be compared with == or !=
[error] 
[error] Note: a match type could not be fully reduced:
[error] 
[error]   trying to reduce  Required[Columns]
[error]   failed since selector Columns
[error]   is uninhabited (there are no values of that type).
[error]     assert(constValueTuple[Required[Columns]] == ("one", "two", "three"))

Expectation

Should compile (probably)

WojciechMazur commented 1 month ago

The same commit pointed out by bisect leads to failures in other projects julianpeeters/dynamical

Reproducer

//> using options -Ykind-projector:underscores 

sealed trait Monomial[A, B, Y]
object Monomial:
  case class Interface[A, B, Y](yᵉˣᵖ: A => Y, coeff: B) extends Monomial[A, B, Y]
  case class Store[S, Y](yᵉˣᵖ: S => Y, coeff: S) extends Monomial[S, S, Y]
  case class StoreF[F[_], S, Y](yᵉˣᵖ: F[S] => Y, coeff: S) extends Monomial[F[S], S, Y]
import Monomial.{Store, Interface, StoreF}

trait Mealy[P[_]]:
  def init[Y]: Init[P, Y]

type ~>[P[_], Q[_]] = PolyMap[P, Q, _]
abstract class PolyMap[P[_], Q[_], Y]

type Init[P[_], Y] = P[Y] match
  case PolyMap[p, q, Y]   => Init[p, Y]
  case Store[s, Y]        => s
  case StoreF[f, s, Y]    => s
  case Interface[a, b, Y] => b
  case Tensor[p, q, Y]    => (Init[p, Y], Init[q, Y]) 

type ⊗[P[_], Q[_]] = Tensor[P, Q, _]
abstract class Tensor[P[_], Q[_], Y]

object tensors:
  extension [S1, S2, A1, B1, A2, B2] (m1: Mealy[Store[S1, _] ~> Interface[A1, A1 => B1, _]])
    def ⊗(m2: Mealy[Store[S2, _] ~> Interface[A2, A2 => B2, _]]): Mealy[(Store[S1, _] ⊗ Store[S2, _]) ~> (Interface[A1, A1 => B1, _] ⊗ Interface[A2, A2 => B2, _])] =
      new Mealy[(Store[S1, _] ⊗ Store[S2, _]) ~> (Interface[A1, A1 => B1, _] ⊗ Interface[A2, A2 => B2, _])]:
        def init[Y]: Init[(Store[S1, _] ⊗ Store[S2, _]) ~> (Interface[A1, A1 => B1, _] ⊗ Interface[A2, A2 => B2, _]), Y] =
          (m1.init, m2.init)

Output

[error] ./src/main/scala/usage.scala:31:12
[error] Found:    Init[
[error]   ([_] =>> Monomial.Store[S1, _]) ~>
[error]     ([_²] =>> Monomial.Interface[A1, A1 => B1, _²]),
[error] Y]
[error] Required: S1
[error] 
[error] where:    Y  is a type variable
[error]           _  is a type variable
[error]           _² is a type variable
[error]           (m1.init, m2.init)
[error]            ^^^^^^^
[error] ./src/main/scala/usage.scala:31:21
[error] Found:    Init[
[error]   ([_] =>> Monomial.Store[S2, _]) ~>
[error]     ([_²] =>> Monomial.Interface[A2, A2 => B2, _²]),
[error] Y]
[error] Required: S2
[error] 
[error] where:    Y  is a type variable
[error]           _  is a type variable
[error]           _² is a type variable
[error]           (m1.init, m2.init)
[error]             
odersky commented 1 month ago

@sjrd Can you take a look to see whether the breakages are inevitable or whether there is some mitigation?

sjrd commented 1 month ago

I'll have a look on Monday.