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

Compiler plugin naming issues after typer #11395

Closed
irenedea opened this Issue Feb 9, 2019 · 4 comments

Comments

Projects
None yet
2 participants
@irenedea
Copy link

irenedea commented Feb 9, 2019

I've been working on a compile plugin whose components run after typer and I've run into a handful of different issues that seem to relate to naming. I have a couple examples here which produce different but seemingly related errors:

My plugin component

to which I've added to Transformer.transform in each of the cases

private class Component extends PluginComponent with TypingTransformers with Transform {
    val global: TestPlugin.this.global.type = TestPlugin.this.global
    val runsAfter = List[String]("typer")
    val phaseName = "myPlugin"

    protected def newTransformer(unit: CompilationUnit): Transformer = new Transformer(unit)

    class Transformer(unit: global.CompilationUnit) extends TypingTransformer(unit) {
      override def transform(t: global.Tree): global.Tree = {
        t match {
          // Add more cases here...
          case _ => super.transform(t)
        }
      }
    }
  }

Case 1: Renaming/creating new function and calling it

Expectation: I expect my program to print out hello after this transformation.
Reality: Compilation error
Note: If I print out the tree after the transformation the tree looks as I would expect, with hello replacing goodbye properly.

Source File:

object Test extends App {
    def goodbye(): Unit = { //Transform to def hello()
        println("goodbye")
    }
    goodbye() // Transform to hello()
}

Added match-case in transform: (this fails even if the funcCall case is in a different plugin component that runs after)

case def @ DefDef(_, TermName("hello"), _, _, _, _) => 
    val tree =
      q"""
      def hello(): Unit= {
        println("hello")
      }
    """.asInstanceOf[DefDef]
    val DefDef(modifiers, name, tparams, vparams, tpt, rhs) = tree
    val source = treeCopy.DefDef(tree, modifiers, name, tparams, vparams, tpt, rhs)
    localTyper.namer.enterDefDef(source)
    localTyper typed source
...
// transform goodbye() to hello()
case funcCall @ Apply(Select(This(TypeName("Test")), TermName("goodbye")), List()) =>
    val source = Apply(Select(This(TypeName("Test")), TermName("hello")), List())
    localTyper typed source

Error when compiling:

[error] scala.reflect.internal.Types$TypeError: value goodbye is not a member of object test.Test

Case 2: Overwriting an object /copying an object (post-typer)

Expectation: Should compile fine with nothing effectively changed since I'm replacing object Test with a copy of itself.
Reality: Compilation Error
Note: If I print out the tree after the transformation, the tree looks as I would expect, exactly like the source file should look at this point in the compiler.

Source File:

object Test extends App {
    val x = 0 // works without this line.
}

Added match-case in transform: (this fails even if the funcCall case is in a different plugin component that runs after)

case obj@ModuleDef(_, termName("Test"), _) => 
    val q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body }" = obj
    //Effectively just copy the tree
    val source = q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body }".asInstanceOf[ModuleDef]
    localTyper.namer.enterModuleDef(source)
    localTyper typed source

Error when compiling:

scala.reflect.internal.Types$TypeError: value x in object Test cannot be accessed in object Test
exception when typing Test.this.x/class scala.reflect.internal.Trees$Select
@SethTisue

This comment has been minimized.

Copy link
Member

SethTisue commented Feb 11, 2019

I'm sorry, but we need to keep scala/bug focused on actual bug reports and not on questions

@SethTisue SethTisue closed this Feb 11, 2019

@SethTisue

This comment has been minimized.

Copy link
Member

SethTisue commented Feb 11, 2019

https://contributors.scala-lang.org is a possible next place to try

there is no surefire way to get help with a question like this (aside from Lightbend commercial support, I mean). you can try Gitter and Discourse and it might work or might not

Stack Overflow, somewhat surprisingly, is a goldmine for macro questions and answers — often they are from some years ago but compiler internals haven’t changed much so most of it still applies — you’re writing a compiler plugin and not macros, but often the issues are the same for both

looking for existing macros and plugins that do similar transforms and cargo-culting them is often effective

@irenedea

This comment has been minimized.

Copy link
Author

irenedea commented Feb 13, 2019

Found a solution:

case obj@ModuleDef(_, termName("Test"), _) => 
    val q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body }" = obj
    //Effectively just copy the tree
    val source = q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body }".asInstanceOf[ModuleDef]
    obj.symbol.owner.info.decls.unlink(obj.symbol)
    localTyper.namer.enterModuleDef(source)
    source.symbol.owner.info.decls.enter(source.symbol)
    localTyper typed source

You must unlink the old symbol and enter the new symbol from the owner (in this case it was the package that the module was in).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment