Skip to content

Opaque type's type parameter widened causing implicit lookup failure #12950

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

Closed
erikerlandson opened this issue Jun 26, 2021 · 6 comments · Fixed by #13206
Closed

Opaque type's type parameter widened causing implicit lookup failure #12950

erikerlandson opened this issue Jun 26, 2021 · 6 comments · Fixed by #13206

Comments

@erikerlandson
Copy link

erikerlandson commented Jun 26, 2021

Compiler version

3.0.0

Minimized code

package repro
object repro:
    object opq:
        opaque type Lift[T] = Int
        extension(v: Int)
            def lift[T]: Lift[T] = v
        extension[T](l: Lift[T])
            def value: Int = l

    export opq.Lift as Lift
    export opq.lift as lift

    final type Two

    extension[TL](l: Lift[TL])
        def repro[TR](using m: Mul[TL, TR]): Int = l.value + m.value

    abstract class Mul[TL, TR]:
        val value: Int

    transparent inline given mulGivenInt[TL <: Int & Singleton, TR <: Int & Singleton]: Mul[TL, TR] =
        val m: Int = scala.compiletime.constValue[TL] * scala.compiletime.constValue[TR]
        new Mul[TL, TR] { val value: Int = m }

    transparent inline given mulGivenTwo[TR <: Int & Singleton]: Mul[Two, TR] =
        val m: Int = 2 * scala.compiletime.constValue[TR]
        new Mul[Two, TR] { val value: Int = m }

Output

scala> import repro.repro.{*, given}

// this works, using a type 'Two' that is not an integer literal
scala> val x = 1.lift[Two]
val x: repro.repro.opq.Lift[repro.repro.Two] = 1

scala> x.repro[2]
val res0: Int = 5

// this will fail for some reason
scala> val y = 1.lift[2]                                                                                                                                                             
val y: repro.repro.opq.Lift[2] = 1
scala> y.repro[2]
1 |y.repro[2]
  |          ^
  |no implicit argument of type repro.repro.Mul[Int, (2 : Int)] was found for parameter m of method repro in object repro.
  |I found:
  |
  |    repro.repro.mulGivenInt[(Int & Singleton), (Int & Singleton)]
  |
  |But given instance mulGivenInt in object repro does not match type repro.repro.Mul[Int, (2 : Int)].

Expectation

In the second example val y = 1.lift[2] I would expect that the integer literal type parameter not be widened to Int in the call to y.repro[2] - it should succeed and return 5, the same as with x.repro[2]

Instead the TL type parameter is being widened to Int: repro.repro.Mul[Int, (2 : Int)]

It seems to be specifically related to using opaque type - a more simple repro I attempted worked correctly, when the opaque type was not involved.

@erikerlandson
Copy link
Author

erikerlandson commented Jun 27, 2021

For comparison, here is an example that is very similar to the above, just without using opaque type. It works as expected:

object repro2:
    abstract class Lift[T]:
        val value: Int

    extension(v: Int)
        def lift[T]: Lift[T] =
            new Lift[T]:
                val value = v

    final type Two

    extension[TL](l: Lift[TL])
        def repro[TR](using m: Mul[TL, TR]): Int = l.value + m.value

    abstract class Mul[TL, TR]:
        val value: Int

    transparent inline given mulGivenInt[TL <: Int & Singleton, TR <: Int & Singleton]: Mul[TL, TR] =
        val m: Int = scala.compiletime.constValue[TL] * scala.compiletime.constValue[TR]
        new Mul[TL, TR] { val value: Int = m }

    transparent inline given mulGivenTwo[TR <: Int & Singleton]: Mul[Two, TR] =
        val m: Int = 2 * scala.compiletime.constValue[TR]
        new Mul[Two, TR] { val value: Int = m }

In this example the integer literal is not widened and both invocations work:

scala> import repro.repro2.{*, given}

scala> val x = 1.lift[Two]
val x: repro.repro2.Lift[repro.repro2.Two] = repro.repro2$$anon$1@386e9fd8

scala> x.repro[2]                                                                                                                                                                                                          
val res0: Int = 5

scala> val y = 1.lift[2]                                                                                                                                                                 
val y: repro.repro2.Lift[2] = repro.repro2$$anon$1@5b5b8730

scala> y.repro[2]                                                                                                                                                                           
val res1: Int = 5

@anatoliykmetyuk anatoliykmetyuk added the stat:needs minimization Needs a self contained minimization label Jun 28, 2021
@erikerlandson
Copy link
Author

@anatoliykmetyuk I simplified it somewhat by removing the metaprogramming. I do not believe it can be further simplified and still show what I am trying to show.

@dwijnand dwijnand changed the title integer literal type is unexpectedly widened to Int Opaque type's type parameter widened causing implicit lookup failure Jun 30, 2021
@dwijnand dwijnand removed the stat:needs minimization Needs a self contained minimization label Jun 30, 2021
@erikerlandson
Copy link
Author

for reference, the actual use case where this comes up is here:
https://github.com/erikerlandson/poc-coulomb-scala3/blob/main/coulomb/src/coulomb/quantity.scala#L19

The type parameter U is a sort of "free" parameter that adds unit information to a raw value type V. In the simplified repro above I effectively hardcoded V to Int

@odersky
Copy link
Contributor

odersky commented Jun 30, 2021

I won't be able to work on the inliner for the time being. @nicolasstucki is already overloaded. We need some relief from others to step up here.

@anatoliykmetyuk
Copy link
Contributor

Minimised to:

Lib.scala

opaque type F[T] = Int

Main.scala

type G[T]
given gt[T <: Int & Singleton]: G[T] = ???
def h[T](l: F[T])(using m: G[T]) = ???
val y = h(??? : F[2])

Compile separately.

Output:

-- Error: /Users/kmetiuk/Projects/scala3/playground/i12950/Bad/Main.scala:4:21 -                                                                      
4 |val y = h(??? : F[2])
  |                     ^
  |no implicit argument of type G[Int] was found for parameter m of method h.
  |I found:
  |
  |    gt[T]
  |
  |But given instance gt does not match type G[Int].

If compiled together, the error goes away.

@anatoliykmetyuk anatoliykmetyuk removed their assignment Jul 13, 2021
@soronpo
Copy link
Contributor

soronpo commented Jul 22, 2021

Possibly related to #12945

odersky added a commit to dotty-staging/dotty that referenced this issue Jul 30, 2021
odersky added a commit to dotty-staging/dotty that referenced this issue Jul 30, 2021
tanishiking pushed a commit to tanishiking/scala3 that referenced this issue Aug 10, 2021
smarter pushed a commit to dotty-staging/dotty that referenced this issue Aug 11, 2021
@Kordyjan Kordyjan added this to the 3.1.0 milestone Aug 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants