scala / scala3

The Scala 3 compiler, also known as Dotty.
https://dotty.epfl.ch
Apache License 2.0
5.81k stars 1.05k forks source link

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

Open andrzejressel opened 4 months ago

andrzejressel commented 4 months ago

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.

Gedochao commented 3 months ago

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.

andrzejressel commented 3 months ago

@Gedochao No, everything is default: https://github.com/andrzejressel/scala3-20449/actions/runs/9397702223/job/25881452117

jchyb commented 3 months ago

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

jchyb commented 3 months ago

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

jchyb commented 3 months ago

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