Skip to content

Commit

Permalink
Merge pull request #424 from eed3si9n/wip/417
Browse files Browse the repository at this point in the history
Fixes undercompilation on inheritance on same source
  • Loading branch information
dwijnand committed Oct 12, 2017
2 parents 291cefa + 9403160 commit 1a7dc5d
Show file tree
Hide file tree
Showing 25 changed files with 165 additions and 31 deletions.
21 changes: 13 additions & 8 deletions internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala
Expand Up @@ -92,16 +92,21 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with
}

// Define processor reusing `processDependency` definition
val memberRef = processDependency(DependencyByMemberRef) _
val inheritance = processDependency(DependencyByInheritance) _
val localInheritance = processDependency(LocalDependencyByInheritance) _
val memberRef = processDependency(DependencyByMemberRef, false) _
val inheritance = processDependency(DependencyByInheritance, true) _
val localInheritance = processDependency(LocalDependencyByInheritance, true) _

@deprecated("Use processDependency that takes allowLocal.", "1.1.0")
def processDependency(context: DependencyContext)(dep: ClassDependency): Unit =
processDependency(context, true)(dep)

/*
* Handles dependency on given symbol by trying to figure out if represents a term
* that is coming from either source code (not necessarily compiled in this compilation
* run) or from class file and calls respective callback method.
*/
def processDependency(context: DependencyContext)(dep: ClassDependency): Unit = {
def processDependency(context: DependencyContext, allowLocal: Boolean)(
dep: ClassDependency): Unit = {
val fromClassName = classNameAsString(dep.from)

def binaryDependency(file: File, binaryClassName: String) =
Expand Down Expand Up @@ -133,11 +138,12 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with
case None =>
debuglog(Feedback.noOriginFileForExternalSymbol(dep.to))
}
} else if (onSource.file != sourceFile) {
// Dependency is internal -- but from other file / compilation unit
} else if (onSource.file != sourceFile || allowLocal) {
// We cannot ignore dependencies coming from the same source file because
// the dependency info needs to propagate. See source-dependencies/trait-trait-211.
val onClassName = classNameAsString(dep.to)
callback.classDependency(onClassName, fromClassName, context)
} else () // Comes from the same file, ignore
}
}
}

Expand Down Expand Up @@ -227,7 +233,6 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with
val depClass = enclOrModuleClass(dep)
val dependency = ClassDependency(fromClass, depClass)
if (!cache.contains(dependency) &&
fromClass.associatedFile != depClass.associatedFile &&
!depClass.isRefinementClass) {
process(dependency)
cache.add(dependency)
Expand Down
40 changes: 19 additions & 21 deletions internal/zinc-core/src/main/scala/sbt/internal/inc/Relations.scala
Expand Up @@ -275,6 +275,7 @@ object Relations {
case o: ClassDependencies => internal == o.internal && external == o.external
case _ => false
}
override def toString: String = s"ClassDependencies(internal = $internal, external = $external)"

override def hashCode = (internal, external).hashCode
}
Expand Down Expand Up @@ -661,26 +662,23 @@ private class MRelationsNameHashing(
override def hashCode =
(srcProd :: libraryDep :: libraryClassName :: memberRef :: inheritance :: classes :: Nil).hashCode

override def toString = (
"""
override def toString: String = {
val internalDepsStr = (internalDependencies.dependencies map {
case (k, vs) => k + " " + relation_s(vs)
}).mkString("\n ", "\n ", "")
val externalDepsStr = (externalDependencies.dependencies map {
case (k, vs) => k + " " + relation_s(vs)
}).mkString("\n ", "\n ", "")
s"""
|Relations (with name hashing enabled):
| products: %s
| library deps: %s
| library class names: %s
| class deps: %s
| ext deps: %s
| class names: %s
| used names: %s
| product class names: %s
""".trim.stripMargin.format(
List(srcProd,
libraryDep,
libraryClassName,
internalClassDep,
externalClassDep,
classes,
names,
productClassName) map relation_s: _*)
)

| products: ${relation_s(srcProd)}
| library deps: ${relation_s(libraryDep)}
| library class names: ${relation_s(libraryClassName)}
| internalDependencies: $internalDepsStr
| externalDependencies: $externalDepsStr
| class names: ${relation_s(classes)}
| used names: ${relation_s(names)}
| product class names: ${relation_s(productClassName)}
""".trim.stripMargin
}
}
@@ -0,0 +1,6 @@
package foo

// This class is used to pad the number of source code.
class Bar {
def bar: Unit = ???
}
@@ -0,0 +1,6 @@
package foo

// This class is used to pad the number of source code.
class Foo {
def foo: Unit = ???
}
@@ -0,0 +1 @@
scalac.options = -Xfatal-warnings
3 changes: 1 addition & 2 deletions zinc/src/sbt-test/source-dependencies/sealed/test
Expand Up @@ -5,5 +5,4 @@ $ copy-file changes/A.scala A.scala

# D.scala needs recompiling because the pattern match in D
# is no longer exhaustive, which emits a warning
> checkRecompilations 1 A B C E
> checkRecompilations 2 D
-> compile
@@ -0,0 +1,8 @@
{
"projects": [
{
"name": "mirtest",
"scalaVersion": "2.11.8"
}
]
}
@@ -0,0 +1,12 @@
package gg
package table

trait A {
def transform: Unit = {
// the use site is updated
buildNonemptyObjects(0, 1)
}

// add extra parameter here
def buildNonemptyObjects(a: Int, b: Int): Unit = ()
}
@@ -0,0 +1,10 @@
package gg
package table

trait A {
def transform: Unit = {
buildNonemptyObjects(0)
}

def buildNonemptyObjects(a: Int): Unit = ()
}
@@ -0,0 +1,9 @@
package xx

import gg.table._

trait C extends B {
}

trait B extends A {
}
@@ -0,0 +1,8 @@
package xx

object Hello extends App {
val consumer = new Foo
consumer.transform
}

class Foo extends C
@@ -0,0 +1 @@
relationsDebug = true
5 changes: 5 additions & 0 deletions zinc/src/sbt-test/source-dependencies/trait-trait-211/test
@@ -0,0 +1,5 @@
> mirtest/run

## After copying the Good implementation, we should be able to run successfully.
$ copy-file changes/A1.scala mirtest/A.scala
> mirtest/run
@@ -0,0 +1,8 @@
{
"projects": [
{
"name": "mirtest",
"scalaVersion": "2.12.3"
}
]
}
@@ -0,0 +1,12 @@
package gg
package table

trait A {
def transform: Unit = {
// the use site is updated
buildNonemptyObjects(0, 1)
}

// add extra parameter here
def buildNonemptyObjects(a: Int, b: Int): Unit = ()
}
@@ -0,0 +1,10 @@
package gg
package table

trait A {
def transform: Unit = {
buildNonemptyObjects(0)
}

def buildNonemptyObjects(a: Int): Unit = ()
}
@@ -0,0 +1,9 @@
package xx

import gg.table._

trait C extends B {
}

trait B extends A {
}
@@ -0,0 +1,8 @@
package xx

object Hello extends App {
val consumer = new Foo
consumer.transform
}

class Foo extends C
@@ -0,0 +1 @@
relationsDebug = true
5 changes: 5 additions & 0 deletions zinc/src/sbt-test/source-dependencies/trait-trait-212/test
@@ -0,0 +1,5 @@
> mirtest/run

## After copying the Good implementation, we should be able to run successfully.
$ copy-file changes/A1.scala mirtest/A.scala
> mirtest/run
@@ -0,0 +1 @@
abstract class A
@@ -0,0 +1,2 @@
class B extends A
class C extends B
@@ -0,0 +1,4 @@
class D extends C
object Hello extends App {
new D
}
@@ -0,0 +1,3 @@
abstract class A {
def foo: String = ""
}
3 changes: 3 additions & 0 deletions zinc/src/sbt-test/source-dependencies/transitive-class/test
@@ -0,0 +1,3 @@
> compile
$ copy-file changes/A.scala A.scala
> checkRecompilations 2 A B C D Hello

0 comments on commit 1a7dc5d

Please sign in to comment.