From 6ab7c1da8987806e0e3cbc820781b6ef955c109b Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Thu, 23 Mar 2017 16:54:58 +0100 Subject: [PATCH 1/3] Revert "sbt.ExtractDependencies: avoid false dependencies" This reverts commit 57641b9c7447fa0a6f1f47352dffb4c56c560b6a. Using `Type#select` instead of `Type#member` turned out to not help at all in avoiding false dependencies, you can still get a symbol whose denotation does not reallyExists. A proper fix for this issue is contained in the next commit. --- compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala index c392880c5cc8..fefa63f6fdc4 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala @@ -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)) From 65455bbcaace514ea34a30a3fc102c8ecee16498 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Thu, 23 Mar 2017 17:31:43 +0100 Subject: [PATCH 2/3] 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 `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. The added test is currently marked pending because it does not work with JUnit (which doesn't handle separate compilation), only partest. I didn't managed to get it to work right, and this won't be necessary once our testing framework is overhauled by https://github.com/lampepfl/dotty/pull/2125 anyway, so I'll just have to remember to enable this test afterwards. --- .../src/dotty/tools/dotc/typer/Namer.scala | 19 +++++++++++++++++++ .../false-companion/00_outerinnerTest_2.scala | 5 +++++ .../false-companion/01_outerinnerFoo_2.scala | 5 +++++ .../pos/false-companion/outerFoo_1.scala | 2 ++ .../pos/false-companion/outerinnerFoo_1.scala | 5 +++++ 5 files changed, 36 insertions(+) create mode 100644 tests/pending/pos/false-companion/00_outerinnerTest_2.scala create mode 100644 tests/pending/pos/false-companion/01_outerinnerFoo_2.scala create mode 100644 tests/pending/pos/false-companion/outerFoo_1.scala create mode 100644 tests/pending/pos/false-companion/outerinnerFoo_1.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 2cefc52a084e..706e8b5bf3a3 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -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.denotNamed(moduleName.encode).symbol + if (moduleSym.isDefinedInCurrentRun) { + val className = moduleName.stripModuleClassSuffix.toTypeName + val classSym = ctx.denotNamed(className.encode).symbol + if (!classSym.isDefinedInCurrentRun) { + val absentClassSymbol = ctx.newClassSymbol(ctx.owner, className, EmptyFlags, _ => NoType) + enterSymbol(absentClassSymbol) + } + } + } + } } stats.foreach(expand) diff --git a/tests/pending/pos/false-companion/00_outerinnerTest_2.scala b/tests/pending/pos/false-companion/00_outerinnerTest_2.scala new file mode 100644 index 000000000000..8f5802343e92 --- /dev/null +++ b/tests/pending/pos/false-companion/00_outerinnerTest_2.scala @@ -0,0 +1,5 @@ +package outer +package inner +object Test { + val x: Foo = new Foo +} diff --git a/tests/pending/pos/false-companion/01_outerinnerFoo_2.scala b/tests/pending/pos/false-companion/01_outerinnerFoo_2.scala new file mode 100644 index 000000000000..111f7fcd5787 --- /dev/null +++ b/tests/pending/pos/false-companion/01_outerinnerFoo_2.scala @@ -0,0 +1,5 @@ +package outer +package inner +object Foo { + // val a: Int = 1 +} diff --git a/tests/pending/pos/false-companion/outerFoo_1.scala b/tests/pending/pos/false-companion/outerFoo_1.scala new file mode 100644 index 000000000000..8c2ef109c64a --- /dev/null +++ b/tests/pending/pos/false-companion/outerFoo_1.scala @@ -0,0 +1,2 @@ +package outer +class Foo diff --git a/tests/pending/pos/false-companion/outerinnerFoo_1.scala b/tests/pending/pos/false-companion/outerinnerFoo_1.scala new file mode 100644 index 000000000000..29f3df724e25 --- /dev/null +++ b/tests/pending/pos/false-companion/outerinnerFoo_1.scala @@ -0,0 +1,5 @@ +package outer +package inner +object Foo { + val a: Int = 1 +} From b6411818b2e84c0a28c501e2814de4cfde720208 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Thu, 23 Mar 2017 20:38:55 +0100 Subject: [PATCH 3/3] Namer#createCompanionLinks: avoid using denotNamed The previous commit introduced two new usages of `denotNamed` which broke tests/run/t1987b because `denotNamed` indirectly calls `packageObj` which caches the package object, except that at this point the package object is not yet entered, so the cache was incorrect. We fix this by using `effectiveScope.lookup` instead since we only need to look into the current scope, this is also true for the two existing usage of `denotNamed` in `createCompanionLinks` so they were also replaced by `effectiveScope.lookup`. --- compiler/src/dotty/tools/dotc/typer/Namer.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 706e8b5bf3a3..4077d8d6569e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -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 } @@ -613,10 +613,10 @@ class Namer { typer: Typer => // example where this matters. if (ctx.owner.is(PackageClass)) { for (cdef @ TypeDef(moduleName, _) <- moduleDef.values) { - val moduleSym = ctx.denotNamed(moduleName.encode).symbol + val moduleSym = ctx.effectiveScope.lookup(moduleName.encode) if (moduleSym.isDefinedInCurrentRun) { val className = moduleName.stripModuleClassSuffix.toTypeName - val classSym = ctx.denotNamed(className.encode).symbol + val classSym = ctx.effectiveScope.lookup(className.encode) if (!classSym.isDefinedInCurrentRun) { val absentClassSymbol = ctx.newClassSymbol(ctx.owner, className, EmptyFlags, _ => NoType) enterSymbol(absentClassSymbol)