Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala
Original file line number Diff line number Diff line change
Expand Up @@ -486,19 +486,40 @@ object GenericSignatures {
}

private object RefOrAppliedType {
def unapply(tp: Type)(using Context): Option[(Symbol, Type, List[Type])] = tp match {
// In the special case where we see a type parameter applied to type parameters,
// such as `K[X, Y]` given `[X, Y, K <: Iterable[(X, Y)]]`, we must find its bound
// and instantiate it, otherwise in our example we end up with `Iterable[X, Y]` which is nonsensical.
private def resolveAppliedType(a: AppliedType)(using Context): Option[Type] =
a.tycon match
case TypeParamRef(binder, paramNum) =>
binder.paramInfos(paramNum).hi match
case hkt @ HKTypeLambda(_, _) =>
val instantiated = hkt.instantiate(a.args).dealias
// However, since Java doesn't have a way to refer to HKTs in generic signatures,
// we must trade precision for termination by only resolving one level,
// otherwise we end up in infinite loops, e.g., in `X[A] <: Thing[X[A]]` we keep resolving `X -> Thing[X[A]]`.
val hasNested = instantiated.existsPart:
case AppliedType(TypeParamRef(_, _), nestedArgs) => nestedArgs.exists(a.args.contains)
case _ => false
if hasNested then None
else Some(instantiated)
case _ => None
case _ => None

def unapply(tp: Type)(using Context): Option[(Symbol, Type, List[Type])] = tp match
case TypeParamRef(_, _) =>
Some((tp.typeSymbol, tp, Nil))
case TermParamRef(_, _) =>
Some((tp.termSymbol, tp, Nil))
case TypeRef(pre, _) if !tp.typeSymbol.isAliasType =>
val sym = tp.typeSymbol
Some((sym, pre, Nil))
case AppliedType(pre, args) =>
Some((pre.typeSymbol, pre, args))
case a @ AppliedType(pre, args) =>
resolveAppliedType(a) match
case Some(resolved) => unapply(resolved)
case _ => Some((pre.typeSymbol, pre, args))
case _ =>
None
}
}

private def needsJavaSig(tp: Type, throwsArgs: List[Type])(using Context): Boolean = !ctx.settings.XnoGenericSig.value && {
Expand Down
1 change: 1 addition & 0 deletions tests/run/i15903.check
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
scala.collection.Iterable<scala.Tuple2<K, A>>
10 changes: 10 additions & 0 deletions tests/run/i15903.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// scalajs: --skip
// (JVM-only, generic signatures)

trait Working:
type M[X,Y] = Iterable[(X, Y)]
def example[K, A, T[X,Y] <: M[X,Y]](ab: String): T[K,A] = ???

object Test:
def main(args: Array[String]): Unit =
classOf[Working].getMethods.filter(_.getName == "example").map(_.getGenericReturnType).foreach(println)
1 change: 1 addition & 0 deletions tests/run/i15903b.check
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
scala.collection.Iterable<scala.Tuple2<K, A>>
9 changes: 9 additions & 0 deletions tests/run/i15903b.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// scalajs: --skip
// (JVM-only, generic signatures)

trait NotWorking:
def example[K, A, T[X,Y] <: Iterable[(X, Y)]](ab: String): T[K,A] = ???

object Test:
def main(args: Array[String]): Unit =
classOf[NotWorking].getMethods.filter(_.getName == "example").map(_.getGenericReturnType).foreach(println)
1 change: 1 addition & 0 deletions tests/run/i15903c.check
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
scala.collection.Iterable<scala.Tuple2<A, K>>
10 changes: 10 additions & 0 deletions tests/run/i15903c.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// scalajs: --skip
// (JVM-only, generic signatures)

trait NotWorking:
// return type args reversed compared to test case (b)
def example[K, A, T[X,Y] <: Iterable[(X, Y)]](ab: String): T[A,K] = ???

object Test:
def main(args: Array[String]): Unit =
classOf[NotWorking].getMethods.filter(_.getName == "example").map(_.getGenericReturnType).foreach(println)
Loading