Skip to content

Fix #2137: Create dummy companions for top-level objects without a real one #2139

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 3 commits into from
Mar 28, 2017
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
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ private class ExtractDependenciesCollector(implicit val ctx: Context) extends tp
override def traverse(tree: Tree)(implicit ctx: Context): Unit = {
tree match {
case Import(expr, selectors) =>
def lookupImported(name: Name) = expr.tpe.select(name).typeSymbol
def lookupImported(name: Name) = expr.tpe.member(name).symbol
def addImported(name: Name) = {
// importing a name means importing both a term and a type (if they exist)
addDependency(lookupImported(name.toTermName))
Expand Down
23 changes: 21 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -570,8 +570,8 @@ class Namer { typer: Typer =>

/** Create links between companion object and companion class */
def createLinks(classTree: TypeDef, moduleTree: TypeDef)(implicit ctx: Context) = {
val claz = ctx.denotNamed(classTree.name.encode).symbol
val modl = ctx.denotNamed(moduleTree.name.encode).symbol
val claz = ctx.effectiveScope.lookup(classTree.name.encode)
val modl = ctx.effectiveScope.lookup(moduleTree.name.encode)
ctx.synthesizeCompanionMethod(nme.COMPANION_CLASS_METHOD, claz, modl).entered
ctx.synthesizeCompanionMethod(nme.COMPANION_MODULE_METHOD, modl, claz).entered
}
Expand Down Expand Up @@ -605,6 +605,25 @@ class Namer { typer: Typer =>
case EmptyTree =>
}
}

// If a top-level object has no companion class in the current run, we
// enter a dummy companion class symbol (`denot.isAbsent` returns true) in
// scope. This ensures that we never use a companion from a previous run
// or from the classpath. See tests/pos/false-companion for an
// example where this matters.
if (ctx.owner.is(PackageClass)) {
for (cdef @ TypeDef(moduleName, _) <- moduleDef.values) {
val moduleSym = ctx.effectiveScope.lookup(moduleName.encode)
if (moduleSym.isDefinedInCurrentRun) {
val className = moduleName.stripModuleClassSuffix.toTypeName
val classSym = ctx.effectiveScope.lookup(className.encode)
if (!classSym.isDefinedInCurrentRun) {
val absentClassSymbol = ctx.newClassSymbol(ctx.owner, className, EmptyFlags, _ => NoType)
enterSymbol(absentClassSymbol)
}
}
}
}
}

stats.foreach(expand)
Expand Down
5 changes: 5 additions & 0 deletions tests/pending/pos/false-companion/00_outerinnerTest_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package outer
package inner
object Test {
val x: Foo = new Foo
}
5 changes: 5 additions & 0 deletions tests/pending/pos/false-companion/01_outerinnerFoo_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package outer
package inner
object Foo {
// val a: Int = 1
}
2 changes: 2 additions & 0 deletions tests/pending/pos/false-companion/outerFoo_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
package outer
class Foo
5 changes: 5 additions & 0 deletions tests/pending/pos/false-companion/outerinnerFoo_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package outer
package inner
object Foo {
val a: Int = 1
}