Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Eagerly enter default getters into scope #6286

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

Merged
merged 4 commits into from
May 30, 2018

Conversation

retronym
Copy link
Member

@retronym retronym commented Jan 30, 2018

This stabilizes the order they appear in the owners scope. Previously,
their order was governed by the order that the methods bearing default
parameters were type completed.

References scala/scala-dev#405

@scala-jenkins scala-jenkins added this to the 2.13.0-M4 milestone Jan 30, 2018
@retronym retronym requested a review from lrytz January 30, 2018 01:49
@retronym retronym force-pushed the topic/eager-default-getters branch 6 times, most recently from cf6c44d to ae90434 Compare January 30, 2018 03:55
val default = owner.newMethodSymbol(name, vparam.pos, paramFlagsToDefaultGetter(meth.flags))
default.setPrivateWithin(meth.privateWithin)
default.referenced = meth
default.setInfo(ErrorType)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part is a bit gross, but we're not able to form the RHS of the default getter yet, so we can't assign the type completer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reminds me of our discussion on generalizing unit.synthetics to take tree creators rather than trees

@retronym
Copy link
Member Author

retronym commented Feb 1, 2018

Hmmm

ok 1475 - pos/t9181.scala                         
Build timed out (after 150 minutes). Marking the build as aborted.
Build was aborted
Archiving artifacts

ed7e6b2 / https://scala-ci.typesafe.com/job/scala-2.13.x-validate-test/817/console

The subsequent commit built successfully.

@retronym

This comment has been minimized.

@retronym

This comment has been minimized.

@retronym

This comment has been minimized.

@retronym

This comment has been minimized.

@retronym retronym force-pushed the topic/eager-default-getters branch from ae90434 to f3f7935 Compare February 13, 2018 05:12
@SethTisue

This comment has been minimized.

@SethTisue

This comment has been minimized.

@retronym retronym force-pushed the topic/eager-default-getters branch from f3f7935 to 53bf5f8 Compare February 28, 2018 07:00
@retronym
Copy link
Member Author

Pushed a fix for problem identifed by the failing test: test/files/run/macroPlugins-namerHooks.scala

@retronym retronym force-pushed the topic/eager-default-getters branch from 53bf5f8 to 51d83db Compare March 12, 2018 06:50
@SethTisue

This comment has been minimized.

@adriaanm adriaanm modified the milestones: 2.13.0-M4, 2.13.0-M5 Apr 30, 2018
@retronym retronym force-pushed the topic/eager-default-getters branch from 51d83db to 1fcb418 Compare May 4, 2018 05:43
@retronym
Copy link
Member Author

retronym commented May 4, 2018

rebased

  - Factor out differences between constructors and regular methods
    into an virtual call to a helper class
  - Tease apart symbol creation/entry from synthesis of the default
    getter tree to prepare for a subsequent commit that will perform
    the first part eagerly.
  - Add a test to show the unstable order of the default getter
    symbols in the owner's scope.
@retronym retronym force-pushed the topic/eager-default-getters branch from 1fcb418 to d8f5db0 Compare May 28, 2018 03:38
@retronym retronym assigned lrytz and unassigned adriaanm May 28, 2018
@retronym
Copy link
Member Author

retronym commented May 28, 2018

rebased again

@adriaanm
Copy link
Contributor

Looks like this broke paradise

java.lang.AssertionError: assertion failed: 
  (class InteropIdentity,copy$default$1)
     while compiling: /home/travis/build/scala/scala/test/macro-annot/src/test/scala/run/InteropCaseSynthesis.scala
        during phase: typer
     library version: version 2.13.0-20180528-033852-61322fc
    compiler version: version 2.13.0-20180528-033852-61322fc
  reconstructed args: -classpath /home/travis/build/scala/scala/test/macro-annot/target/test-classes:/home/travis/build/scala/scala/test/macro-annot/target/classes:/home/travis/build/scala/scala/build/quick/classes/reflect:/home/travis/build/scala/scala/build/quick/classes/compiler:/home/travis/build/scala/scala/build/quick/classes/repl:/home/travis/build/scala/scala/build/quick/classes/interactive:/home/travis/build/scala/scala/build/quick/classes/repl-frontend:/home/travis/build/scala/scala/build/quick/classes/scaladoc:/home/travis/.ivy2/cache/org.scala-lang.modules/scala-asm/bundles/scala-asm-6.0.0-scala-1.jar:/home/travis/.ivy2/cache/jline/jline/jars/jline-2.14.6.jar:/home/travis/.ivy2/cache/junit/junit/jars/junit-4.11.jar:/home/travis/.ivy2/cache/org.hamcrest/hamcrest-core/jars/hamcrest-core-1.3.jar:/home/travis/.ivy2/cache/com.novocode/junit-interface/jars/junit-interface-0.11.jar:/home/travis/.ivy2/cache/org.scala-sbt/test-interface/jars/test-interface-1.0.jar -Ywarn-unused-import -bootclasspath /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/resources.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/rt.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/sunrsasign.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jsse.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jce.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/charsets.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jfr.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/classes:/home/travis/build/scala/scala/build/quick/classes/library -Xfatal-warnings -Ywarn-unused:imports -Ymacro-annotations
  last tree to typer: This(class InteropIdentity)
       tree position: line 6 of /home/travis/build/scala/scala/test/macro-annot/src/test/scala/run/InteropCaseSynthesis.scala
            tree tpe: InteropIdentity.this.type
              symbol: case class InteropIdentity
   symbol definition: case class InteropIdentity extends Product with Serializable (a ClassSymbol)
      symbol package: <empty>
       symbol owners: class InteropIdentity
           call site: method copy in class InteropIdentity in package <empty>
== Source file context for tree position ==
     3 import org.junit.runners._
     4 import Assert._
     5 
     6 @identity case class InteropIdentity(x: Int)
     7 @placebo case class InteropPlacebo(x: Int)
     8 
     9 @RunWith(classOf[JUnit4])
	at scala.reflect.internal.SymbolTable.throwAssertionError(SymbolTable.scala:163)
	at scala.tools.nsc.typechecker.Namers$Namer.$anonfun$addDefaultGetters$10(Namers.scala:1572)
	at scala.tools.nsc.typechecker.Namers$Namer$DefaultMethodInOwningScope.addGetter(Namers.scala:1648)
	at scala.tools.nsc.typechecker.Namers$Namer.$anonfun$addDefaultGetters$8(Namers.scala:1541)
	at scala.tools.nsc.typechecker.Namers$Namer.$anonfun$addDefaultGetters$6(Namers.scala:1512)
	at scala.collection.LinearSeqOps.foldLeft(LinearSeq.scala:126)
	at scala.collection.LinearSeqOps.foldLeft$(LinearSeq.scala:122)
	at scala.collection.immutable.List.foldLeft(List.scala:72)
	at scala.tools.nsc.typechecker.Namers$Namer.addDefaultGetters(Namers.scala:1508)
	at scala.tools.nsc.typechecker.Namers$Namer.methodSig(Namers.scala:1395)
	at scala.tools.nsc.typechecker.Namers$Namer.memberSig(Namers.scala:1860)
	at scala.tools.nsc.typechecker.Namers$Namer.typeSig(Namers.scala:1826)
	at scala.tools.nsc.typechecker.Namers$Namer$MonoTypeCompleter.completeImpl(Namers.scala:848)
	at scala.tools.nsc.typechecker.Namers$LockingTypeCompleter.complete(Namers.scala:2023)
	at scala.tools.nsc.typechecker.Namers$LockingTypeCompleter.complete$(Namers.scala:2021)
	at scala.tools.nsc.typechecker.Namers$TypeCompleterBase.complete(Namers.scala:2016)
	at scala.tools.nsc.typechecker.Namers$CompleterWrapper.complete(Namers.scala:2072)
	at scala.tools.nsc.typechecker.Namers$Namer$$anon$3.complete(Namers.scala:612)
	at scala.reflect.internal.Symbols$Symbol.info(Symbols.scala:1527)
	at scala.reflect.internal.Symbols$Symbol.initialize(Symbols.scala:1675)
	at scala.tools.nsc.typechecker.Typers$Typer.$anonfun$typedStats$6(Typers.scala:3198)
	at scala.tools.nsc.typechecker.Typers$Typer.$anonfun$typedStats$6$adapted(Typers.scala:3196)
	at scala.Option$WithFilter.foreach(Option.scala:270)
	at scala.tools.nsc.typechecker.Typers$Typer.$anonfun$typedStats$4(Typers.scala:3196)
	at scala.tools.nsc.typechecker.Typers$Typer.$anonfun$typedStats$4$adapted(Typers.scala:3194)
	at scala.reflect.internal.Scopes$Scope.foreach(Scopes.scala:409)
	at scala.tools.nsc.typechecker.Typers$Typer.addSynthetics$1(Typers.scala:3194)
	at scala.tools.nsc.typechecker.Typers$Typer.typedStats(Typers.scala:3256)

@retronym retronym self-assigned this May 29, 2018
@retronym
Copy link
Member Author

I'll chase that problem down soon.

@retronym
Copy link
Member Author

retronym commented May 29, 2018

I guess something like this is needed:

diff --git a/src/compiler/scala/tools/nsc/typechecker/MacroAnnotationNamers.scala b/src/compiler/scala/tools/nsc/typechecker/MacroAnnotationNamers.scala
index 0444d547d1..e116fd5432 100644
--- a/src/compiler/scala/tools/nsc/typechecker/MacroAnnotationNamers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/MacroAnnotationNamers.scala
@@ -272,8 +272,13 @@ trait MacroAnnotationNamers { self: Analyzer =>
             if (isEnumConstant(tree))
               tree.symbol setInfo ConstantType(Constant(tree.symbol))
           case tree @ DefDef(_, nme.CONSTRUCTOR, _, _, _, _) =>
+            if (mexists(tree.vparamss)(_.mods.hasDefault))
+              enterDefaultGetters(tree.symbol, tree, tree.vparamss, tree.tparams)
             sym setInfo completerOf(tree)
           case tree @ DefDef(mods, name, tparams, _, _, _) =>
+            if (mexists(tree.vparamss)(_.mods.hasDefault))
+              enterDefaultGetters(tree.symbol, tree, tree.vparamss, tree.tparams)
+
             val completer =
               if (sym hasFlag SYNTHETIC) {
                 if (name == nme.copy) copyMethodCompleter(tree)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index 0266dd86cc..c97824dfb0 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -1432,7 +1432,7 @@ trait Namers extends MethodSynthesis {
     /**
      * For every default argument, insert a method symbol computing that default
      */
-    private def enterDefaultGetters(meth: Symbol, ddef: DefDef, vparamss: List[List[ValDef]], tparams: List[TypeDef]) {
+    def enterDefaultGetters(meth: Symbol, ddef: DefDef, vparamss: List[List[ValDef]], tparams: List[TypeDef]) {
       val methOwner  = meth.owner
       val search = DefaultGetterNamerSearch(context, meth, initCompanionModule = false)
       var posCounter = 1

But I can't get the macro annotation test suite to run:

> macroAnnot/test:compile
[info] Compiling 50 Scala sources to /Users/jz/code/scala/test/macro-annot/target/test-classes...
[error] /Users/jz/code/scala/test/macro-annot/src/test/scala/run/PackageObject.scala:7: macro implementation not found: macroTransform
[error] (the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them)
[error]   @doubler def foo(x: Int) = x
[error]    ^
[error] /Users/jz/code/scala/test/macro-annot/src/test/scala/run/PackageObject.scala:15: macro implementation not found: macroTransform
[error] (the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them)
[error]   @hello object `package`
[error]    ^
[error] two errors found
[error] (macroAnnot/test:compileIncremental) Compilation failed
[error] Total time: 6 s, completed 30 May 2018, 9:31:50 am

Debugging that with test.args

$ dbg ./build/quick/bin/scalac @test.args

I see in this code:

  def macroExpandWithoutRuntime(typer: Typer, expandee: Tree): MacroStatus = {
    import typer.TyperErrorGen._
    val fallbackSym = expandee.symbol.nextOverriddenSymbol orElse MacroImplementationNotFoundError(expandee)
    macroLogLite(s"falling back to: $fallbackSym")
expandee.symbol.debugLocationString = macro method macroTransform in class hello
expandee.symbol.nextOverriddenSymbol = None

Why is this looking for the overriden symbol of the macroTransform method? @xeno-by any clues? It was added in https://github.com/scala/scala/pull/1159/files#diff-269d2d5528eed96b476aded2ea039444R809

@retronym
Copy link
Member Author

Oh, its another instance of the JDK9+ problem in which global.classPath.asURLs can give a null for for the platform classpath. The NPE is is triggered in:

 protected def findMacroClassLoader(): ClassLoader = {
    val classpath = global.classPath.asURLs
    def newLoader = () => {
      macroLogVerbose("macro classloader: initializing from -cp: %s".format(classpath))
      ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader)
    }

    import scala.tools.nsc.io.Jar
    import scala.reflect.io.{AbstractFile, Path}
    val locations = classpath.map(u => Path(AbstractFile.getURL(u).file))

And swallowed in:

    def resolveRuntime(): MacroRuntime = {
      if (className == Predef_???.owner.javaClassName && methName == Predef_???.name.encoded) {
        args => throw new AbortMacroException(args.c.enclosingPosition, "macro implementation is missing")
      } else {
        try {
          macroLogVerbose(s"resolving macro implementation as $className.$methName (isBundle = $isBundle)")
          macroLogVerbose(s"classloader is: ${ReflectionUtils.show(defaultMacroClassloader)}")
          resolveJavaReflectionRuntime(defaultMacroClassloader)
        } catch {
          case ex: Exception =>
            macroLogVerbose(s"macro runtime failed to load: ${ex.toString}")
            macroDef setFlag IS_ERROR
            null
        }
      }
    }

And the null macro runtime gets into some fallback code path in macroExpandWithoutRuntime.

@retronym
Copy link
Member Author

The Java 9 thing was fixed in 2.12.x but accidentally lost in the merge to 2.13.x. Bringing it back in: #6695

This stabilizes the order they appear in the owners scope. Previously,
their order was goverened by the order that the methods bearing
default parameters were type completed.

Make macro annotations compatible with these changes
@retronym retronym force-pushed the topic/eager-default-getters branch from 991c4df to 87be453 Compare May 30, 2018 03:20
@retronym
Copy link
Member Author

retronym commented May 30, 2018

@adriaanm I've fixed the macro annotation incompatibility.

@@ -27,7 +27,9 @@ trait NamesDefaults { self: Analyzer =>
// we need the ClassDef. To create and enter the symbols into the companion
// object, we need the templateNamer of that module class. These two are stored
// as an attachment in the companion module symbol
class ConstructorDefaultsAttachment(val classWithDefault: ClassDef, var companionModuleClassNamer: Namer)
class ConstructorDefaultsAttachment(val classWithDefault: ClassDef, var companionModuleClassNamer: Namer) {
var defaults = mutable.ListBuffer[Symbol]()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this a var?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be a val, I guess I started out with a var default: List[..] but it worked out more convenient to use a mutable collection.

Copy link
Contributor

@adriaanm adriaanm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tricky stuff! Can merge as is, but would be nice to update the comment in the new test

@@ -18,6 +18,6 @@ class NamerTest extends BytecodeTesting {
compiler.compileClasses("package p1; class Test { C.b(); C.a() }; object C { def a(x: Int = 0) = 0; def b(x: Int = 0) = 0 }")
val methods = compiler.global.rootMirror.getRequiredModule("p1.C").info.decls.toList.map(_.name.toString).filter(_.matches("""(a|b).*"""))
def getterName(s: String) = nme.defaultGetterName(TermName(s), 1).toString
Assert.assertEquals(List("a", "b", getterName("b"), getterName("a")), methods) // order depends on order of lazy type completion :(
Assert.assertEquals(List("a", getterName("a"), "b", getterName("b")), methods) // order depends on order of lazy type completion :(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment now outdated?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a commit to replace :( with :)

val default = owner.newMethodSymbol(name, vparam.pos, paramFlagsToDefaultGetter(meth.flags))
default.setPrivateWithin(meth.privateWithin)
default.referenced = meth
default.setInfo(ErrorType)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reminds me of our discussion on generalizing unit.synthetics to take tree creators rather than trees

@retronym
Copy link
Member Author

reminds me of our discussion on generalizing unit.synthetics to take tree creators rather than trees

FTR, here's the pattern of case-class-esque synthesis that I'd like to make more straight forward with lazy synthetics:

https://github.com/retronym/boxer/blob/topic/trait-field/plugin/src/main/scala/demo/DemoPlugin.scala#L54-L63

@adriaanm
Copy link
Contributor

👌 I'll hit merge when ✅

@lrytz
Copy link
Member

lrytz commented May 30, 2018

Thanks for reviewing!

@adriaanm

This comment has been minimized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants