Skip to content

Passing opaque type through two wrappers compiled in another module causes the type to lose its identity and opaqueness #20449

Closed
@andrzejressel

Description

@andrzejressel

Compiler version

3.3.3
3.4.2
3.5.0-RC1

Minimized code

import scala.quoted.*

class ForeignWrapper1[-A] {
  inline def getTypeInfo(inline source: String): Unit =
    ${ getTypeInfoImpl[A]('source) }
  def createWrapper2 = ForeignWrapper2(this)
}

class ForeignWrapper2[-A](val self: ForeignWrapper1[A]) {
  inline def getTypeInfo(inline source: String): Unit =
    ${getTypeInfoImpl[A]('source)}
}

transparent inline def getTypeInfo[T](inline source: String) =
  ${ getTypeInfoImpl[T]('source) }

def getTypeInfoImpl[T: Type](source: Expr[String])(using ctx: Quotes) : Expr[Unit] = {
  import ctx.reflect.*

  println("------" + source.valueOrAbort + "---------")
  val tpe = TypeRepr.of[T]
  println(s"Original: ${tpe.show}")
  println(s"Dealias: ${tpe.dealias.show}")
  println(s"Dealias dealias: ${tpe.dealias.dealias.show}")

  '{ () }
}
object UserName {
  opaque type T = String

  def apply(s: String): T = s
}

type UserName = UserName.T

class Wrapper1[-A] {
  inline def getTypeInfo(inline source: String): Unit =
    ${ getTypeInfoImpl[A]('source) }
  def createWrapper2 = Wrapper2(this)
}

class Wrapper2[-A](val self: Wrapper1[A]) {
  inline def getTypeInfo(inline source: String): Unit =
    ${getTypeInfoImpl[A]('source)}
}


val _ = {
  getTypeInfo[UserName.T]("UserName.T - Directly")
  getTypeInfo[UserName]("UserName.T - Directly")

  val foreignWrapper = ForeignWrapper1[UserName.T]()
  foreignWrapper.getTypeInfo("ForeignWrapper1[UserName.T]")
  foreignWrapper.createWrapper2.getTypeInfo("ForeignWrapper2[UserName.T]")

  val foreignWrapper2 = ForeignWrapper1[UserName]()
  foreignWrapper2.getTypeInfo("ForeignWrapper1[UserName]")
  foreignWrapper2.createWrapper2.getTypeInfo("ForeignWrapper2[UserName]")

  val wrapper = Wrapper1[UserName.T]()
  wrapper.getTypeInfo("Wrapper1[UserName.T]")
  wrapper.createWrapper2.getTypeInfo("Wrapper2[UserName.T]")

  val wrapper2 = Wrapper1[UserName]()
  wrapper2.getTypeInfo("Wrapper1[UserName]")
  wrapper2.createWrapper2.getTypeInfo("Wrapper2[UserName]")
  
}

Output

------UserName.T - Directly---------
Original: test$package.UserName.T
Dealias: test$package.UserName.T
Dealias dealias: test$package.UserName.T
------UserName.T - Directly---------
Original: test$package.UserName
Dealias: test$package.UserName.T
Dealias dealias: test$package.UserName.T
------ForeignWrapper1[UserName.T]---------
Original: test$package.UserName.T
Dealias: test$package.UserName.T
Dealias dealias: test$package.UserName.T
------ForeignWrapper2[UserName.T]---------
Original: $proxy1.T
Dealias: java.lang.String
Dealias dealias: java.lang.String
------ForeignWrapper1[UserName]---------
Original: test$package.UserName
Dealias: test$package.UserName.T
Dealias dealias: test$package.UserName.T
------ForeignWrapper2[UserName]---------
Original: test$package.UserName
Dealias: test$package.UserName.T
Dealias dealias: test$package.UserName.T
------Wrapper1[UserName.T]---------
Original: test$package.UserName.T
Dealias: test$package.UserName.T
Dealias dealias: test$package.UserName.T
------Wrapper2[UserName.T]---------
Original: test$package.UserName.T
Dealias: test$package.UserName.T
Dealias dealias: test$package.UserName.T
------Wrapper1[UserName]---------
Original: test$package.UserName
Dealias: test$package.UserName.T
Dealias dealias: test$package.UserName.T
------Wrapper2[UserName]---------
Original: test$package.UserName
Dealias: test$package.UserName.T
Dealias dealias: test$package.UserName.T

Expectation

------ForeignWrapper2[UserName.T]---------
Original: $proxy1.T
Dealias: java.lang.String
Dealias dealias: java.lang.String

Expectation is that opaque type would not become $proxy1.T that can be dealiased to underlying type.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions