Skip to content
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

SI-8931 make generic signature consistent with interface list in classfiles #4080

Merged
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 1 addition & 20 deletions src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
Expand Up @@ -195,32 +195,13 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
case _ => NoSymbol
}

/**
* Drop redundant interfaces (which are implemented by some other parent) from the immediate
* parents. In other words, no two interfaces in the result are related by subtyping.
*/
def dropRedundantInterfaces(lstIfaces: List[Symbol]): List[Symbol] = {
var rest = lstIfaces
var leaves = List.empty[Symbol]
while (!rest.isEmpty) {
val candidate = rest.head
val nonLeaf = leaves exists { lsym => lsym isSubClass candidate }
if (!nonLeaf) {
leaves = candidate :: (leaves filterNot { lsym => candidate isSubClass lsym })
}
rest = rest.tail
}

leaves
}

val superInterfaces0: List[Symbol] = classSym.mixinClasses
val superInterfaces = existingSymbols(superInterfaces0 ++ classSym.annotations.map(newParentForAnnotation)).distinct

assert(!superInterfaces.contains(NoSymbol), s"found NoSymbol among: ${superInterfaces.mkString(", ")}")
assert(superInterfaces.forall(s => s.isInterface || s.isTrait), s"found non-interface among: ${superInterfaces.mkString(", ")}")

dropRedundantInterfaces(superInterfaces)
erasure.minimizeInterfaces(superInterfaces.map(_.info)).map(_.typeSymbol)
}

private def buildNestedInfo(innerClassSym: Symbol): Option[NestedInfo] = {
Expand Down
20 changes: 1 addition & 19 deletions src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
Expand Up @@ -1214,30 +1214,12 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self =>
case _ => NoSymbol
}

/* Drop redundant interfaces (ones which are implemented by some other parent) from the immediate parents.
* This is important on Android because there is otherwise an interface explosion.
*/
def minimizeInterfaces(lstIfaces: List[Symbol]): List[Symbol] = {
var rest = lstIfaces
var leaves = List.empty[Symbol]
while(!rest.isEmpty) {
val candidate = rest.head
val nonLeaf = leaves exists { lsym => lsym isSubClass candidate }
if(!nonLeaf) {
leaves = candidate :: (leaves filterNot { lsym => candidate isSubClass lsym })
}
rest = rest.tail
}

leaves
}

val ps = c.symbol.info.parents
val superInterfaces0: List[Symbol] = if(ps.isEmpty) Nil else c.symbol.mixinClasses
val superInterfaces = existingSymbols(superInterfaces0 ++ c.symbol.annotations.map(newParentForAttr)).distinct

if(superInterfaces.isEmpty) EMPTY_STRING_ARRAY
else mkArray(minimizeInterfaces(superInterfaces) map javaName)
else mkArray(erasure.minimizeInterfaces(superInterfaces.map(_.info)).map(t => javaName(t.typeSymbol)))
}

var clasz: IClass = _ // this var must be assigned only by genClass()
Expand Down
45 changes: 36 additions & 9 deletions src/compiler/scala/tools/nsc/transform/Erasure.scala
Expand Up @@ -185,23 +185,50 @@ abstract class Erasure extends AddInterfaces

private def isErasedValueType(tpe: Type) = tpe.isInstanceOf[ErasedValueType]

/* Drop redundant interfaces (ones which are implemented by some other parent) from the immediate parents.
* This is important on Android because there is otherwise an interface explosion.
*/
def minimizeInterfaces(lstIfaces: List[Type]): List[Type] = {
var rest = lstIfaces
var leaves = List.empty[Type]
while(!rest.isEmpty) {
val candidate = rest.head
val nonLeaf = leaves exists { t => t.typeSymbol isSubClass candidate.typeSymbol }
if(!nonLeaf) {
leaves = candidate :: (leaves filterNot { t => candidate.typeSymbol isSubClass t.typeSymbol })
}
rest = rest.tail
}

leaves.reverse
}


/** The Java signature of type 'info', for symbol sym. The symbol is used to give the right return
* type for constructors.
*/
def javaSig(sym0: Symbol, info: Type): Option[String] = enteringErasure {
val isTraitSignature = sym0.enclClass.isTrait

def superSig(parents: List[Type]) = {
val ps = (
if (isTraitSignature) {
def isInterfaceOrTrait(sym: Symbol) = sym.isInterface || sym.isTrait

// a signature should always start with a class
def ensureClassAsFirstParent(tps: List[Type]) = tps match {
case Nil => ObjectTpe :: Nil
case head :: tail if isInterfaceOrTrait(head.typeSymbol) => ObjectTpe :: tps
case _ => tps
}

val minParents = minimizeInterfaces(parents)
val validParents =
if (isTraitSignature)
// java is unthrilled about seeing interfaces inherit from classes
val ok = parents filter (p => p.typeSymbol.isTrait || p.typeSymbol.isInterface)
// traits should always list Object.
if (ok.isEmpty || ok.head.typeSymbol != ObjectClass) ObjectTpe :: ok
else ok
}
else parents
)
minParents filter (p => isInterfaceOrTrait(p.typeSymbol))
else minParents

val ps = ensureClassAsFirstParent(validParents)

(ps map boxedSig).mkString
}
def boxedSig(tp: Type) = jsig(tp, primitiveOK = false)
Expand Down
1 change: 1 addition & 0 deletions test/files/run/t8931.check
@@ -0,0 +1 @@
List(interface B)
15 changes: 15 additions & 0 deletions test/files/run/t8931.scala
@@ -0,0 +1,15 @@

trait A

trait B extends A

class C extends A with B

object Test extends App {
val c = classOf[C]

println(c.getGenericInterfaces.toList)

assert(c.getGenericInterfaces.length == c.getInterfaces.length,
s"mismatch between ${c.getGenericInterfaces} and ${c.getInterfaces}")
}