diff options
author | Eugene Burmako <xeno.by@gmail.com> | 2012-12-25 02:37:31 +0100 |
---|---|---|
committer | Eugene Burmako <xeno.by@gmail.com> | 2013-01-05 01:39:15 +0300 |
commit | 348c8fac9f897f9661f84e32949b8a4e0c99e93a (patch) | |
tree | 810b2d7d53451ceba5e43e1687a86cdedffd39f1 /src/compiler/scala/tools | |
parent | ed4f4798df2ac104d9557de2241d8e89283b0662 (diff) | |
download | scala-348c8fac9f897f9661f84e32949b8a4e0c99e93a.tar.gz scala-348c8fac9f897f9661f84e32949b8a4e0c99e93a.tar.bz2 scala-348c8fac9f897f9661f84e32949b8a4e0c99e93a.zip |
adds c.introduceTopLevel
The first in the family of mutators for the global symbol table,
`introduceTopLevel` is capable of creating synthetic top-level
classes and modules.
The addition of nme.EMPTY_PACKAGE_NAME is necessary to let
programmers insert definitions into the empty package. That's explicitly
discouraged in the docs, but at times might come in handy.
This patch introduce workarounds to avoid incompatibilities with SBT.
First of all SBT doesn't like VirtualFiles having JFile set to null.
Secondly SBT gets confused when someone depends on synthetic files
added by c.introduceTopLevel.
Strictly speaking these problems require changes to SBT, and that will be
done later. However the main target of the patch is paradise/macros,
which needs to be useful immediately, therefore we apply workarounds.
Diffstat (limited to 'src/compiler/scala/tools')
3 files changed, 31 insertions, 7 deletions
diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala index a2108b8ced..15d365ab8c 100644 --- a/src/compiler/scala/tools/nsc/CompilationUnits.scala +++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala @@ -39,11 +39,31 @@ trait CompilationUnits { self: Global => /** Note: depends now contains toplevel classes. * To get their sourcefiles, you need to dereference with .sourcefile */ - val depends = mutable.HashSet[Symbol]() + private[this] val _depends = mutable.HashSet[Symbol]() + // SBT compatibility (SI-6875) + // + // imagine we have a file named A.scala, which defines a trait named Foo and a module named Main + // Main contains a call to a macro, which calls c.introduceTopLevel to define a mock for Foo + // c.introduceTopLevel creates a virtual file Virt35af32.scala, which contains a class named FooMock extending Foo, + // and macro expansion instantiates FooMock. the stage is now set. let's see what happens next. + // + // without this workaround in scalac or without being patched itself, sbt will think that + // * Virt35af32 depends on A (because it extends Foo from A) + // * A depends on Virt35af32 (because it contains a macro expansion referring to FooMock from Virt35af32) + // + // after compiling A.scala, SBT will notice that it has a new source file named Virt35af32. + // it will also think that this file hasn't yet been compiled and since A depends on it + // it will think that A needs to be recompiled. + // + // recompilation will lead to another macro expansion. that another macro expansion might choose to create a fresh mock, + // producing another virtual file, say, Virtee509a, which will again trick SBT into thinking that A needs a recompile, + // which will lead to another macro expansion, which will produce another virtual file and so on + def depends = if (exists && !source.file.isVirtual) _depends else mutable.HashSet[Symbol]() /** so we can relink */ - val defined = mutable.HashSet[Symbol]() + private[this] val _defined = mutable.HashSet[Symbol]() + def defined = if (exists && !source.file.isVirtual) _defined else mutable.HashSet[Symbol]() /** Synthetic definitions generated by namer, eliminated by typer. */ diff --git a/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala index 8a9ce8907e..f1bf590ebf 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala @@ -23,10 +23,14 @@ abstract class SyntaxAnalyzer extends SubComponent with Parsers with MarkupParse def apply(unit: global.CompilationUnit) { import global._ informProgress("parsing " + unit) - unit.body = - if (unit.isJava) new JavaUnitParser(unit).parse() - else if (reporter.incompleteHandled) new UnitParser(unit).parse() - else new UnitParser(unit).smartParse() + // if the body is already filled in, do nothing + // otherwise compileLate is going to overwrite bodies of synthetic source files + if (unit.body == EmptyTree) { + unit.body = + if (unit.isJava) new JavaUnitParser(unit).parse() + else if (reporter.incompleteHandled) new UnitParser(unit).parse() + else new UnitParser(unit).smartParse() + } if (settings.Yrangepos.value && !reporter.hasErrors) validatePositions(unit.body) diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index 14c8d85836..dfc1196f2e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -394,7 +394,7 @@ trait NamesDefaults { self: Analyzer => // TODO #3649 can create spurious errors when companion object is gone (because it becomes unlinked from scope) if (defGetter == NoSymbol) None // prevent crash in erroneous trees, #3649 else { - var default1 = qual match { + var default1: Tree = qual match { case Some(q) => gen.mkAttributedSelect(q.duplicate, defGetter) case None => gen.mkAttributedRef(defGetter) |