-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Only rename method type params shadowing enclosing class type params #24684
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
base: main
Are you sure you want to change the base?
Conversation
Previously, method type parameters were always renamed if they had the same name as any class type parameter, regardless of whether the class type parameter was actually used in the method signature. As a result, we rename methods's type params for unnecessary names like: scala#24671 (Note that renaming actually doesn't cause binary incompatibility, and this change is just for make generic signature a bit clear and silence false-positive MIMA reporting). For example, in an example below, the Scala compiler rename the generic signature of `bar` to something like `bar[T1](x: T1): T1` because `T` is used by `Foo[T]`. However, this is unnessary rename because none of T in method signature refer the `T` of `Foo[T]`. ```scala class Foo[T]: def bar[T](x: T): T = ??? ``` This commit makes the renaming conditional: Method type parameters are only renamed when - (1) A class type parameter is referenced in the method signature, and - (2) That class type parameter is shadowed by a method type parameter
d190111 to
a93c849
Compare
| // copy$default$1 still have `<T1> T Bar.copy$default$1` rather than `<T> T Bar.copy$default$1` | ||
| // as reported in https://github.com/scala/scala3/issues/24671 | ||
| // The type parameter rename occurs because the return type T refers the enclosing class's type param T. | ||
| // printMethodSig(barMethods, "copy$default$1") |
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.
Still MiMa won't be happy with <T1> T Bar.copy$default$1 rather than <T> T Bar.copy$default$1 (but the original signature was wrong, the return type T is shadowed by method type parameter.)
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.
Ah! I didn't pay attention. A small reproducer is
abstract class C[T] {
def x: T
def m[T] = x
}We have that wrong in Scala 2, I created scala/bug#13144.
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.
Thanks! that's a good reproducer :)
It seems like renaming to <T1:Ljava/lang/Object;>()TT; is right thing to do 👍
lrytz
left a comment
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.
false-positive MIMA reporting
nitpicking.. it's not really a false positive report by mima, but mima can be configured to check changes in the classfile (generic signatures) that go beyond binary compatibility.
| // copy$default$1 still have `<T1> T Bar.copy$default$1` rather than `<T> T Bar.copy$default$1` | ||
| // as reported in https://github.com/scala/scala3/issues/24671 | ||
| // The type parameter rename occurs because the return type T refers the enclosing class's type param T. | ||
| // printMethodSig(barMethods, "copy$default$1") |
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.
Ah! I didn't pay attention. A small reproducer is
abstract class C[T] {
def x: T
def m[T] = x
}We have that wrong in Scala 2, I created scala/bug#13144.
| val (tparams, vparams, rte) = collectMethodParams(mtd) | ||
| if (toplevel && !sym0.isConstructor) polyParamSig(tparams) | ||
| if (toplevel && !sym0.isConstructor) { | ||
| if (sym0.is(Method)) { |
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.
It seems name shadowing can also happen for nested classes, though that's a lot more exotic
abstract class C[T] {
val x: T
class I[T <: x.type]
}the class C$I has signautre <T:TT;>Ljava/lang/Object;, which I guess is also wrong? Either way, I think we don't need to fix this in this PR.
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.
Right. In that case, the renaming strategy for methods in nested classes will be more complicated. I won't fix it in this PR. Instead, I gonna rebase #24621 on top of this change and address it there.
| val usedMethodTypeParamNames = collection.mutable.Set.empty[Name] | ||
| val usedClassTypeParams = collection.mutable.Set.empty[Symbol] | ||
|
|
||
| def collect(tp: Type): Unit = tp.dealias match |
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.
In Scala 2 we have a tp.foreach, isn't there something similar? https://github.com/scala/scala/blob/41f6cfcd4b05298c59fc375c27b21eaea8e09653/src/reflect/scala/reflect/internal/Types.scala#L782
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.
Ah, we have
scala3/compiler/src/dotty/tools/dotc/core/Types.scala
Lines 7067 to 7071 in ecb1fd3
| abstract class TypeTraverser(using Context) extends TypeAccumulator[Unit] { | |
| def traverse(tp: Type): Unit | |
| def apply(x: Unit, tp: Type): Unit = traverse(tp) | |
| protected def traverseChildren(tp: Type): Unit = foldOver((), tp) | |
| } |
Previously, method type parameters were always renamed if they had the same name as any class type parameter, regardless of whether the class type parameter was actually used in the method signature.
As a result, we rename methods's type params for unnecessary names like: #24671 (Note that renaming actually doesn't cause binary incompatibility, and this change is just for make generic signature a bit clear and silence false-positive MIMA reporting).
For example, in an example below, the Scala compiler rename the generic signature of
barto something likebar[T1](x: T1): T1becauseTis used byFoo[T]. However, this is unnessary rename because none of T in method signature refer theTofFoo[T].This commit makes the renaming conditional:
Method type parameters are only renamed when