Skip to content

Commit f1c191a

Browse files
committed
Merge pull request #488 from dotty-staging/functionalInterfaces
New phase: FunctionalInterfaces. Rewires closures to implement more specific types of Function SAMs.
2 parents e3449e9 + 3462e98 commit f1c191a

File tree

140 files changed

+2261
-901
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

140 files changed

+2261
-901
lines changed

src/dotty/tools/dotc/Compiler.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ class Compiler {
6262
List(new Mixin,
6363
new Memoize,
6464
new CapturedVars,
65-
new Constructors),
65+
new Constructors,
66+
new FunctionalInterfaces),
6667
List(new LambdaLift,
6768
new Flatten,
6869
new RestoreScopes),

src/dotty/tools/dotc/core/Denotations.scala

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -177,13 +177,15 @@ object Denotations {
177177
}
178178

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

875877
/** The current denotation of the static reference given by path,
876878
* or a MissingRef or NoQualifyingRef instance, if it does not exist.
879+
* if generateStubs is set, generates stubs for missing top-level symbols
877880
*/
878-
def staticRef(path: Name)(implicit ctx: Context): Denotation = {
881+
def staticRef(path: Name, generateStubs: Boolean = true)(implicit ctx: Context): Denotation = {
879882
def recur(path: Name, len: Int): Denotation = {
880883
val point = path.lastIndexOf('.', len - 1)
881884
val owner =
@@ -887,7 +890,9 @@ object Denotations {
887890
val result = owner.info.member(name)
888891
if (result ne NoDenotation) result
889892
else {
890-
val alt = missingHook(owner.symbol.moduleClass, name)
893+
val alt =
894+
if (generateStubs) missingHook(owner.symbol.moduleClass, name)
895+
else NoSymbol
891896
if (alt.exists) alt.denot
892897
else MissingRef(owner, name)
893898
}

src/dotty/tools/dotc/core/NameOps.scala

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ object NameOps {
113113
def stripAnonNumberSuffix: Name = {
114114
var pos = name.length
115115
while (pos > 0 && name(pos - 1).isDigit)
116-
pos -= 1
116+
pos -= 1
117117

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

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

150150
/** The expanded name of `name` relative to given class `base`.
151151
*/
@@ -224,6 +224,28 @@ object NameOps {
224224
case nme.clone_ => nme.clone_
225225
}
226226

227+
def specializedFor(returnType: Types.Type, args: List[Types.Type])(implicit ctx: Context): name.ThisName = {
228+
229+
def typeToTag(tp: Types.Type): Name = {
230+
tp.classSymbol match {
231+
case t if t eq defn.IntClass => nme.specializedTypeNames.Int
232+
case t if t eq defn.BooleanClass => nme.specializedTypeNames.Boolean
233+
case t if t eq defn.ByteClass => nme.specializedTypeNames.Byte
234+
case t if t eq defn.LongClass => nme.specializedTypeNames.Long
235+
case t if t eq defn.ShortClass => nme.specializedTypeNames.Short
236+
case t if t eq defn.FloatClass => nme.specializedTypeNames.Float
237+
case t if t eq defn.UnitClass => nme.specializedTypeNames.Void
238+
case t if t eq defn.DoubleClass => nme.specializedTypeNames.Double
239+
case t if t eq defn.CharClass => nme.specializedTypeNames.Char
240+
case _ => nme.specializedTypeNames.Object
241+
}
242+
}
243+
244+
name.fromName(name ++ nme.specializedTypeNames.prefix ++
245+
args.map(typeToTag).foldRight(typeToTag(returnType))(_ ++ _) ++
246+
nme.specializedTypeNames.suffix)
247+
}
248+
227249
/** If name length exceeds allowable limit, replace part of it by hash */
228250
def compactified(implicit ctx: Context): TermName = termName(compactify(name.toString))
229251
}

src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,22 @@ object StdNames {
540540
final val isUnary: Set[Name] = Set(MINUS, PLUS, TILDE, BANG)
541541
}
542542

543+
object specializedTypeNames {
544+
final val Boolean: N = "Z"
545+
final val Byte: N = "B"
546+
final val Char: N = "C"
547+
final val Short: N = "S"
548+
final val Int: N = "I"
549+
final val Long: N = "J"
550+
final val Float: N = "F"
551+
final val Double: N = "D"
552+
final val Void: N = "V"
553+
final val Object: N = "L"
554+
555+
final val prefix: N = "$mc"
556+
final val suffix: N = "$sp"
557+
}
558+
543559
// value-conversion methods
544560
val toByte: N = "toByte"
545561
val toShort: N = "toShort"

src/dotty/tools/dotc/core/Symbols.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,12 @@ trait Symbols { this: Context =>
325325
def requiredClass(path: PreName): ClassSymbol =
326326
base.staticRef(path.toTypeName).requiredSymbol(_.isClass).asClass
327327

328+
/** Get ClassSymbol if class is either defined in current compilation run
329+
* or present on classpath.
330+
* Returns NoSymbol otherwise. */
331+
def getClassIfDefined(path: PreName): Symbol =
332+
base.staticRef(path.toTypeName, generateStubs = false).requiredSymbol(_.isClass, generateStubs = false)
333+
328334
def requiredModule(path: PreName): TermSymbol =
329335
base.staticRef(path.toTermName).requiredSymbol(_ is Module).asTerm
330336

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import TreeTransforms._
5+
import core.DenotTransformers._
6+
import core.Symbols._
7+
import core.Contexts._
8+
import core.Types._
9+
import core.Flags._
10+
import core.Decorators._
11+
import core.SymDenotations._
12+
import core.StdNames.nme
13+
import core.Names._
14+
import core.NameOps._
15+
import ast.Trees._
16+
import SymUtils._
17+
import dotty.tools.dotc.ast.tpd
18+
import collection.{ mutable, immutable }
19+
import collection.mutable.{ LinkedHashMap, LinkedHashSet, TreeSet }
20+
21+
/**
22+
* Rewires closures to implement more specific types of Functions.
23+
*/
24+
class FunctionalInterfaces extends MiniPhaseTransform {
25+
import tpd._
26+
27+
def phaseName: String = "functionalInterfaces"
28+
29+
private var allowedReturnTypes: Set[Symbol] = _ // moved here to make it explicit what specializations are generated
30+
private var allowedArgumentTypes: Set[Symbol] = _
31+
val maxArgsCount = 2
32+
33+
def shouldSpecialize(m: MethodType)(implicit ctx: Context) =
34+
(m.paramTypes.size <= maxArgsCount) &&
35+
m.paramTypes.forall(x => allowedArgumentTypes.contains(x.typeSymbol)) &&
36+
allowedReturnTypes.contains(m.resultType.typeSymbol)
37+
38+
val functionName = "JFunction".toTermName
39+
val functionPackage = "scala.compat.java8.".toTermName
40+
41+
override def prepareForUnit(tree: tpd.Tree)(implicit ctx: Context): TreeTransform = {
42+
allowedReturnTypes = Set(defn.UnitClass,
43+
defn.BooleanClass,
44+
defn.IntClass,
45+
defn.FloatClass,
46+
defn.LongClass,
47+
defn.DoubleClass,
48+
/* only for Function0: */ defn.ByteClass,
49+
defn.ShortClass,
50+
defn.CharClass)
51+
52+
allowedArgumentTypes = Set(defn.IntClass,
53+
defn.LongClass,
54+
defn.DoubleClass,
55+
/* only for Function1: */ defn.FloatClass)
56+
57+
this
58+
}
59+
60+
override def transformClosure(tree: Closure)(implicit ctx: Context, info: TransformerInfo): Tree = {
61+
tree.tpt match {
62+
case EmptyTree =>
63+
val m = tree.meth.tpe.widen.asInstanceOf[MethodType]
64+
65+
if (shouldSpecialize(m)) {
66+
val interfaceName = (functionName ++ m.paramTypes.length.toString).specializedFor(m.resultType, m.paramTypes)
67+
// symbols loaded from classpath aren't defined in periods earlier than when they where loaded
68+
val interface = ctx.withPhase(ctx.typerPhase).getClassIfDefined(functionPackage ++ interfaceName)
69+
if (interface.exists) {
70+
val tpt = tpd.TypeTree(interface.asType.typeRef)
71+
tpd.Closure(tree.env, tree.meth, tpt)
72+
} else tree
73+
} else tree
74+
case _ =>
75+
tree
76+
}
77+
}
78+
}

0 commit comments

Comments
 (0)