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

Inline (not transparent) with match type alters typechecking #13250

Open
Katrix opened this issue Aug 4, 2021 · 4 comments
Open

Inline (not transparent) with match type alters typechecking #13250

Katrix opened this issue Aug 4, 2021 · 4 comments

Comments

@Katrix
Copy link
Contributor

Katrix commented Aug 4, 2021

Compiler version

3.0.3-RC1-bin-20210803-010f8de-NIGHTLY

Minimized code

import scala.deriving.Mirror

class MyGeneric[A, Repr]

type TransformTuple[T <: Tuple] <: Tuple = T match {
  case x *: xs => x *: TransformTuple[xs]
  case EmptyTuple => EmptyTuple
}

//Both of these are equivalent with the exception that one is defined inline
object NotInline {
  given materializeProduct[T <: Product](
    using m: Mirror.ProductOf[T]
  ): MyGeneric[T, TransformTuple[m.MirroredElemTypes]] =
    new MyGeneric[T, TransformTuple[m.MirroredElemTypes]]
}

object WithInline {
  inline given materializeProduct[T <: Product](
    using m: Mirror.ProductOf[T]
  ): MyGeneric[T, TransformTuple[m.MirroredElemTypes]] =
    new MyGeneric[T, TransformTuple[m.MirroredElemTypes]]
}

@main def testGen = {
  val t = (23, "foo", true)
  type T = (Int, String, Boolean)
  val g1 = NotInline.materializeProduct[T]
  val g2 = WithInline.materializeProduct[T]

  //Passing g1 defined in a value which is not inline explicitly compiles
  takesGen(t)(using g1)

  //Inlining g1 fails
  //takesGen(t)(using NotInline.materializeProduct[T])

  //Fails
  val dummy1 = {
    import NotInline.materializeProduct
    //takesGen(t)
  }

  //Works fine
  val dummy2 = {
    import WithInline.materializeProduct
    takesGen(t)
  }
}

def takesGen[A, Repr](a: A)(using MyGeneric[A, Repr]): String = "Foo"

Note, the match type is required. Without it, everything works normally.

Output

error] -- [E007] Type Mismatch Error: D:\DevProjects\Stable\shapeless\core\src\main\scala-3\Testing.scala:35:20
[error] 35 |  takesGen(t)(using NotInline.materializeProduct[T])
[error]    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[error]    |          Found:    MyGeneric[T, Int *: String *: Boolean *: EmptyTuple]
[error]    |          Required: MyGeneric[(Int, String, Boolean), TransformTuple[?]]
[error]    |
[error]    |          Note: a match type could not be fully reduced:
[error]    |
[error]    |            trying to reduce  TransformTuple[?]
[error]    |            failed since selector  Any
[error]    |            does not match  case x *: xs => x *: TransformTuple[xs]
[error]    |            and cannot be shown to be disjoint from it either.
[error]    |            Therefore, reduction cannot advance to the remaining case
[error]    |
[error]    |              case EmptyTuple => EmptyTuple
[error] -- Error: D:\DevProjects\Stable\shapeless\core\src\main\scala-3\Testing.scala:40:15
[error] 40 |    takesGen(t)
[error]    |               ^
[error]    |no implicit argument of type MyGeneric[(Int, String, Boolean), Repr] was found for parameter x$2 of method takesGen
[error]    |
[error]    |where:    Repr is a type variable
[error]    |.
[error]    |I found:
[error]    |
[error]    |    NotInline.materializeProduct[(Int, String, Boolean)](
[error]    |      {
[error]    |        final class $anon() extends Object() {
[error]    |          type MirroredMonoType = (Int, String, Boolean)
[error]    |        }
[error]    |        (new $anon():Object)
[error]    |      }.$asInstanceOf[
[error]    |
[error]    |          (
[error]    |            deriving.Mirror.Product{
[error]    |              MirroredType = (Int, String, Boolean);
[error]    |                MirroredMonoType = (Int, String, Boolean)
[error]    |              ; MirroredElemTypes <: Tuple
[error]    |            }
[error]    |           &
[error]    |            scala.deriving.Mirror.Product{
[error]    |              MirroredMonoType = (Int, String, Boolean);
[error]    |                MirroredType = (Int, String, Boolean)
[error]    |              ; MirroredLabel = ("Tuple3" : String)
[error]    |            }
[error]    |          ){
[error]    |            MirroredElemTypes = (Int, String, Boolean);
[error]    |              MirroredElemLabels = (("_1" : String), ("_2" : String),
[error]    |                ("_3" : String)
[error]    |              )
[error]    |          }
[error]    |
[error]    |      ]
[error]    |    )
[error]    |
[error]    |But given instance materializeProduct in object NotInline does not match type MyGeneric[(Int, String, Boolean), Repr].
[error]    |
[error]    |One of the following imports might make progress towards fixing the problem:
[error]    |
[error]    |  import shapeless.~?>.idKeyWitness
[error]    |  import shapeless.~?>.idValueWitness
[error]    |  import shapeless.~?>.witness
[error]    |
[error] two errors found

Expectation

Inline without transparent should not change typechecking. In this case I'd expect both to compile, but only the inline one does.

@nicolasstucki
Copy link
Contributor

Minimized

import scala.deriving.Mirror

class MyGeneric[Repr]

type TransformTuple[T <: Tuple] <: Tuple = T match {
  case x *: xs => x *: TransformTuple[xs]
  case EmptyTuple => EmptyTuple
}

def myGeneric[T <: Product](using m: Mirror.ProductOf[T]): MyGeneric[TransformTuple[m.MirroredElemTypes]] = ???

inline def myGenericInline[T <: Product](using m: Mirror.ProductOf[T]): MyGeneric[TransformTuple[m.MirroredElemTypes]] = ???


def testGen = {
  takesGen(myGeneric[(Int, String)])
//         ^^^^^^^^^^^^^^^^^^^^^^^^
//         Found:    MyGeneric[Int *: String *: EmptyTuple]
//         Required: MyGeneric[TransformTuple[?]]
//
//         Note: a match type could not be fully reduced:
//
//           trying to reduce  TransformTuple[?]
//           failed since selector  Any
//           does not match  case x *: xs => x *: TransformTuple[xs]
//           and cannot be shown to be disjoint from it either.
//           Therefore, reduction cannot advance to the remaining case
//
//             case EmptyTuple => EmptyTuple
  takesGen(myGenericInline[(Int, String)])
}

def takesGen[Repr](mg: MyGeneric[Repr]): String = "Foo"

@nicolasstucki
Copy link
Contributor

This fails in typer before we inline the code. There must be some part of the logic of match types that does something different when seeing an inline definition. Both definitions should be typed the same way.

@Katrix
Copy link
Contributor Author

Katrix commented Aug 20, 2021

Was told to mention that the Shapeless 2 port to Scala 3 needs this fixed. Currently most givens are defined as inline just incase, but would be nice to drop that

milessabin/shapeless#1200

@dwijnand
Copy link
Member

dwijnand commented Mar 4, 2022

Now it says:

def testGen =
  takesGen(myGeneric[(Int, String)])
//         ^^^^^^^^^^^^^^^^^^^^^^^^
//         Found:    MyGeneric[Int *: String *: EmptyTuple]
//         Required: MyGeneric[TransformTuple[Nothing]]
//
//         Note: a match type could not be fully reduced:
//
//           trying to reduce  TransformTuple[Nothing]
//           failed since selector  Nothing
//           is uninhabited (there are no values of that type).
  takesGen(myGenericInline[(Int, String)])

.. which looks worst?

Both definitions should be typed the same way.

I assume we agree it should compile? I would hope so, but inline is the newer code branch, so I'd assume the non-inline path typer takes to less buggy...

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

4 participants