scala/scala3

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

Opened this issue · 5 comments

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.

Not sure if I'm missing something about the reproduction, but trying to compile it with 3.4.2 / 3.5.0-RC1 / nightly gives me the following crash:

 An unhandled exception was thrown in the compiler.
  Please file a crash report here:
  https://github.com/scala/scala3/issues/new/choose
  For non-enriched exceptions, compile with -Xno-enrich-error-messages.

     while compiling: /Users/pchabelski/IdeaProjects/scala-cli-tests/compiler-repro/repro.scala
        during phase: typer
                mode: Mode(ImplicitsEnabled)
     library version: version 2.13.12
    compiler version: version 3.5.1-RC1-bin-20240602-c6fbe6f-NIGHTLY-git-c6fbe6f
            settings: -classpath /Users/pchabelski/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-library_3/3.5.1-RC1-bin-20240602-c6fbe6f-NIGHTLY/scala3-library_3-3.5.1-RC1-bin-20240602-c6fbe6f-NIGHTLY.jar:/Users/pchabelski/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.12/scala-library-2.13.12.jar -d /Users/pchabelski/IdeaProjects/scala-cli-tests/compiler-repro/.scala-build/compiler-repro_4d37e6c5c0-fb1e690db3/classes/main -java-output-version 17 -sourceroot /Users/pchabelski/IdeaProjects/scala-cli-tests/compiler-repro


  Exception while compiling /Users/pchabelski/IdeaProjects/scala-cli-tests/compiler-repro/repro.scala, /Users/pchabelski/IdeaProjects/scala-cli-tests/compiler-repro/Macro.scala

  An unhandled exception was thrown in the compiler.
  Please file a crash report here:
  https://github.com/scala/scala3/issues/new/choose
  For non-enriched exceptions, compile with -Xno-enrich-error-messages.

     while compiling: <no file>
        during phase: parser
                mode: Mode()
     library version: version 2.13.12
    compiler version: version 3.5.1-RC1-bin-20240602-c6fbe6f-NIGHTLY-git-c6fbe6f
            settings: -classpath /Users/pchabelski/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-library_3/3.5.1-RC1-bin-20240602-c6fbe6f-NIGHTLY/scala3-library_3-3.5.1-RC1-bin-20240602-c6fbe6f-NIGHTLY.jar:/Users/pchabelski/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.12/scala-library-2.13.12.jar -d /Users/pchabelski/IdeaProjects/scala-cli-tests/compiler-repro/.scala-build/compiler-repro_4d37e6c5c0-fb1e690db3/classes/main -java-output-version 17 -sourceroot /Users/pchabelski/IdeaProjects/scala-cli-tests/compiler-repro

Exception in thread "main" dotty.tools.dotc.core.Denotations$StaleSymbolException: stale symbol; module class Wrapper1$#4131 in module class <empty>, defined in Period(2.1-8), is referred to in run Period(3.1)
        at dotty.tools.dotc.core.Denotations$SingleDenotation.staleSymbolError(Denotations.scala:961)
        at dotty.tools.dotc.core.Denotations$SingleDenotation.bringForward(Denotations.scala:759)
        at dotty.tools.dotc.core.Denotations$SingleDenotation.toNewRun$1(Denotations.scala:806)
        at dotty.tools.dotc.core.Denotations$SingleDenotation.current(Denotations.scala:877)
        at dotty.tools.dotc.core.Symbols$Symbol.recomputeDenot(Symbols.scala:124)
        at dotty.tools.dotc.core.Symbols$Symbol.computeDenot(Symbols.scala:118)
        at dotty.tools.dotc.core.Symbols$Symbol.denot(Symbols.scala:109)
        at dotty.tools.dotc.core.SymDenotations$ModuleCompleter.complete(SymDenotations.scala:2817)
        at dotty.tools.dotc.core.SymDenotations$SymDenotation.completeFrom(SymDenotations.scala:175)
        at dotty.tools.dotc.core.Denotations$Denotation.completeInfo$1(Denotations.scala:190)
        at dotty.tools.dotc.core.Denotations$Denotation.info(Denotations.scala:192)
        at dotty.tools.dotc.core.SymDenotations$SymDenotation.ensureCompleted(SymDenotations.scala:393)
        at dotty.tools.dotc.core.SymDenotations$SymDenotation.flags(SymDenotations.scala:66)
        at dotty.tools.dotc.core.SymDenotations$SymDenotation.is(SymDenotations.scala:112)
        at dotty.tools.dotc.typer.Typer.isSelfDenot$1(Typer.scala:396)
        at dotty.tools.dotc.typer.Typer.loop$1(Typer.scala:489)
        at dotty.tools.dotc.typer.Typer.findRefRecur$1(Typer.scala:553)
        at dotty.tools.dotc.typer.Typer.findRef(Typer.scala:556)
        at dotty.tools.dotc.typer.Typer.typedIdent(Typer.scala:617)
        at dotty.tools.dotc.typer.Typer.typedNamed$1(Typer.scala:3371)
        at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3481)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3559)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3563)
        at dotty.tools.dotc.typer.Typer.typedExpr(Typer.scala:3674)
        at dotty.tools.dotc.typer.Applications.typedTypeApply(Applications.scala:1284)
        at dotty.tools.dotc.typer.Applications.typedTypeApply$(Applications.scala:434)
        at dotty.tools.dotc.typer.Typer.typedTypeApply(Typer.scala:145)
        at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:3417)
        at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3482)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3559)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3563)
        at dotty.tools.dotc.typer.Typer.typedExpr(Typer.scala:3674)
        at dotty.tools.dotc.typer.Applications.realApply$1(Applications.scala:1040)
        at dotty.tools.dotc.typer.Applications.typedApply(Applications.scala:1231)
        at dotty.tools.dotc.typer.Applications.typedApply$(Applications.scala:434)
        at dotty.tools.dotc.typer.Typer.typedApply(Typer.scala:145)
        at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:3397)
        at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3482)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3559)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3563)
        at dotty.tools.dotc.typer.Typer.typedExpr(Typer.scala:3674)
        at dotty.tools.dotc.typer.Namer.typedAheadExpr$$anonfun$1(Namer.scala:1747)
        at dotty.tools.dotc.typer.Namer.typedAhead(Namer.scala:1737)
        at dotty.tools.dotc.typer.Namer.typedAheadExpr(Namer.scala:1747)
        at dotty.tools.dotc.typer.Namer.typedAheadRhs$1$$anonfun$1(Namer.scala:2073)
        at dotty.tools.dotc.inlines.PrepareInlineable$.dropInlineIfError(PrepareInlineable.scala:256)
        at dotty.tools.dotc.typer.Namer.typedAheadRhs$1(Namer.scala:2073)
        at dotty.tools.dotc.typer.Namer.rhsType$1(Namer.scala:2081)
        at dotty.tools.dotc.typer.Namer.cookedRhsType$1(Namer.scala:2100)
        at dotty.tools.dotc.typer.Namer.lhsType$1(Namer.scala:2101)
        at dotty.tools.dotc.typer.Namer.inferredResultType(Namer.scala:2112)
        at dotty.tools.dotc.typer.Namer.inferredType$1(Namer.scala:1779)
        at dotty.tools.dotc.typer.Namer.valOrDefDefSig(Namer.scala:1785)
        at dotty.tools.dotc.typer.Namer$Completer.typeSig(Namer.scala:823)
        at dotty.tools.dotc.typer.Namer$Completer.completeInCreationContext(Namer.scala:974)
        at dotty.tools.dotc.typer.Namer$Completer.complete(Namer.scala:850)
        at dotty.tools.dotc.core.SymDenotations$SymDenotation.completeFrom(SymDenotations.scala:175)
        at dotty.tools.dotc.core.Denotations$Denotation.completeInfo$1(Denotations.scala:190)
        at dotty.tools.dotc.core.Denotations$Denotation.info(Denotations.scala:192)
        at dotty.tools.dotc.core.SymDenotations$SymDenotation.ensureCompleted(SymDenotations.scala:393)
        at dotty.tools.dotc.typer.Typer.retrieveSym(Typer.scala:3344)
        at dotty.tools.dotc.typer.Typer.typedNamed$1(Typer.scala:3369)
        at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3481)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3559)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3563)
        at dotty.tools.dotc.typer.Typer.traverse$1(Typer.scala:3585)
        at dotty.tools.dotc.typer.Typer.typedStats(Typer.scala:3631)
        at dotty.tools.dotc.typer.Typer.typedBlockStats(Typer.scala:1375)
        at dotty.tools.dotc.typer.Typer.typedBlock(Typer.scala:1379)
        at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:3405)
        at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3482)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3559)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3563)
        at dotty.tools.dotc.typer.Typer.typedExpr(Typer.scala:3674)
        at dotty.tools.dotc.typer.Typer.typedMatch(Typer.scala:1972)
        at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:3412)
        at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3482)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3559)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3563)
        at dotty.tools.dotc.typer.Typer.traverse$1(Typer.scala:3612)
        at dotty.tools.dotc.typer.Typer.typedStats(Typer.scala:3631)
        at dotty.tools.dotc.typer.Typer.typedClassDef(Typer.scala:3079)
        at dotty.tools.dotc.typer.Typer.typedTypeOrClassDef$1(Typer.scala:3385)
        at dotty.tools.dotc.typer.Typer.typedNamed$1(Typer.scala:3389)
        at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3481)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3559)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3563)
        at dotty.tools.dotc.typer.Typer.traverse$1(Typer.scala:3585)
        at dotty.tools.dotc.typer.Typer.typedStats(Typer.scala:3631)
        at dotty.tools.dotc.typer.Typer.typedPackageDef(Typer.scala:3212)
        at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:3431)
        at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3482)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3559)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3563)
        at dotty.tools.dotc.typer.Typer.typedExpr(Typer.scala:3674)
        at dotty.tools.dotc.typer.TyperPhase.typeCheck$$anonfun$1(TyperPhase.scala:47)
        at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
        at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
        at dotty.tools.dotc.core.Phases$Phase.monitor(Phases.scala:503)
        at dotty.tools.dotc.typer.TyperPhase.typeCheck(TyperPhase.scala:53)
        at dotty.tools.dotc.typer.TyperPhase.$anonfun$4(TyperPhase.scala:99)
        at scala.collection.Iterator$$anon$6.hasNext(Iterator.scala:479)
        at scala.collection.Iterator$$anon$9.hasNext(Iterator.scala:583)
        at scala.collection.immutable.List.prependedAll(List.scala:152)
        at scala.collection.immutable.List$.from(List.scala:684)
        at scala.collection.immutable.List$.from(List.scala:681)
        at scala.collection.IterableOps$WithFilter.map(Iterable.scala:898)
        at dotty.tools.dotc.typer.TyperPhase.runOn(TyperPhase.scala:98)
        at dotty.tools.dotc.Run.runPhases$1$$anonfun$1(Run.scala:343)
        at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
        at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
        at scala.collection.ArrayOps$.foreach$extension(ArrayOps.scala:1323)
        at dotty.tools.dotc.Run.runPhases$1(Run.scala:336)
        at dotty.tools.dotc.Run.compileUnits$$anonfun$1(Run.scala:384)
        at dotty.tools.dotc.Run.compileUnits$$anonfun$adapted$1(Run.scala:396)
        at dotty.tools.dotc.util.Stats$.maybeMonitored(Stats.scala:69)
        at dotty.tools.dotc.Run.compileUnits(Run.scala:396)
        at dotty.tools.dotc.Run.compileUnits(Run.scala:288)
        at dotty.tools.dotc.Run.compileSuspendedUnits(Run.scala:410)
        at dotty.tools.dotc.Driver.finish(Driver.scala:63)
        at dotty.tools.dotc.Driver.doCompile(Driver.scala:38)
        at dotty.tools.dotc.Driver.process(Driver.scala:201)
        at dotty.tools.dotc.Driver.process(Driver.scala:169)
        at dotty.tools.dotc.Driver.process(Driver.scala:181)
        at dotty.tools.dotc.Driver.main(Driver.scala:211)
        at dotty.tools.dotc.Main.main(Main.scala)

@andrzejressel did you pass any compiler options when reproducing this?
Either way, tagging this as a crash issue for now.

Looks like the crash happens only if both files are compiled in the same compilation run

Also it seems that the overzealous dealiasing issue happens only when compiling in two compilation runs. Interesting...

Also found this:

import scala.quoted.*
transparent inline def getTypeInfo[T]() = ${ getTypeInfoImpl[T] }
def getTypeInfoImpl[T: Type](using ctx: Quotes): Expr[Unit] = '{ () }
class Wrapper1[A]
val _ = {
  getTypeInfo[Any]()
  val wrapper2 = Wrapper1[Any]()
}

producing:

-- [E006] Not Found Error: M2.scala:5:17 ---------------------------------------
5 |  val wrapper2 = Wrapper1[Any]()
  |                 ^^^^^^^^
  |                 Not found: Wrapper1 - did you mean wrapper2?
  |
  | longer explanation available when compiling with `-explain`

It seems we lose some part of context when suspending to expand a macro