Skip to content

Commit dc50896

Browse files
committed
Fix #2137: Create dummy companions for top-level objects without a real one
Previously, we sometimes ended up forcing a companion class symbol from a previous run or from the classpath which lead to weird issues like in `tests/pos-special/false-companion`. Even if we end up not forcing such a symbol, its presence can still lead to issue: before this commit incremental compilation of `dotty-compiler-bootstrapped` was broken because we recorded a false dependency on the non-bootstrapped `dotty-compiler` jar.
1 parent 6ab7c1d commit dc50896

File tree

6 files changed

+51
-0
lines changed

6 files changed

+51
-0
lines changed

compiler/src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,25 @@ class Namer { typer: Typer =>
605605
case EmptyTree =>
606606
}
607607
}
608+
609+
// If a top-level object has no companion class in the current run, we
610+
// enter a dummy companion class symbol (`denot.isAbsent` returns true) in
611+
// scope. This ensures that we never use a companion from a previous run
612+
// or from the classpath. See tests/pos-special/false-companion for an
613+
// example where this matters.
614+
if (ctx.owner.is(PackageClass)) {
615+
for (cdef @ TypeDef(moduleName, _) <- moduleDef.values) {
616+
val moduleSym = ctx.denotNamed(moduleName.encode).symbol
617+
if (moduleSym.isDefinedInCurrentRun) {
618+
val className = moduleName.stripModuleClassSuffix.toTypeName
619+
val classSym = ctx.denotNamed(className.encode).symbol
620+
if (!classSym.isDefinedInCurrentRun) {
621+
val absentClassSymbol = ctx.newClassSymbol(ctx.owner, className, EmptyFlags, _ => NoType)
622+
enterSymbol(absentClassSymbol)
623+
}
624+
}
625+
}
626+
}
608627
}
609628

610629
stats.foreach(expand)

compiler/test/dotc/tests.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,21 @@ class tests extends CompilerTest {
168168
compileFile(posSpecialDir, "spec-t5545/S_1")
169169
compileFile(posSpecialDir, "spec-t5545/S_2")
170170
}
171+
@Test def pos_false_companion = {
172+
// compile by hand in two batches, since junit lacks the infrastructure to
173+
// compile files in multiple batches according to _1, _2, ... suffixes.
174+
val opts = List("-priorityclasspath", defaultOutputDir) ++ defaultOptions
175+
176+
compileList("pos_false_companion Part 1/2", List(
177+
s"$posSpecialDir/false-companion/outerFoo_1.scala",
178+
s"$posSpecialDir/false-companion/outerinnerFoo_1.scala"
179+
), opts)
180+
compileList("pos_false_companion Part 2/2", List(
181+
// The order in which we pass these files to the compiler is important
182+
s"$posSpecialDir/false-companion/00_outerinnerTest_2.scala",
183+
s"$posSpecialDir/false-companion/01_outerinnerFoo_2.scala"
184+
), opts)
185+
}
171186
@Test def pos_utf8 = compileFile(posSpecialDir, "utf8encoded", explicitUTF8)
172187
@Test def pos_utf16 = compileFile(posSpecialDir, "utf16encoded", explicitUTF16)
173188

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package outer
2+
package inner
3+
object Test {
4+
val x: Foo = new Foo
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package outer
2+
package inner
3+
object Foo {
4+
// val a: Int = 1
5+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
package outer
2+
class Foo
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package outer
2+
package inner
3+
object Foo {
4+
val a: Int = 1
5+
}

0 commit comments

Comments
 (0)