Skip to content

Fix #8349: Generic signatures of generic arrays #8361

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

Merged
merged 1 commit into from
Mar 3, 2020
Merged
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
77 changes: 10 additions & 67 deletions compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import core.Flags._
import core.Names.{DerivedName, Name, SimpleName, TypeName}
import core.Symbols._
import core.TypeApplications.TypeParamInfo
import core.TypeErasure.erasure
import core.TypeErasure.{erasure, isUnboundedGeneric}
import core.Types._
import core.classfile.ClassfileConstants
import ast.Trees._
Expand Down Expand Up @@ -182,15 +182,16 @@ object GenericSignatures {
case ref @ TypeParamRef(_: PolyType, _) =>
typeParamSig(ref.paramName.lastPart)

case defn.ArrayOf(elemtp) =>
if (isUnboundedGeneric(elemtp))
jsig(defn.ObjectType)
else {
builder.append(ClassfileConstants.ARRAY_TAG)
jsig(elemtp)
}

case RefOrAppliedType(sym, pre, args) =>
// If args isEmpty, Array is being used as a type constructor
if (sym == defn.ArrayClass && args.nonEmpty)
if (unboundedGenericArrayLevel(tp) == 1) jsig(defn.ObjectType)
else {
builder.append(ClassfileConstants.ARRAY_TAG)
args.foreach(jsig(_))
}
else if (sym == defn.PairClass && tp.tupleArity > Definitions.MaxTupleArity)
if (sym == defn.PairClass && tp.tupleArity > Definitions.MaxTupleArity)
jsig(defn.TupleXXLClass.typeRef)
else if (isTypeParameterInSig(sym, sym0)) {
assert(!sym.isAliasType, "Unexpected alias type: " + sym)
Expand Down Expand Up @@ -368,17 +369,6 @@ object GenericSignatures {
case tp => tp :: Nil
}

/** Arrays despite their finality may turn up as refined type parents,
* e.g. with "tagged types" like Array[Int] with T.
*/
private def unboundedGenericArrayLevel(tp: Type)(implicit ctx: Context): Int = tp match {
case GenericArray(core, level) if !(core <:< defn.AnyRefType) =>
level
case AndType(tp1, tp2) =>
unboundedGenericArrayLevel(tp1) max unboundedGenericArrayLevel(tp2)
case _ =>
0
}

// only refer to type params that will actually make it into the sig, this excludes:
// * higher-order type parameters
Expand Down Expand Up @@ -420,53 +410,6 @@ object GenericSignatures {
if (owner.is(PackageClass) || owner.isTerm) pre else cls.owner.info /* .tpe_* */
}

object GenericArray {

/** Is `tp` an unbounded generic type (i.e. which could be instantiated
* with primitive as well as class types)?.
*/
private def genericCore(tp: Type)(implicit ctx: Context): Type = tp.widenDealias match {
/* A Java Array<T> is erased to Array[Object] (T can only be a reference type), where as a Scala Array[T] is
* erased to Object. However, there is only symbol for the Array class. So to make the distinction between
* a Java and a Scala array, we check if the owner of T comes from a Java class.
* This however caused issue scala/bug#5654. The additional test for EXISTENTIAL fixes it, see the ticket comments.
* In short, members of an existential type (e.g. `T` in `forSome { type T }`) can have pretty arbitrary
* owners (e.g. when computing lubs, <root> is used). All packageClass symbols have `isJavaDefined == true`.
*/
case RefOrAppliedType(sym, tp, _) =>
if (sym.isAbstractOrParamType && (!sym.owner.is(JavaDefined) || sym.is(Scala2Existential)))
tp
else
NoType

case bounds: TypeBounds =>
bounds

case _ =>
NoType
}

/** If `tp` is of the form Array[...Array[T]...] where `T` is an abstract type
* then Some((N, T)) where N is the number of Array constructors enclosing `T`,
* otherwise None. Existentials on any level are ignored.
*/
def unapply(tp: Type)(implicit ctx: Context): Option[(Type, Int)] = tp.widenDealias match {
case defn.ArrayOf(arg) =>
genericCore(arg) match {
case NoType =>
arg match {
case GenericArray(core, level) => Some((core, level + 1))
case _ => None
}
case core =>
Some((core, 1))
}
case _ =>
None
}
}


private object RefOrAppliedType {
def unapply(tp: Type)(implicit ctx: Context): Option[(Symbol, Type, List[Type])] = tp match {
case TypeParamRef(_, _) =>
Expand Down
5 changes: 4 additions & 1 deletion tests/generic-java-signatures/arrayBound.check
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@ T <: java.lang.Object
U <: T[]
V <: java.util.List<T[]>
W <: java.util.List<? extends java.util.Date>
X <: java.util.HashMap<java.lang.Object, java.util.ArrayList<? extends java.util.Date>>
X <: java.util.HashMap<java.lang.Object, java.util.ArrayList<? extends java.util.Date>>
T1 <: java.lang.Object
U1 <: java.lang.Object
V1 <: java.lang.Object[]
15 changes: 12 additions & 3 deletions tests/generic-java-signatures/arrayBound.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
class Foo[T <: Array[_], U <: Array[T], V <: java.util.List[Array[T]], W <: java.util.List[_ <: java.util.Date], X <: java.util.HashMap[Array[_], java.util.ArrayList[_ <: java.util.Date]]]
class Foo[
T <: Array[_],
U <: Array[T],
V <: java.util.List[Array[T]],
W <: java.util.List[_ <: java.util.Date],
X <: java.util.HashMap[Array[_], java.util.ArrayList[_ <: java.util.Date]],
T1,
U1 <: Array[T1],
V1 <: Array[Array[T1]]
]
object Test {
def main(args: Array[String]): Unit = {
val tParams = classOf[Foo[_, _, _, _, _]].getTypeParameters()
val tParams = classOf[Foo[_, _, _, _, _, _, _, _]].getTypeParameters()
tParams.foreach { tp =>
println(tp.getName + " <: " + tp.getBounds.map(_.getTypeName).mkString(", "))
}
}
}
}
2 changes: 1 addition & 1 deletion tests/generic-java-signatures/primitives.check
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ E <: int[]
F <: long[]
G <: short[]
H <: boolean[]
I <: java.lang.Object
I <: byte[]