Skip to content

Private members included in API hash confuse name hashing #2490

@gkossakowski

Description

@gkossakowski

The #2441 made private members of traits to contribute to the API hash but left behavior of name hashing undefined. The current implementation doesn't include private members in name hashes. For example:

trait A {
  // uncomment B 
  // private object B
}

The overall api hash of A will change if you uncomment B but the hash of the name A will remain the same. This breaks a contract in incremental compiler that says there's always at least one name changed if api has changed (or its hash). Nothing blows up at the moment because that contract hasn't been enforced but it's enough to add

assert(modifiedNames.regularNames.nonEmpty || modifiedNames.implicitNames.nonEmpty,
    s"Modified names for $modified0 is empty")

to the sbt.inc.NamesChange and run trait-private-object scripted test to see:

[info] java.lang.AssertionError: assertion failed: Modified names for A is empty
[info]  at scala.Predef$.assert(Predef.scala:179)
[info]  at sbt.inc.NamesChange.<init>(Changes.scala:30)
[info]  at sbt.inc.IncrementalNameHashing.sameAPI(IncrementalNameHashing.scala:33)
[info]  at sbt.inc.IncrementalCommon.sameClass(IncrementalCommon.scala:146)
[info]  at sbt.inc.IncrementalCommon$$anonfun$6.apply(IncrementalCommon.scala:129)
[info]  at sbt.inc.IncrementalCommon$$anonfun$6.apply(IncrementalCommon.scala:129)

I've run into this problem while debugging class-based dependency branch.

The unenforced contract can be seen here:
https://github.com/sbt/sbt/blob/0.13/compile/inc/src/main/scala/sbt/inc/IncrementalNameHashing.scala#L23

  override protected def sameAPI[T](src: T, a: Source, b: Source): Option[APIChange[T]] = {
    if (SameAPI(a, b))
      None
    else {
      val aNameHashes = a._internalOnly_nameHashes
      val bNameHashes = b._internalOnly_nameHashes
      val modifiedNames = ModifiedNames.compareTwoNameHashes(aNameHashes, bNameHashes)
      val apiChange = NamesChange(src, modifiedNames)
      Some(apiChange)
    }
  }

The code assumes that if the api is not the same it's because some names has changed.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions