mirror of https://github.com/sbt/sbt.git
Always invalidate API when return type is a value class
Before this commit, we did not do the invalidation for methods with multiple parameter list, the comment above `hasValueClassAsReturnType` said: Note: We only inspect the "outermost type" (i.e. no recursion) because we don't need to inspect after erasure a function that would, for instance, return a function that returns a subtype of AnyVal. But this is wrong: a method with signature: def foo(a: A)(b: B): C is erased to: def foo(a: A, b: B): C and not, as the comment in the code suggest, to: def foo(a: A): B => C so we do need to inspect the final result type of methods, because they can be value classes that will be erased to their underlying value.
This commit is contained in:
parent
e64b01142b
commit
0993c1c7cc
|
|
@ -208,20 +208,14 @@ class ExtractAPI[GlobalType <: CallbackGlobal](val global: GlobalType,
|
||||||
s.asMethod.paramss.flatten map (_.info) exists (t => isAnyValSubtype(t.typeSymbol))
|
s.asMethod.paramss.flatten map (_.info) exists (t => isAnyValSubtype(t.typeSymbol))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: We only inspect the "outermost type" (i.e. no recursion) because we don't need to
|
def hasValueClassAsReturnType(tpe: Type): Boolean = tpe match {
|
||||||
// inspect after erasure a function that would, for instance, return a function that returns
|
case PolyType(_, base) => hasValueClassAsReturnType(base)
|
||||||
// a subtype of AnyVal.
|
case MethodType(_, resultType) => hasValueClassAsReturnType(resultType)
|
||||||
val hasValueClassAsReturnType: Boolean = {
|
case Nullary(resultType) => hasValueClassAsReturnType(resultType)
|
||||||
val tpe = viewer(in).memberInfo(s)
|
case resultType => isAnyValSubtype(resultType.typeSymbol)
|
||||||
tpe match {
|
|
||||||
case PolyType(_, base) => isAnyValSubtype(base.typeSymbol)
|
|
||||||
case MethodType(_, resultType) => isAnyValSubtype(resultType.typeSymbol)
|
|
||||||
case Nullary(resultType) => isAnyValSubtype(resultType.typeSymbol)
|
|
||||||
case resultType => isAnyValSubtype(resultType.typeSymbol)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val inspectPostErasure = hasValueClassAsParameter || hasValueClassAsReturnType
|
val inspectPostErasure = hasValueClassAsParameter || hasValueClassAsReturnType(viewer(in).memberInfo(s))
|
||||||
|
|
||||||
def build(t: Type, typeParams: Array[xsbti.api.TypeParameter], valueParameters: List[xsbti.api.ParameterList]): List[xsbti.api.Def] =
|
def build(t: Type, typeParams: Array[xsbti.api.TypeParameter], valueParameters: List[xsbti.api.ParameterList]): List[xsbti.api.Def] =
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
class B {
|
||||||
|
def bar(dummy: String)(dummy2: String): A = new A(0)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
object C extends App {
|
||||||
|
println(new B().bar("")("").x)
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
## Case 1: value class as parameter of method
|
||||||
$ copy-file changes/A0.scala src/main/scala/A.scala
|
$ copy-file changes/A0.scala src/main/scala/A.scala
|
||||||
$ copy-file changes/B0.scala src/main/scala/B.scala
|
$ copy-file changes/B0.scala src/main/scala/B.scala
|
||||||
$ copy-file changes/C0.scala src/main/scala/C.scala
|
$ copy-file changes/C0.scala src/main/scala/C.scala
|
||||||
|
|
@ -13,6 +14,8 @@ $ copy-file changes/A1.scala src/main/scala/A.scala
|
||||||
# This means that we have invalidated C.scala, as expected!
|
# This means that we have invalidated C.scala, as expected!
|
||||||
-> compile
|
-> compile
|
||||||
|
|
||||||
|
|
||||||
|
## Case 2: value class as return type of method with no parameter lists
|
||||||
$ copy-file changes/A0.scala src/main/scala/A.scala
|
$ copy-file changes/A0.scala src/main/scala/A.scala
|
||||||
$ copy-file changes/B1.scala src/main/scala/B.scala
|
$ copy-file changes/B1.scala src/main/scala/B.scala
|
||||||
$ copy-file changes/C1.scala src/main/scala/C.scala
|
$ copy-file changes/C1.scala src/main/scala/C.scala
|
||||||
|
|
@ -28,3 +31,20 @@ $ copy-file changes/A1.scala src/main/scala/A.scala
|
||||||
# because A is now a value class.
|
# because A is now a value class.
|
||||||
> run
|
> run
|
||||||
|
|
||||||
|
|
||||||
|
## Case 3: value class as return type of method with multiple parameter lists
|
||||||
|
$ copy-file changes/A0.scala src/main/scala/A.scala
|
||||||
|
$ copy-file changes/B2.scala src/main/scala/B.scala
|
||||||
|
$ copy-file changes/C2.scala src/main/scala/C.scala
|
||||||
|
|
||||||
|
# A is a normal class. B.bar takes two dummy arguments and returns an instance of A. C calls B.bar("")("").
|
||||||
|
> compile
|
||||||
|
> run
|
||||||
|
|
||||||
|
# Make A a value class.
|
||||||
|
$ copy-file changes/A1.scala src/main/scala/A.scala
|
||||||
|
|
||||||
|
# The code compiles. It will run iff C is recompiled because the signature of B.bar has changed,
|
||||||
|
# because A is now a value class.
|
||||||
|
> run
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue