Skip to content

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

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
andrzejressel opened this issue May 21, 2024 · 6 comments · Fixed by #22655

Comments

@andrzejressel
Copy link
Contributor

andrzejressel commented May 21, 2024

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.

@andrzejressel andrzejressel added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels May 21, 2024
@andrzejressel andrzejressel changed the title Passing opaque type through two wrappers compiled in another scope causes opaque type to lose it's identity and opaqueness Passing opaque type through two wrappers compiled in another module causes opaque type to lose it's identity and opaqueness May 21, 2024
@andrzejressel andrzejressel changed the title Passing opaque type through two wrappers compiled in another module causes opaque type to lose it's identity and opaqueness Passing opaque type through two wrappers compiled in another module causes opaque type to lose its identity and opaqueness May 21, 2024
@andrzejressel andrzejressel changed the title Passing opaque type through two wrappers compiled in another module causes opaque type to lose its identity and opaqueness Passing opaque type through two wrappers compiled in another module causes the type to lose its identity and opaqueness May 21, 2024
@Gedochao
Copy link
Contributor

Gedochao commented Jun 4, 2024

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.

@Gedochao Gedochao added area:typer itype:crash area:inline area:opaque-types area:metaprogramming:quotes Issues related to quotes and splices and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels Jun 4, 2024
@andrzejressel
Copy link
Contributor Author

@jchyb
Copy link
Contributor

jchyb commented Jun 17, 2024

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

@jchyb
Copy link
Contributor

jchyb commented Jun 17, 2024

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

@jchyb
Copy link
Contributor

jchyb commented Jun 18, 2024

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 Edit: it's also related to a stale symbol.

dwijnand added a commit that referenced this issue Mar 4, 2025
…nd a related stale symbol issue) (#22655)

This PR fixes the 2 issues found in
#20449, split into 2 commits.

The first commit fixes the stale symbol related issue found if the files
from the issue minimization are compiled together. After suspending and
retrying compilation, the classDefs that are defined directly in
packages previously would sometimes not have companion objects
regenerated, instead relying on the stale symbols from the previous run,
causing them not to to pass the reallyExists check when looking for a
specific ref. Now we make sure to go through lastKnwonDenotation, since
the current one may not exists and may not point us to a Module flag
when checking if to regenerate it.

The second commit fixes the opaque type alias rhs leaking in a macro.
That was caused by building proxies for all parts of the type, including
type arguments to opaque types - from the perspective of a type like
Object[OpaqueType], the opaque type rhs should not be visible.
@jchyb
Copy link
Contributor

jchyb commented Mar 4, 2025

Fixed with #22655 (I forgot to properly link it before)

@jchyb jchyb closed this as completed Mar 4, 2025
@WojciechMazur WojciechMazur added this to the 3.7.0 milestone Mar 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment