Skip to content

Unresolved symbols when pickling quotes: by-name parameters, underlyingArgument and unintuitive error message #7030

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
anatoliykmetyuk opened this issue Aug 12, 2019 · 1 comment · Fixed by #8796

Comments

@anatoliykmetyuk
Copy link
Contributor

Macros:

import scala.quoted._

inline def inner(exprs: Any): Any = ${innerImpl('exprs)}
def innerImpl(exprs: Expr[Any]) given QuoteContext: Expr[Any] =
  '{$exprs ; ()}

inline def outer(expr: => Any): Any = ${outerImpl('expr)}
def outerImpl(body: Expr[Any]) given (ctx: QuoteContext): Expr[Any] = {
  import ctx.tasty._
  body.unseal.underlyingArgument.seal
}

Test:

val x = outer(inner(???))
Crash
exception occurred while compiling ../pg-dotc/Main.scala
java.lang.AssertionError: assertion failed: unresolved symbols: value exprs(line 0) when pickling ../pg-dotc/Main.scala while compiling ../pg-dotc/Main.scala
Exception in thread "main" java.lang.AssertionError: assertion failed: unresolved symbols: value exprs(line 0) when pickling ../pg-dotc/Main.scala
	at dotty.DottyPredef$.assertFail(DottyPredef.scala:16)
	at dotty.tools.dotc.core.tasty.TreePickler.pickle(TreePickler.scala:695)
	at dotty.tools.dotc.transform.Pickler.run$$anonfun$10$$anonfun$8(Pickler.scala:60)
	at dotty.runtime.function.JProcedure1.apply(JProcedure1.java:15)
	at dotty.runtime.function.JProcedure1.apply(JProcedure1.java:10)
	at scala.collection.immutable.List.foreach(List.scala:392)
	at dotty.tools.dotc.transform.Pickler.run$$anonfun$2(Pickler.scala:83)
	at dotty.runtime.function.JProcedure1.apply(JProcedure1.java:15)
	at dotty.runtime.function.JProcedure1.apply(JProcedure1.java:10)
	at scala.collection.immutable.List.foreach(List.scala:392)
	at dotty.tools.dotc.transform.Pickler.run(Pickler.scala:83)
	at dotty.tools.dotc.core.Phases$Phase.runOn$$anonfun$1(Phases.scala:316)
	at scala.collection.immutable.List.map(List.scala:286)
	at dotty.tools.dotc.core.Phases$Phase.runOn(Phases.scala:318)
	at dotty.tools.dotc.transform.Pickler.runOn(Pickler.scala:87)
	at dotty.tools.dotc.Run.runPhases$4$$anonfun$4(Run.scala:158)
	at dotty.runtime.function.JProcedure1.apply(JProcedure1.java:15)
	at dotty.runtime.function.JProcedure1.apply(JProcedure1.java:10)
	at scala.collection.IndexedSeqOptimized.foreach(IndexedSeqOptimized.scala:36)
	at scala.collection.IndexedSeqOptimized.foreach$(IndexedSeqOptimized.scala:33)
	at scala.collection.mutable.ArrayOps$ofRef.foreach(ArrayOps.scala:198)
	at dotty.tools.dotc.Run.runPhases$5(Run.scala:170)
	at dotty.tools.dotc.Run.compileUnits$$anonfun$1(Run.scala:178)
	at dotty.runtime.function.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:12)
	at dotty.tools.dotc.util.Stats$.maybeMonitored(Stats.scala:65)
	at dotty.tools.dotc.Run.compileUnits(Run.scala:185)
	at dotty.tools.dotc.Run.compileSources(Run.scala:120)
	at dotty.tools.dotc.Run.compile(Run.scala:104)
	at dotty.tools.dotc.Driver.doCompile(Driver.scala:35)
	at dotty.tools.dotc.Driver.process(Driver.scala:180)
	at dotty.tools.dotc.Driver.process(Driver.scala:149)
	at dotty.tools.dotc.Driver.process(Driver.scala:161)
	at dotty.tools.dotc.Driver.main(Driver.scala:188)
	at dotty.tools.dotc.Main.main(Main.scala)
[error] Nonzero exit code returned from runner: 1
[error] (dotty-compiler / Compile / runMain) Nonzero exit code returned from runner: 1
[error] Total time: 8 s, completed Aug 12, 2019 2:07:51 PM

To make it compile, make the parameter to inner by-name:

inline def inner(exprs: => Any): Any = ${innerImpl('exprs)}

In my original use case, it was a mistake for me not to make it by-name. However, due to the obscure crash, I was not able to see that mistake right away.

This is already the second time I encounter unintuitive behaviour of underlyingArgument (don't remember the first time, but it was likely the same thing). I expected it to just return an immediate child node of the callee. However, it seems that it performs a deep traversal and transformation of the callee tree. If you debug outerImpl as follows:

def outerImpl(body: Expr[Any]) given (ctx: QuoteContext): Expr[Any] = {
  import ctx.tasty._
  println(s"Original:\n${body.unseal}")
  println(s"\nUnderlying:\n${body.unseal.underlyingArgument}")
  body.unseal.underlyingArgument.seal
}
Original will be
Inlined(
  EmptyTree,List(

  ),Inlined(
    Apply(
      Ident(
        inner
      ),List(
        Ident(
          ???
        )
      )
    ),List(
      ValDef(
        exprs,TypeTree[TypeRef(
          ThisType(
            TypeRef(
              NoPrefix,module class scala
            )
          ),class Nothing
        )],Ident(
          ???
        )
      )
    ),Typed(
      Inlined(
        Ident(
          Macros$package$
        ),List(

        ),Block(
          List(
            Inlined(
              EmptyTree,List(

              ),Inlined(
                EmptyTree,List(

                ),Ident(
                  exprs
                )
              )
            )
          ),Literal(
            Constant(
              (

              )
            )
          )
        )
      ),Ident(
        Any
      )
    )
  )
)
Underlying will be
Typed(
  Block(
    List(
      Ident(
        exprs
      )
    ),Literal(
      Constant(
        (

        )
      )
    )
  ),Ident(
    Any
  )
)

Notice how the following code is not present in Underlying:

      ValDef(
        exprs,TypeTree[TypeRef(
          ThisType(
            TypeRef(
              NoPrefix,module class scala
            )
          ),class Nothing
        )],Ident(
          ???
        )
      )

Which is the likely cause of the crash.

I believe underlyingArgument should do just that – pill off the "outer layer" of the tree, returning the meaningful child. If the user really wants the current logic, we should make it into a separate method with a name reflecting its deep nature.

/cc @liufengyun since you looked into the issues with unresolved symbols while pickling recently

@nicolasstucki
Copy link
Contributor

We are supposed to also replace exprs with its underlyingArgument. We might be missing that tree when we try to do that. Or maybe not performing the recursive transformation completely.

nicolasstucki added a commit to dotty-staging/dotty that referenced this issue Apr 29, 2020
nicolasstucki added a commit to dotty-staging/dotty that referenced this issue Apr 29, 2020
Fix scala#6140, fix scala#6772, fix scala#7030, fix scala#7892, fix scala#7997, fix scala#8651 and improve scala#8100.

Differences with previous implementation
* Only track and check levels within quotes or splices
* Track levels of all symbols not at level 0
* Split level checking into specialized variants for types and terms (healType/healTermType)
* Detect inconsistent types rather than try to detect consistent ones
* Check/heal term inconsistencies only on leaf nodes (TypeTree, RefTree, Ident, This)
nicolasstucki added a commit to dotty-staging/dotty that referenced this issue May 4, 2020
Fix scala#6140, fix scala#6772, fix scala#7030, fix scala#7892, fix scala#7997, fix scala#8651 and improve scala#8100.

Differences with previous implementation
* Only track and check levels within quotes or splices
* Track levels of all symbols not at level 0
* Split level checking into specialized variants for types and terms (healType/healTermType)
* Detect inconsistent types rather than try to detect consistent ones
* Check/heal term inconsistencies only on leaf nodes (TypeTree, RefTree, Ident, This)
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.

2 participants