Skip to content

top level extension method dealiases opaque type in result type #18097

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
artembakhanov opened this issue Jun 29, 2023 · 5 comments · Fixed by #21527
Closed

top level extension method dealiases opaque type in result type #18097

artembakhanov opened this issue Jun 29, 2023 · 5 comments · Fixed by #21527

Comments

@artembakhanov
Copy link

artembakhanov commented Jun 29, 2023

Compiler version

3.3.0

Minimized code

opaque type PositiveNumber = Double

object PositiveNumber:
  extension (x: PositiveNumber)
    def mult1(other: PositiveNumber): PositiveNumber =
      x * other

extension (x: PositiveNumber)
  def mult2(other: PositiveNumber): PositiveNumber =
    x * other

object Test:
  def multMap1[A](x: Map[A, PositiveNumber], num: PositiveNumber): Map[A, PositiveNumber] = x.map((key, value) => key -> value.mult1(num)).toMap

  def multMap2[A](x: Map[A, PositiveNumber], num: PositiveNumber): Map[A, PositiveNumber] = x.map((key, value) => key -> value.mult2(num)).toMap
  def multMap2_2[A](x: Map[A, PositiveNumber], num: PositiveNumber): Map[A, PositiveNumber] = x.map((key, value) => key -> mult2(value)(num)).toMap

Output

[error] ./foo.scala:15:145
[error] Cannot prove that (A, Double) <:< (A, V2).
[error] 
[error] where:    V2 is a type variable with constraint <: PositiveNumber
[error]   def multMap2[A](x: Map[A, PositiveNumber], num: PositiveNumber): Map[A, PositiveNumber] = x.map((key, value) => key -> value.mult2(num)).toMap
[error]

Expectation

I would expect both functions be defined without type error. For some reason, when an opaque type is not a top-level definition, there is no type error when defining these functions.

@artembakhanov artembakhanov added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels Jun 29, 2023
@Kordyjan Kordyjan added area:opaque-types and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels Jun 30, 2023
@Kordyjan Kordyjan added this to the Future versions milestone Jun 30, 2023
@bishabosha
Copy link
Member

bishabosha commented Aug 29, 2023

so for some reason PositiveNumber is being de-aliased to Double when mult2 is used in another scope, and no problem for when mult2 is called directly and not as an extension method

def multMap2[A >: Nothing <: Any](x: Map[A, PositiveNumber],
  num: PositiveNumber): Map[A, PositiveNumber] =
  x.map[A, Double](
    {
      def $anonfun(x$1: (A, PositiveNumber)): (A, Double) =
        {
          val key: A = x$1._1
          val value: PositiveNumber = x$1._2
          ArrowAssoc[A](key).->[Double](mult2(value)(num))
        }
      closure($anonfun)
    }
  ).toMap[A, PositiveNumber](
    /* missing */summon[(A, Double) <:< (A, PositiveNumber)])
def multMap2_2[A >: Nothing <: Any](x: Map[A, PositiveNumber],
  num: PositiveNumber): Map[A, PositiveNumber] =
  x.map[A, PositiveNumber](
    {
      def $anonfun(x$1: (A, PositiveNumber)): (A, PositiveNumber) =
        {
          val key: A = x$1._1
          val value: PositiveNumber = x$1._2
          ArrowAssoc[A](key).->[PositiveNumber](mult2(value)(num))
        }
      closure($anonfun)
    }
  ).toMap[A, PositiveNumber](<:<.refl[(A, PositiveNumber)])

@bishabosha bishabosha changed the title Strange behaviour of extension methods of top-level defined opaque types top level extension method dealiases opaque type in result type Aug 29, 2023
@odersky odersky assigned bishabosha and unassigned odersky Dec 12, 2023
@odersky
Copy link
Contributor

odersky commented Dec 12, 2023

This needs a minimization to be sure, right now there is way too much type inference going on for a good diagnosis.

@ghostbuster91
Copy link
Contributor

ghostbuster91 commented Dec 12, 2023

I think I have a simpler reproducer for the same issue: https://scastie.scala-lang.org/YRmAX35aTyO8mEoIM1kgRw this only happens if the extensions method has the same name as native method on the wrapped type.

Everything works fine if we move it to the Main object: https://scastie.scala-lang.org/R8zww6x4TtKm6roSNyvOyg

@warjort
Copy link

warjort commented Jan 20, 2024

I am also seeing this problem with 3.3.1, 3.3.2-RC2, 3.4.0-RC1

This is my minimization.

Opaque class:

package test

type Foo = Unit
val bar: Foo = ()

//object Foo {
opaque type Opaque = Unit

extension (foo: Foo)
  def go: Option[Opaque] = ???
//}

Test class:

package test

//import Foo.*

final case class Test(value: Opaque)

def test: Test =
  //go(bar) match
  bar.go match
    case Some(value) => Test(value)
    case _           => ???

Incorrect compiler error:

Found:    (value : Unit)
Required: test.Opaque

The above includes 2 different (commented out) workarounds:

  • Put the opaque type and the extension method in a non top level scope
  • Call the extension method as a function instead of using selector syntax

@warjort
Copy link

warjort commented Jan 20, 2024

NOTE: I originally saw this problem in a slightly different context. But I assume it is the same?

My extension method was returning an anonymous tuple inside an Option.

Option[(Opaque, A)]

here a third workaround was to use a case class instead of the tuple

Option[Result[A]]
// with
final case class Result[A](value: Opaque, a: A)

Is this because the opaque type is not explicitly mentioned in the return type?

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.

8 participants