Skip to content

Regressions in fingo/spata for match types #21013

Open
@WojciechMazur

Description

@WojciechMazur

The last good compiler version 3.4.2-RC1-bin-20240229-9fe0111-NIGHTLY
There are 2 issues related to the same reproducer

Reproducer

import scala.deriving.Mirror
import scala.annotation.unused
import scala.compiletime.{constValue, erasedValue, *}

type Key = String & Singleton

trait StringParser[+A]

private inline def getLabels[T <: Tuple]: List[String] = inline erasedValue[T] match
  case _: EmptyTuple => Nil
  case _: (t *: ts) => constValue[t].toString :: getLabels[ts]

private inline def getTypes[T <: Tuple]: List[StringParser[?]] = inline erasedValue[T] match
  case _: EmptyTuple => Nil
  case _: (t *: ts) => summonInline[StringParser[t]] :: getTypes[ts]

import TypedRecord.*
final class TypedRecord[KS <: Tuple, VS <: Tuple](
  keys: KS,
  values: VS,
)(using @unused ev1: Tuple.Size[KS] =:= Tuple.Size[VS], @unused ev2: Tuple.Union[KS] <:< Key):

  inline def to[P <: Product](using
    m: Mirror.ProductOf[P],
    ev: Tuple.Union[Tuple.Zip[m.MirroredElemLabels, m.MirroredElemTypes]] <:< Tuple.Union[Tuple.Zip[KS, VS]]
  ): P =
    val labels = getLabels[m.MirroredElemLabels]
    val vals = labels.map(l => get(l, keys, values)) // error: issue 2
    m.fromProduct(Tuple.fromArray(vals.toArray)) // error: issue 1

  private def get[K <: Key, KS <: Tuple, VS <: Tuple](key: K, keys: KS, values: VS): Select[K, KS, VS] =
    val selected = (keys: @unchecked) match
      case `key` *: _ => getH(values)
      case _ *: tk => getT(key, tk, values)
    selected.asInstanceOf[Select[K, KS, VS]]

  private def getT[K <: Key, KS <: Tuple, VS <: Tuple](key: K, keys: KS, values: VS): SelectT[K, KS, VS] =
    (values: @unchecked) match
      case vs: (h *: t) => get(key, keys, vs.tail[h *: t])

  private def getH[VS <: Tuple](values: VS): SelectH[VS] =
    (values: @unchecked) match
      case vs: *:[h, t] => vs.head[h *: t]

object TypedRecord:
  type Select[K <: Key, KS <: Tuple, VS <: Tuple] = KS match
    case K *: ? => SelectH[VS]
    case h *: t => SelectT[K, t, VS]

  type SelectT[K <: Key, KS <: Tuple, VS <: Tuple] = VS match
    case ? *: tv => Select[K, KS, tv]

  type SelectH[VS <: Tuple] = VS match
    case h *: ? => h

Outputs

Issue 1

[error] -- [E172] Type Error: /Users/wmazur/projects/community-build3/repo/src/main/scala/info/fingo/spata/schema/TypedRecord.scala:93:46 
[error] 93 |    m.fromProduct(Tuple.fromArray(vals.toArray))
[error]    |                                              ^
[error]    |No ClassTag available for T
[error]    |
[error]    |where:    T is a type variable with constraint >: info.fingo.spata.schema.TypedRecord.SelectH[VS] | info.fingo.spata.schema.TypedRecord.SelectT[String, t, VS]

Introduced in #19761 and described in #20972 (comment)
Before that change, the toArray was inferred to be toArray[Any] which even though it allowed for compilation it was probably incorrect, but I'd like to request confirmation.

Issue 2

After using explicit `toArray[Any] to mitigate issue 1 in some later versions of compiler
Last good release: 3.4.2-RC1-bin-20240302-c7a0459-NIGHTLY
First bad release: 3.4.2-RC1-bin-20240305-beba585-NIGHTLY
No exact bisect result due to issues with the compiler builds

-- [E057] Type Mismatch Error: /Users/wmazur/projects/dotty/bisect/test.scala:28:12 
28 |    val vals = labels.map(l => get(l, keys, values))
   |            ^
   |Type argument String does not conform to upper bound Key in subpart TypedRecord.SelectT[String, t, VS] of inferred type List[TypedRecord.SelectH[VS] | TypedRecord.SelectT[String, t, VS]]

or with later versions

-- [E007] Type Mismatch Error: /Users/wmazur/projects/dotty/bisect/test.scala:28:34 
28 |    val vals = labels.map(l => get(l, keys, values))
   |                               ^^^^^^^^^^^^^^^^^^^^
   |     Found:    TypedRecord.Select[(l : String), KS, VS]
   |     Required: KS match {
   |       case ? <: String *: _ => TypedRecord.SelectH[VS]
   |       case h *: t => TypedRecord.SelectT[String, t, VS]
   |     } <: TypedRecord.SelectH[VS] | TypedRecord.SelectT[String, t², VS]
   |
   |     where:    KS is a type in class TypedRecord with bounds <: Tuple
   |               VS is a type in class TypedRecord with bounds <: Tuple
   |               t  is a type variable with constraint <: Tuple
   |               t² is a type in type Select with bounds <: Tuple
   |
   |
   |     Note: a match type could not be fully reduced:
   |
   |       trying to reduce  TypedRecord.Select[(l : String), KS, VS]
   |       failed since selector KS
   |       does not match  case (l : String) *: _ => TypedRecord.SelectH[VS]
   |       and cannot be shown to be disjoint from it either.
   |       Therefore, reduction cannot advance to the remaining case
   |
   |         case h *: t => TypedRecord.SelectT[(l : String), t, VS]
   |

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions