-
Notifications
You must be signed in to change notification settings - Fork 937
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
Include ONLY nonpublic vars in API hash of traits #2441
Conversation
It's not clear what the distinction between compiler-project and source-dependencies scripted tests is so let's stick to the one that has the biggest number of tests for incremental compiler.
The previous logic was wrong and included all members of traits in their API hash, which degraded the performance of the incremental compiler. This commit changes this logic so that only the non-public members of traits are included into their API hash. Fixes sbt#2436
Can one of the admins verify this patch? |
@gkossakowski Could you review this? |
What happens in situation like this: // A.scala
trait A {
private type Foo = Int // (1) change Int to String
private def foo: Int = 12 // (2) change Int to String and constant to a string constant
}
// B.scala
class B extends A Will (1) or (2) trigger recompilation of |
@gkossakowski In both cases, |
@gkossakowski I've added a new commit that fixes the problems you exposed and behaves correctly on the following scenario: // A.scala
trait A {
private var a: Int = 0 // change to String, B gets recompiled and runs fine
} // B.scala
object B extends A {
def main(args: Array[String]): Unit = println("Hello")
} |
I think you need to cover both private vars and vals. For the val, you have to generate a backing field. Try out changing a var to val. |
You're right, it still crashes if we use vals. |
It would be interesting to know how much of this is still valid in Scala 2.12 with the new trait encoding. |
Let's cover vals by the existing test. |
What do you mean? |
@Duhemm I think he's asking you to test this in a scripted test by adding more changes to https://github.com/sbt/sbt/tree/0.13/sbt/src/sbt-test/source-dependencies/trait-private-var |
Private vars and private vals defined in a trait impact the bytecode generated for the implementors of these traits. The scripted test `source-dependencies/trait-private-var` only accounted for private vars. It now tries the same scenario both with private vars and private vals.
I updated the scripted test |
Ok, so let's systematically go over the list of different private members we can have:
I checked the ones we discussed so far. What about the rest? Should the hash code of a trait include private hashes of any remaining members from the list? |
|
Private objects must be included as well in the API hash: // A.scala
trait A {
val foo = 0 // + X.a Run once and then uncomment these two lines
// private object X { val a = 1 }
} // B.scala
object B extends A {
def main(args: Array[String]): Unit = {
println("Hello " + foo)
}
}
We get exactly the same result by replacing the private object by a private lazy val |
Yes, objects are resembling closely lazy vals at their encoding. The type checking rules are different (objects introduce singleton types) but here we are mostly worried about what's going on at the byte code level. |
Assuming this is a 0.13.10-RC show stopper, and we need to 0.13.10-RC3, how much more time do you guys need to settle this? Could we fix the most common cases for now, and accept some perf regression? |
@eed3si9n To fix @gkossakowski If I try with something coming from outside of the trait, then this something will be part of the API of the trait and once modified the clients of the trait will be recompiled. I guess I misunderstood what you meant? I have been trying to add private inner traits and classes and looking at the resulting byte code in the implementing classes, but I do not see any mention of these classes in them. What I have now is: def isTraitBreaker(d: Definition): Boolean = {
d match {
case fl: FieldLike => true
case cl: ClassLike => cl.definitionType == DefinitionType.Module
case d: Def => d.name contains "$"
case _ => false
}
} |
That feels hacky, but I don't have any good alternative. |
I think you want to check if a method symbol has the |
LGTM |
LGTM Good work! |
And thanks for the recap. |
Shall commits be squashed a little bit? |
I'm not too fussy about the git history. I'll just merge this as is |
Include ONLY nonpublic vars in API hash of traits
case _ => false | ||
} | ||
val includedPrivateDefinitions = | ||
if (!includePrivate && !topLevel && isTrait) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Duhemm why is !topLevel
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gkossakowski Because if topLevel
is true, then we have defs == ds
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I don't understand the original logic. Why do we care about whether definitions are enclosed by a top level class or not? Or is it about top level classes themselves?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding is that topLevel = true
for definitions that are only enclosed in a package.
This is still work in progress as more tests need to be added. Note that this is still WIP and the implementation may change in the next days. This pull request builds upon @Duhemm's work on sbt/sbt#2441, but changes slightly the way it's solved. It instead adds a specific api change that only invalidates changes via inheritance (both global and local) and doesn't invalidate member references, which gives us more correctness and precision. As explained in sbt/sbt#2490, the previous fix violated some of the invariants of the incremental algorithm. As such, we introduce the notion of an extra api hash which is independent of the normal one. This extra hash, in the case of traits, is computed from the private definitions found, and otherwise it's the same as the normal hash. As a result, we can tell apart the cases where there are changes in the normal public API hash and the extra one. I've called it extra instead of `trait private hash` or something similar because it can accommodate other fixes in the future and we cannot afford adding a new hash per `AnalyzedClass` every time we need to have `HashAPI` be special cased for a particular encoding. Fixes: 1. `source-dependencies/trait-private-val` 1. `source-dependencies/trait-private-var` 1. `source-dependencies/trait-super`
This pull request builds upon @Duhemm's work on sbt/sbt#2441, but changes slightly the way it's solved. It instead adds a specific api change that only invalidates changes via inheritance (both global and local) and doesn't invalidate member references, which gives us more correctness and precision. As explained in sbt/sbt#2490, the previous fix violated some of the invariants of the incremental algorithm. As such, we introduce the notion of an extra api hash which is independent of the normal one. This extra hash, in the case of traits, is computed from the private definitions found, and otherwise it's the same as the normal hash. As a result, we can tell apart the cases where there are changes in the normal public API hash and the extra one. I've called it extra instead of `trait private hash` or something similar because it can accommodate other fixes in the future and we cannot afford adding a new hash per `AnalyzedClass` every time we need to have `HashAPI` be special cased for a particular encoding. Fixes: 1. `source-dependencies/trait-private-val` 1. `source-dependencies/trait-private-var` 1. `source-dependencies/trait-private-object` 1. `source-dependencies/trait-super` And it also adds another two tests to complement `source-dependencies/trait-member-modified` in checking that member references to private trait definitions do not trigger unnecessary compilations and that local inheritance does trigger an extra compile. This is still *work in progress* as more tests need to be added. I'm open to changing some details of the implementation since I'm not still 100% sure that the `apiExtraHash` route is the best choice. Maybe we should go for something more explicit in name?
This pull request builds upon @Duhemm's work on sbt/sbt#2441, but changes slightly the way it's solved. It instead adds a specific api change that only invalidates changes via inheritance (both global and local) and doesn't invalidate member references, which gives us more correctness and precision. As explained in sbt/sbt#2490, the previous fix violated some of the invariants of the incremental algorithm. As such, we introduce the notion of an extra api hash which is independent of the normal one. This extra hash, in the case of traits, is computed from the private definitions found, and otherwise it's the same as the normal hash. As a result, we can tell apart the cases where there are changes in the normal public API hash and the extra one. I've called it extra instead of `trait private hash` or something similar because it can accommodate other fixes in the future and we cannot afford adding a new hash per `AnalyzedClass` every time we need to have `HashAPI` be special cased for a particular encoding. Fixes: 1. `source-dependencies/trait-private-val` 1. `source-dependencies/trait-private-var` 1. `source-dependencies/trait-private-object` 1. `source-dependencies/trait-super` And it also adds another two tests to complement `source-dependencies/trait-member-modified` in checking that member references to private trait definitions do not trigger unnecessary compilations and that local inheritance does trigger an extra compile. This is still *work in progress* as more tests need to be added. I'm open to changing some details of the implementation since I'm not still 100% sure that the `apiExtraHash` route is the best choice. Maybe we should go for something more explicit in name?
This pull request builds upon @Duhemm's work on sbt/sbt#2441, but changes slightly the way it's solved. It instead adds a specific api change that only invalidates changes via inheritance (both global and local) and doesn't invalidate member references, which gives us more correctness and precision. As explained in sbt/sbt#2490, the previous fix violated some of the invariants of the incremental algorithm. As such, we introduce the notion of an extra api hash which is independent of the normal one. This extra hash, in the case of traits, is computed from the private definitions found, and otherwise it's the same as the normal hash. As a result, we can tell apart the cases where there are changes in the normal public API hash and the extra one. I've called it extra instead of `trait private hash` or something similar because it can accommodate other fixes in the future and we cannot afford adding a new hash per `AnalyzedClass` every time we need to have `HashAPI` be special cased for a particular encoding. Fixes: 1. `source-dependencies/trait-private-val` 1. `source-dependencies/trait-private-var` 1. `source-dependencies/trait-private-object` 1. `source-dependencies/trait-super` And it also adds another two tests to complement `source-dependencies/trait-member-modified` in checking that member references to private trait definitions do not trigger unnecessary compilations and that local inheritance does trigger an extra compile. This is still *work in progress* as more tests need to be added. I'm open to changing some details of the implementation since I'm not still 100% sure that the `apiExtraHash` route is the best choice. Maybe we should go for something more explicit in name?
This pull request builds upon @Duhemm's work on sbt/sbt#2441, but changes slightly the way it's solved. It instead adds a specific api change that only invalidates changes via inheritance (both global and local) and doesn't invalidate member references, which gives us more correctness and precision. As explained in sbt/sbt#2490, the previous fix violated some of the invariants of the incremental algorithm. As such, we introduce the notion of an extra hash which is independent of the normal one. This extra hash, in the case of traits, is computed from the private definitions found, and otherwise its value is the empty hash (-1). As a result, we can tell apart the cases where there are changes in the normal public API hash and the extra one. I've called it extra instead of `trait private hash` or something similar because it can accommodate other fixes in the future and we cannot afford adding a new hash per `AnalyzedClass` every time we need to have `HashAPI` be special cased for a particular encoding. Right now, when the extra hash is different and the classes involved in the hash computations are traits we treat it as a breakage in the trait encoding and recompile appropriately. If the classes involved are not hash computations, we fail because no more cases have been added yet. Fixes: 1. `source-dependencies/trait-private-val` 1. `source-dependencies/trait-private-var` 1. `source-dependencies/trait-private-object` 1. `source-dependencies/trait-super` And it also adds another two tests to complement `source-dependencies/trait-member-modified` in checking that member references to private trait definitions do not trigger unnecessary compilations and that local inheritance does trigger an extra compile. Note that the code in `SameAPI` is rotten and will need to go at some point in the future. It's outdated and the only one-liners used in the incremental compiler should be inlined, and the rest of the code ditched. Right now, it tries to emulate the semantics of `HashAPI` but it hasn't been up-to-date ever since we released Zinc 1.0. WIp tests asdf
This pull request builds upon @Duhemm's work on sbt/sbt#2441, but changes slightly the way it's solved. It instead adds a specific api change that only invalidates changes via inheritance (both global and local) and doesn't invalidate member references, which gives us more correctness and precision. As explained in sbt/sbt#2490, the previous fix violated some of the invariants of the incremental algorithm. As such, we introduce the notion of an extra hash which is independent of the normal one. This extra hash, in the case of traits, is computed from the private definitions found, and otherwise its value is the empty hash (-1). As a result, we can tell apart the cases where there are changes in the normal public API hash and the extra one. I've called it extra instead of `trait private hash` or something similar because it can accommodate other fixes in the future and we cannot afford adding a new hash per `AnalyzedClass` every time we need to have `HashAPI` be special cased for a particular encoding. Right now, when the extra hash is different and the classes involved in the hash computations are traits we treat it as a breakage in the trait encoding and recompile appropriately. If the classes involved are not hash computations, we fail because no more cases have been added yet. Fixes: 1. `source-dependencies/trait-private-val` 1. `source-dependencies/trait-private-var` 1. `source-dependencies/trait-private-object` 1. `source-dependencies/trait-super` And it also adds another two tests to complement `source-dependencies/trait-member-modified` in checking that member references to private trait definitions do not trigger unnecessary compilations and that local inheritance does trigger an extra compile. Note that the code in `SameAPI` is rotten and will need to go at some point in the future. It's outdated and the only one-liners used in the incremental compiler should be inlined, and the rest of the code ditched. Right now, it tries to emulate the semantics of `HashAPI` but it hasn't been up-to-date ever since we released Zinc 1.0. WIp tests asdf
This pull request builds upon @Duhemm's work on sbt/sbt#2441, but changes slightly the way it's solved. It instead adds a specific api change that only invalidates changes via inheritance (both global and local) and doesn't invalidate member references, which gives us more correctness and precision. As explained in sbt/sbt#2490, the previous fix violated some of the invariants of the incremental algorithm. As such, we introduce the notion of an extra hash which is independent of the normal one. This extra hash, in the case of traits, is computed from the private definitions found, and otherwise its value is the empty hash (-1). As a result, we can tell apart the cases where there are changes in the normal public API hash and the extra one. I've called it extra instead of `trait private hash` or something similar because it can accommodate other fixes in the future and we cannot afford adding a new hash per `AnalyzedClass` every time we need to have `HashAPI` be special cased for a particular encoding. Right now, when the extra hash is different and the classes involved in the hash computations are traits we treat it as a breakage in the trait encoding and recompile appropriately. If the classes involved are not hash computations, we fail because no more cases have been added yet. Fixes: 1. `source-dependencies/trait-private-val` 1. `source-dependencies/trait-private-var` 1. `source-dependencies/trait-private-object` 1. `source-dependencies/trait-super` And it also adds another two tests to complement `source-dependencies/trait-member-modified` in checking that member references to private trait definitions do not trigger unnecessary compilations and that local inheritance does trigger an extra compile. Note that the code in `SameAPI` is rotten and will need to go at some point in the future. It's outdated and the only one-liners used in the incremental compiler should be inlined, and the rest of the code ditched. Right now, it tries to emulate the semantics of `HashAPI` but it hasn't been up-to-date ever since we released Zinc 1.0. WIp tests asdf
This pull request builds upon @Duhemm's work on sbt/sbt#2441, but changes slightly the way it's solved. It instead adds a specific api change that only invalidates changes via inheritance (both global and local) and doesn't invalidate member references, which gives us more correctness and precision. As explained in sbt/sbt#2490, the previous fix violated some of the invariants of the incremental algorithm. As such, we introduce the notion of an extra hash which is independent of the normal one. This extra hash, in the case of traits, is computed from the private definitions found, and otherwise its value defaults to the public api hash (which represents the emptiness). As a result, we can tell apart the cases where there are changes in the normal public API hash and the extra one. I've called it extra instead of `trait private hash` or something similar because it can accommodate other fixes in the future and we cannot afford adding a new hash per `AnalyzedClass` every time we need to have `HashAPI` be special cased for a particular encoding. Right now, when the extra hash is different and the classes involved in the hash computations are traits we treat it as a breakage in the trait encoding and recompile appropriately. If the classes involved are not hash computations, we fail because no more cases have been added yet. Fixes: 1. `source-dependencies/trait-private-val` 1. `source-dependencies/trait-private-var` 1. `source-dependencies/trait-private-object` 1. `source-dependencies/trait-super` And it also adds another two tests to complement `source-dependencies/trait-member-modified` in checking that member references to private trait definitions do not trigger unnecessary compilations and that local inheritance does trigger an extra compile. Note that the code in `SameAPI` is rotten and will need to go at some point in the future. It's outdated and the only one-liners used in the incremental compiler should be inlined, and the rest of the code ditched. Right now, it tries to emulate the semantics of `HashAPI` but it hasn't been up-to-date ever since we released Zinc 1.0.
This pull request builds upon @Duhemm's work on sbt/sbt#2441, but changes slightly the way it's solved. It instead adds a specific api change that only invalidates changes via inheritance (both global and local) and doesn't invalidate member references, which gives us more correctness and precision. As explained in sbt/sbt#2490, the previous fix violated some of the invariants of the incremental algorithm. As such, we introduce the notion of an extra hash which is independent of the normal one. This extra hash, in the case of traits, is computed from the private definitions found, and otherwise its value defaults to the public api hash (which represents the emptiness). As a result, we can tell apart the cases where there are changes in the normal public API hash and the extra one. I've called it extra instead of `trait private hash` or something similar because it can accommodate other fixes in the future and we cannot afford adding a new hash per `AnalyzedClass` every time we need to have `HashAPI` be special cased for a particular encoding. Right now, when the extra hash is different and the classes involved in the hash computations are traits we treat it as a breakage in the trait encoding and recompile appropriately. If the classes involved are not hash computations, we fail because no more cases have been added yet. Fixes: 1. `source-dependencies/trait-private-val` 1. `source-dependencies/trait-private-var` 1. `source-dependencies/trait-private-object` 1. `source-dependencies/trait-super` And it also adds another two tests to complement `source-dependencies/trait-member-modified` in checking that member references to private trait definitions do not trigger unnecessary compilations and that local inheritance does trigger an extra compile. Note that the code in `SameAPI` is rotten and will need to go at some point in the future. It's outdated and the only one-liners used in the incremental compiler should be inlined, and the rest of the code ditched. Right now, it tries to emulate the semantics of `HashAPI` but it hasn't been up-to-date ever since we released Zinc 1.0.
This pull request builds upon @Duhemm's work on sbt/sbt#2441, but changes slightly the way it's solved. It instead adds a specific api change that only invalidates changes via inheritance (both global and local) and doesn't invalidate member references, which gives us more correctness and precision. As explained in sbt/sbt#2490, the previous fix violated some of the invariants of the incremental algorithm. As such, we introduce the notion of an extra hash which is independent of the normal one. This extra hash, in the case of traits, is computed from the private definitions found, and otherwise its value defaults to the public api hash (which represents the emptiness). As a result, we can tell apart the cases where there are changes in the normal public API hash and the extra one. I've called it extra instead of `trait private hash` or something similar because it can accommodate other fixes in the future and we cannot afford adding a new hash per `AnalyzedClass` every time we need to have `HashAPI` be special cased for a particular encoding. Right now, when the extra hash is different and the classes involved in the hash computations are traits we treat it as a breakage in the trait encoding and recompile appropriately. If the classes involved are not hash computations, we fail because no more cases have been added yet. Fixes: 1. `source-dependencies/trait-private-val` 1. `source-dependencies/trait-private-var` 1. `source-dependencies/trait-private-object` 1. `source-dependencies/trait-super` And it also adds another two tests to complement `source-dependencies/trait-member-modified` in checking that member references to private trait definitions do not trigger unnecessary compilations and that local inheritance does trigger an extra compile. Note that the code in `SameAPI` is rotten and will need to go at some point in the future. It's outdated and the only one-liners used in the incremental compiler should be inlined, and the rest of the code ditched. Right now, it tries to emulate the semantics of `HashAPI` but it hasn't been up-to-date ever since we released Zinc 1.0.
This pull request builds upon @Duhemm's work on sbt/sbt#2441, but changes slightly the way it's solved. It instead adds a specific api change that only invalidates changes via inheritance (both global and local) and doesn't invalidate member references, which gives us more correctness and precision. As explained in sbt/sbt#2490, the previous fix violated some of the invariants of the incremental algorithm. As such, we introduce the notion of an extra hash which is independent of the normal one. This extra hash, in the case of traits, is computed from the private definitions found, and otherwise its value defaults to the public api hash (which represents the emptiness). As a result, we can tell apart the cases where there are changes in the normal public API hash and the extra one. I've called it extra instead of `trait private hash` or something similar because it can accommodate other fixes in the future and we cannot afford adding a new hash per `AnalyzedClass` every time we need to have `HashAPI` be special cased for a particular encoding. Right now, when the extra hash is different and the classes involved in the hash computations are traits we treat it as a breakage in the trait encoding and recompile appropriately. If the classes involved are not hash computations, we fail because no more cases have been added yet. Fixes: 1. `source-dependencies/trait-private-val` 1. `source-dependencies/trait-private-var` 1. `source-dependencies/trait-private-object` 1. `source-dependencies/trait-super` And it also adds another two tests to complement `source-dependencies/trait-member-modified` in checking that member references to private trait definitions do not trigger unnecessary compilations and that local inheritance does trigger an extra compile. Note that the code in `SameAPI` is rotten and will need to go at some point in the future. It's outdated and the only one-liners used in the incremental compiler should be inlined, and the rest of the code ditched. Right now, it tries to emulate the semantics of `HashAPI` but it hasn't been up-to-date ever since we released Zinc 1.0.
The previous logic was wrong and included all members of traits in their
API hash, which degraded the performance of the incremental compiler.
This commit changes this logic so that only the non-public members of
traits are included into their API hash.
Fixes #2436