Skip to content

New phase: FunctionalInterfaces. Rewires closures to implement more specific types of Function SAMs. #488

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 8 commits into from
Apr 23, 2015
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
3 changes: 2 additions & 1 deletion src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ class Compiler {
List(new Mixin,
new Memoize,
new CapturedVars,
new Constructors),
new Constructors,
new FunctionalInterfaces),
List(new LambdaLift,
new Flatten,
new RestoreScopes),
Expand Down
15 changes: 10 additions & 5 deletions src/dotty/tools/dotc/core/Denotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,15 @@ object Denotations {
}

/** Return symbol in this denotation that satisfies the given predicate.
* Return a stubsymbol if denotation is a missing ref.
* if generateStubs is specified, return a stubsymbol if denotation is a missing ref.
* Throw a `TypeError` if predicate fails to disambiguate symbol or no alternative matches.
*/
def requiredSymbol(p: Symbol => Boolean, source: AbstractFile = null)(implicit ctx: Context): Symbol =
def requiredSymbol(p: Symbol => Boolean, source: AbstractFile = null, generateStubs: Boolean = true)(implicit ctx: Context): Symbol =
disambiguate(p) match {
case MissingRef(ownerd, name) =>
ctx.newStubSymbol(ownerd.symbol, name, source)
if (generateStubs)
ctx.newStubSymbol(ownerd.symbol, name, source)
else NoSymbol
case NoDenotation | _: NoQualifyingRef =>
throw new TypeError(s"None of the alternatives of $this satisfies required predicate")
case denot =>
Expand Down Expand Up @@ -874,8 +876,9 @@ object Denotations {

/** The current denotation of the static reference given by path,
* or a MissingRef or NoQualifyingRef instance, if it does not exist.
* if generateStubs is set, generates stubs for missing top-level symbols
*/
def staticRef(path: Name)(implicit ctx: Context): Denotation = {
def staticRef(path: Name, generateStubs: Boolean = true)(implicit ctx: Context): Denotation = {
def recur(path: Name, len: Int): Denotation = {
val point = path.lastIndexOf('.', len - 1)
val owner =
Expand All @@ -887,7 +890,9 @@ object Denotations {
val result = owner.info.member(name)
if (result ne NoDenotation) result
else {
val alt = missingHook(owner.symbol.moduleClass, name)
val alt =
if (generateStubs) missingHook(owner.symbol.moduleClass, name)
else NoSymbol
if (alt.exists) alt.denot
else MissingRef(owner, name)
}
Expand Down
26 changes: 24 additions & 2 deletions src/dotty/tools/dotc/core/NameOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ object NameOps {
def stripAnonNumberSuffix: Name = {
var pos = name.length
while (pos > 0 && name(pos - 1).isDigit)
pos -= 1
pos -= 1

if (pos > 0 && pos < name.length && name(pos - 1) == '$')
name take (pos - 1)
Expand Down Expand Up @@ -145,7 +145,7 @@ object NameOps {
}.asInstanceOf[N]

/** The superaccessor for method with given name */
def superName: TermName = (nme.SUPER_PREFIX ++ name).toTermName
def superName: TermName = (nme.SUPER_PREFIX ++ name).toTermName

/** The expanded name of `name` relative to given class `base`.
*/
Expand Down Expand Up @@ -224,6 +224,28 @@ object NameOps {
case nme.clone_ => nme.clone_
}

def specializedFor(returnType: Types.Type, args: List[Types.Type])(implicit ctx: Context): name.ThisName = {

def typeToTag(tp: Types.Type): Name = {
tp.classSymbol match {
case t if t eq defn.IntClass => nme.specializedTypeNames.Int
case t if t eq defn.BooleanClass => nme.specializedTypeNames.Boolean
case t if t eq defn.ByteClass => nme.specializedTypeNames.Byte
case t if t eq defn.LongClass => nme.specializedTypeNames.Long
case t if t eq defn.ShortClass => nme.specializedTypeNames.Short
case t if t eq defn.FloatClass => nme.specializedTypeNames.Float
case t if t eq defn.UnitClass => nme.specializedTypeNames.Void
case t if t eq defn.DoubleClass => nme.specializedTypeNames.Double
case t if t eq defn.CharClass => nme.specializedTypeNames.Char
case _ => nme.specializedTypeNames.Object
}
}

name.fromName(name ++ nme.specializedTypeNames.prefix ++
args.map(typeToTag).foldRight(typeToTag(returnType))(_ ++ _) ++
nme.specializedTypeNames.suffix)
}

/** If name length exceeds allowable limit, replace part of it by hash */
def compactified(implicit ctx: Context): TermName = termName(compactify(name.toString))
}
Expand Down
16 changes: 16 additions & 0 deletions src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,22 @@ object StdNames {
final val isUnary: Set[Name] = Set(MINUS, PLUS, TILDE, BANG)
}

object specializedTypeNames {
final val Boolean: N = "Z"
final val Byte: N = "B"
final val Char: N = "C"
final val Short: N = "S"
final val Int: N = "I"
final val Long: N = "J"
final val Float: N = "F"
final val Double: N = "D"
final val Void: N = "V"
final val Object: N = "L"

final val prefix: N = "$mc"
final val suffix: N = "$sp"
}

// value-conversion methods
val toByte: N = "toByte"
val toShort: N = "toShort"
Expand Down
6 changes: 6 additions & 0 deletions src/dotty/tools/dotc/core/Symbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,12 @@ trait Symbols { this: Context =>
def requiredClass(path: PreName): ClassSymbol =
base.staticRef(path.toTypeName).requiredSymbol(_.isClass).asClass

/** Get ClassSymbol if class is either defined in current compilation run
* or present on classpath.
* Returns NoSymbol otherwise. */
def getClassIfDefined(path: PreName): Symbol =
base.staticRef(path.toTypeName, generateStubs = false).requiredSymbol(_.isClass, generateStubs = false)

def requiredModule(path: PreName): TermSymbol =
base.staticRef(path.toTermName).requiredSymbol(_ is Module).asTerm

Expand Down
78 changes: 78 additions & 0 deletions src/dotty/tools/dotc/transform/FunctionalInterfaces.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package dotty.tools.dotc
package transform

import TreeTransforms._
import core.DenotTransformers._
import core.Symbols._
import core.Contexts._
import core.Types._
import core.Flags._
import core.Decorators._
import core.SymDenotations._
import core.StdNames.nme
import core.Names._
import core.NameOps._
import ast.Trees._
import SymUtils._
import dotty.tools.dotc.ast.tpd
import collection.{ mutable, immutable }
import collection.mutable.{ LinkedHashMap, LinkedHashSet, TreeSet }

/**
* Rewires closures to implement more specific types of Functions.
*/
class FunctionalInterfaces extends MiniPhaseTransform {
import tpd._

def phaseName: String = "functionalInterfaces"

private var allowedReturnTypes: Set[Symbol] = _ // moved here to make it explicit what specializations are generated
private var allowedArgumentTypes: Set[Symbol] = _
val maxArgsCount = 2

def shouldSpecialize(m: MethodType)(implicit ctx: Context) =
(m.paramTypes.size <= maxArgsCount) &&
m.paramTypes.forall(x => allowedArgumentTypes.contains(x.typeSymbol)) &&
allowedReturnTypes.contains(m.resultType.typeSymbol)

val functionName = "JFunction".toTermName
val functionPackage = "scala.compat.java8.".toTermName

override def prepareForUnit(tree: tpd.Tree)(implicit ctx: Context): TreeTransform = {
allowedReturnTypes = Set(defn.UnitClass,
defn.BooleanClass,
defn.IntClass,
defn.FloatClass,
defn.LongClass,
defn.DoubleClass,
/* only for Function0: */ defn.ByteClass,
defn.ShortClass,
defn.CharClass)

allowedArgumentTypes = Set(defn.IntClass,
defn.LongClass,
defn.DoubleClass,
/* only for Function1: */ defn.FloatClass)

this
}

override def transformClosure(tree: Closure)(implicit ctx: Context, info: TransformerInfo): Tree = {
tree.tpt match {
case EmptyTree =>
val m = tree.meth.tpe.widen.asInstanceOf[MethodType]

if (shouldSpecialize(m)) {
val interfaceName = (functionName ++ m.paramTypes.length.toString).specializedFor(m.resultType, m.paramTypes)
// symbols loaded from classpath aren't defined in periods earlier than when they where loaded
val interface = ctx.withPhase(ctx.typerPhase).getClassIfDefined(functionPackage ++ interfaceName)
if (interface.exists) {
val tpt = tpd.TypeTree(interface.asType.typeRef)
tpd.Closure(tree.env, tree.meth, tpt)
} else tree
} else tree
case _ =>
tree
}
}
}
Loading