8000 Delay package object decls entering to the package object phase by dwijnand · Pull Request #9661 · scala/scala · GitHub
[go: up one dir, main page]

Skip to content

Delay package object decls entering to the package object phase #9661

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
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/compiler/scala/tools/nsc/Global.scala
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ class Global(var currentSettings: Settings, reporter0: Reporter)
import definitions.findNamedMember
def findMemberFromRoot(fullName: Name): Symbol = rootMirror.findMemberFromRoot(fullName)

override def openPackageModule(pkgClass: Symbol, force: Boolean): Unit = {
if (force || isPast(currentRun.namerPhase)) super.openPackageModule(pkgClass, true)
else analyzer.packageObjects.deferredOpen.add(pkgClass)
}

// alternate constructors ------------------------------------------

override def settings = currentSettings
Expand Down Expand Up @@ -1649,8 +1654,7 @@ class Global(var currentSettings: Settings, reporter0: Reporter)
compileLate(new CompilationUnit(scripted(getSourceFile(file))))
}

/** Compile abstract file until `globalPhase`, but at least to phase "namer".
*/
/** Compile the unit until `globalPhase`, but at least to phase "typer". */
def compileLate(unit: CompilationUnit): Unit = {
addUnit(unit)

Expand Down
5 changes: 5 additions & 0 deletions src/compiler/scala/tools/nsc/typechecker/Analyzer.scala
8000
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ trait Analyzer extends AnyRef
object packageObjects extends {
val global: Analyzer.this.global.type = Analyzer.this.global
} with SubComponent {
val deferredOpen = perRunCaches.newSet[Symbol]()
val phaseName = "packageobjects"
val runsAfter = List[String]()
val runsRightAfter= Some("namer")
Expand All @@ -64,6 +65,9 @@ trait Analyzer extends AnyRef
override def traverse(tree: Tree): Unit = tree match {
case ModuleDef(_, _, _) =>
if (tree.symbol.name == nme.PACKAGEkw) {
// we've actually got a source file
deferredOpen.remove(tree.symbol.owner)

openPackageModule(tree.symbol, tree.symbol.owner)
}
case ClassDef(_, _, _, _) => () // make it fast
Expand All @@ -73,6 +77,7 @@ trait Analyzer extends AnyRef

def apply(unit: CompilationUnit): Unit = {
openPackageObjectsTraverser(unit.body)
deferredOpen.foreach(openPackageModule(_))
}
}
}
Expand Down
11 changes: 0 additions & 11 deletions src/compiler/scala/tools/nsc/typechecker/Namers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -467,17 +467,6 @@ trait Namers extends MethodSynthesis {

val existingModule = context.scope lookupModule tree.name
if (existingModule.isModule && !existingModule.hasPackageFlag && inCurrentScope(existingModule) && (currentRun.canRedefine(existingModule) || existingModule.isSynthetic)) {
// This code accounts for the way the package objects found in the classpath are opened up
// early by the completer of the package itself. If the `packageobjects` phase then finds
// the same package object in sources, we have to clean the slate and remove package object
// members from the package class.
//
// TODO scala/bug#4695 Pursue the approach in https://github.com/scala/scala/pull/2789 that avoids
// opening up the package object on the classpath at all if one exists in source.
if (existingModule.isPackageObject) {
val packageScope = existingModule.enclosingPackageClass.rawInfo.decls
packageScope.foreach(mem => if (mem.owner != existingModule.enclosingPackageClass) packageScope unlink mem)
}
updatePosFlags(existingModule, tree.pos, moduleFlags)
setPrivateWithin(tree, existingModule)
existingModule.moduleClass andAlso (setPrivateWithin(tree, _))
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/scala/tools/reflect/ReflectGlobal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,7 @@ class ReflectGlobal(currentSettings: Settings, reporter: Reporter, override val
override implicit val MirrorTag: ClassTag[Mirror] = ClassTag[Mirror](classOf[Mirror])
override type RuntimeClass = java.lang.Class[_]
override implicit val RuntimeClassTag: ClassTag[RuntimeClass] = ClassTag[RuntimeClass](classOf[RuntimeClass])

override def openPackageModule(pkgClass: Symbol, force: Boolean): Unit = super.openPackageModule(pkgClass, true)
}

9 changes: 9 additions & 0 deletions src/interactive/scala/tools/nsc/interactive/Global.scala
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,15 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
val platform: Global.this.platform.type = Global.this.platform
} with BrowsingLoaders

override def openPackageModule(pkgClass: Symbol, force: Boolean): Unit = {
val isPastNamer = force || currentTyperRun == null || (currentTyperRun.currentUnit match {
case unit: RichCompilationUnit => unit.isParsed
case _ => true
})
if (isPastNamer) super.openPackageModule(pkgClass, true)
else analyzer.packageObjects.deferredOpen.add(pkgClass)
}

// ----------------- Polling ---------------------------------------

case class WorkEvent(atNode: Int, atMillis: Long)
Expand Down
2 changes: 1 addition & 1 deletion src/reflect/scala/reflect/internal/SymbolTable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ abstract class SymbolTable extends macros.Universe
}

/** if there's a `package` member object in `pkgClass`, enter its members into it. */
def openPackageModule(pkgClass: Symbol): Unit = {
def openPackageModule(pkgClass: Symbol, force: Boolean = false): Unit = {

val pkgModule = pkgClass.packageObject
def fromSource = pkgModule.rawInfo match {
Expand Down
40 changes: 40 additions & 0 deletions test/files/run/package-object-stale-decl.scala
A3E2
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import scala.reflect.io.Path
import scala.tools.partest._
import java.io.File

object Test extends StoreReporterDirectTest {
class V1 {
def pkg = "package object b extends B"
def B = "package b; class B { def stale = 42 }"
def A = "package b; class A { stale }"
}
class V2 extends V1 {
override def B = "package b; class B { }"
}

override def extraSettings = s"-cp ${sys.props("partest.lib")}${File.pathSeparator}$testOutput"

def show(): Unit = {
val v1 = new V1
val v2 = new V2
compiles(v1.A, v1.B, v1.pkg)()
delete(testOutput / "b" / "A.class")
compiles(v2.B, v2.A)(Some("not found: value stale"))
}

def compiles(codes: String*)(expectedError: Option[String] = None) = {
val global = newCompiler()
withRun(global)(_ compileSources newSources(codes: _*))
val reporterOutput = storeReporter.infos.map(x => x.pos.showError(x.msg)).mkString("\n")
expectedError match {
case None =>
assert(!global.reporter.hasErrors, reporterOutput)
case Some(text) =>
assert(global.reporter.hasErrors, "expected compile failure, got success")
assert(reporterOutput.contains(text), reporterOutput)
}
}

def delete(paths: Path*) = paths.foreach(p => assert(p.delete(), s"$p didn't delete"))
def code = ""
}
40 changes: 40 additions & 0 deletions test/files/run/package-object-toolbox.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import java.io.File
import java.net.URLClassLoader

import scala.reflect.io.Path
import scala.reflect.runtime.{ universe => ru }
import scala.tools.partest._
import scala.tools.reflect.ToolBox

import org.junit.Assert._

object Test extends StoreReporterDirectTest {
val cp = List(sys.props("partest.lib"), testOutput.path)
override def extraSettings = s"-cp ${cp.mkString(File.pathSeparator)}"

def show(): Unit = {
compiles("package object pkg { def foo = 1 }")
val loader = new URLClassLoader(cp.map(new File(_).toURI.toURL).toArray)
val mirror = ru.runtimeMirror(loader)

val toolbox = mirror.mkToolBox()
val result1 = toolbox.eval(toolbox.parse("pkg.foo"))
assertEquals(1, result1)

val obj = toolbox.eval(toolbox.parse("pkg.`package`"))
val pkg = mirror.staticPackage("pkg")
val sym = pkg.info.decl(ru.TermName("foo")).asMethod
val meth = mirror.reflect(obj).reflectMethod(sym)
val res2 = meth.apply()
assertEquals(1, res2)
}

def compiles(codes: String*) = {
val global = newCompiler()
withRun(global)(_ compileSources newSources(codes: _*))
assert(!global.reporter.hasErrors, storeReporter.infos.mkString("\n"))
}

def delete(paths: Path*) = paths.foreach(p => assert(p.delete(), s"$p didn't delete"))
def code = ""
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import scala.reflect.io.Path
import scala.tools.partest._
import java.io.File

object Test extends StoreReporterDirectTest {
def A = "package b; class A"
def pkg = "package object b extends A"

override def extraSettings = s"-cp ${sys.props("partest.lib")}${File.pathSeparator}$testOutput"

def show(): Unit = {
compiles(A, pkg)
delete(testOutput / "b" / "A.class")
compiles(A)
}

def compiles(codes: String*) = {
val global = newCompiler()
withRun(global)(_ compileSources newSources(codes: _*))
assert(!global.reporter.hasErrors, storeReporter.infos.mkString("\n"))
}

def delete(paths: Path*) = paths.foreach(p => assert(p.delete(), s"$p didn't delete"))
def code = ""
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import scala.reflect.io.Path
import scala.tools.partest._
import java.io.File

object Test extends StoreReporterDirectTest {
def A = "package b; class A"
def pkg = "package object b extends A"
def M = "package b; class M"

override def extraSettings = s"-cp ${sys.props("partest.lib")}${File.pathSeparator}$testOutput"

def show(): Unit = {
compiles(A, pkg, M)
delete(testOutput / "b" / "A.class")
compiles(M, A)
}

def compiles(codes: String*) = {
val global = newCompiler()
withRun(global)(_ compileSources newSources(codes: _*))
assert(!global.reporter.hasErrors, storeReporter.infos.mkString("\n"))
}

def delete(paths: Path*) = paths.foreach(p => assert(p.delete(), s"$p didn't delete"))
def code = ""
}
33 changes: 33 additions & 0 deletions test/files/run/package-object-with-inner-class-in-ancestor.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import scala.reflect.io.Path
import scala.tools.partest._
import java.io.File

object Test extends StoreReporterDirectTest {
class V1 {
def O = "package b; object O { def o = \"\" }"
def A = "package b; class A { class C { O.o } }"
def pkg = "package object b extends A"
}
class V2 extends V1 {
override def O = "package b; object O { def o = 42 }"
}

override def extraSettings = s"-cp ${sys.props("partest.lib")}${File.pathSeparator}$testOutput"

def show(): Unit = {
val v1 = new V1
compiles(v1.O, v1.A, v1.pkg)
delete(testOutput / "b" / "A.class", testOutput / "b" / "A$C.class")
val v2 = new V2
compiles(v2.O, v2.A)
}

def compiles(codes: String*) = {
val global = newCompiler()
withRun(global)(_ compileSources newSources(codes: _*))
assert(!global.reporter.hasErrors, storeReporter.infos.mkString("\n"))
}

def delete(paths: Path*) = paths.foreach(p => assert(p.delete(), s"$p didn't delete"))
def code = ""
}
8 changes: 8 additions & 0 deletions test/junit/scala/tools/nsc/DeterminismTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,14 @@ class DeterminismTest {
test(List(code))
}

@Test def testPackageObjectUserLand(): Unit = {
def code = List[SourceFile](
source("package.scala", "package userland; object `package` { type Throwy = java.lang.Throwable }"),
source("th.scala", "package userland; class th[T <: Throwy](cause: T = null)")
)
test(code :: Nil)
}

def source(name: String, code: String): SourceFile = new BatchSourceFile(name, code)
}

Expand Down
0