Skip to content

Commit

Permalink
Avoid needless work in the specialization info transform in the backend
Browse files Browse the repository at this point in the history
Any types that needed to be specialized to support callsites in the current
run would have already been info transformed during the specalization
tree transform of those call sites.

The backend requires further type information, e.g, to know about
inner/enclosing class relationships. This involves calls to `sym.info`
for classes on the classpath that haven't yet been info transformed.

During that process, all base classes of such types are also info
transformed. The specialization info transformer for classes then
looks at the members of the classes to add specialialized variants.

This is undesirable on grounds of performance and the risk of
encountering stub symbols (references to types absent from the
current compilation classpath) which can manifest as compiler crashes.
  • Loading branch information
retronym committed Mar 30, 2017
1 parent 5cd3442 commit 8ae0fda
Showing 1 changed file with 32 additions and 14 deletions.
46 changes: 32 additions & 14 deletions src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
Expand Up @@ -198,6 +198,13 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
override def newPhase(prev: scala.tools.nsc.Phase): StdPhase = new SpecializationPhase(prev)
class SpecializationPhase(prev: scala.tools.nsc.Phase) extends super.Phase(prev) {
override def checkable = false
override def run(): Unit = {
super.run()
exitingSpecialize {
FunctionClass.seq.map(_.info)
TupleClass.seq.map(_.info)
}
}
}

protected def newTransformer(unit: CompilationUnit): Transformer =
Expand Down Expand Up @@ -1199,22 +1206,33 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
* If it is a 'no-specialization' run, it is applied only to loaded symbols.
*/
override def transformInfo(sym: Symbol, tpe: Type): Type = {
if (settings.nospecialization && currentRun.compiles(sym)) tpe
else tpe.resultType match {
if (settings.nospecialization && currentRun.compiles(sym)) {
tpe
} else tpe.resultType match {
case cinfo @ ClassInfoType(parents, decls, clazz) if !unspecializableClass(cinfo) =>
val tparams = tpe.typeParams
if (tparams.isEmpty)
exitingSpecialize(parents map (_.typeSymbol.info))

val parents1 = parents mapConserve specializedType
if (parents ne parents1) {
debuglog("specialization transforms %s%s parents to %s".format(
if (tparams.nonEmpty) "(poly) " else "", clazz, parents1)
)
if (!currentRun.compiles(sym) && isPast(ownPhase)) {
// Skip specialization info transform for third party classes that aren't referenced directly
// from the tree or by the specialization info transform itself that are run up to the end of
// the specialization phase.
//
// As a special case, we unconditionally specialize Function and Tuple classes above in `Phase#apply`
// as the backend needs to know about these for code it inlines to enable box- and null-check elimination.
tpe
} else {
val tparams = tpe.typeParams
if (tparams.isEmpty)
exitingSpecialize(parents map (_.typeSymbol.info))

val parents1 = parents mapConserve specializedType
if (parents ne parents1) {
debuglog("specialization transforms %s%s parents to %s".format(
if (tparams.nonEmpty) "(poly) " else "", clazz, parents1)
)
}
val newScope = newScopeWith(specializeClass(clazz, typeEnv(clazz)) ++ specialOverrides(clazz): _*)
// If tparams.isEmpty, this is just the ClassInfoType.
GenPolyType(tparams, ClassInfoType(parents1, newScope, clazz))
}
val newScope = newScopeWith(specializeClass(clazz, typeEnv(clazz)) ++ specialOverrides(clazz): _*)
// If tparams.isEmpty, this is just the ClassInfoType.
GenPolyType(tparams, ClassInfoType(parents1, newScope, clazz))
case _ =>
tpe
}
Expand Down

0 comments on commit 8ae0fda

Please sign in to comment.