aboutsummaryrefslogtreecommitdiff
path: root/src/dotty
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2015-04-30 09:34:19 +0200
committerMartin Odersky <odersky@gmail.com>2015-05-02 19:07:38 +0200
commit5cd90e5afd73c6f8354d0d3687fb2ee3a9f413e7 (patch)
tree4e69c2beb3a34efd9ff41e2234073ba440f1b855 /src/dotty
parente2c23bd56ef739682699384cf1956b62b75c1ae0 (diff)
downloaddotty-5cd90e5afd73c6f8354d0d3687fb2ee3a9f413e7.tar.gz
dotty-5cd90e5afd73c6f8354d0d3687fb2ee3a9f413e7.tar.bz2
dotty-5cd90e5afd73c6f8354d0d3687fb2ee3a9f413e7.zip
New miniphase: ExpandSAMs
The phase replaces SAM closures with anonymous classes when necessary.
Diffstat (limited to 'src/dotty')
-rw-r--r--src/dotty/tools/dotc/Compiler.scala1
-rw-r--r--src/dotty/tools/dotc/core/StdNames.scala1
-rw-r--r--src/dotty/tools/dotc/core/Types.scala10
-rw-r--r--src/dotty/tools/dotc/transform/ExpandSAMs.scala91
4 files changed, 103 insertions, 0 deletions
diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala
index 44e7ac450..a9aa65201 100644
--- a/src/dotty/tools/dotc/Compiler.scala
+++ b/src/dotty/tools/dotc/Compiler.scala
@@ -45,6 +45,7 @@ class Compiler {
new ElimRepeated,
new NormalizeFlags,
new ExtensionMethods,
+ new ExpandSAMs,
new TailRec),
List(new PatternMatcher,
new ExplicitOuter,
diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala
index 74a121b47..826cbe2c6 100644
--- a/src/dotty/tools/dotc/core/StdNames.scala
+++ b/src/dotty/tools/dotc/core/StdNames.scala
@@ -418,6 +418,7 @@ object StdNames {
val isArray: N = "isArray"
val isDefined: N = "isDefined"
val isDefinedAt: N = "isDefinedAt"
+ val isDefinedAtImpl: N = "$isDefinedAt"
val isEmpty: N = "isEmpty"
val isInstanceOf_ : N = "isInstanceOf"
val java: N = "java"
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index 595732b37..80cceed38 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -545,6 +545,11 @@ object Types {
(name, buf) => buf ++= member(name).altsWith(x => x.isClass))
}
+ final def fields(implicit ctx: Context): Seq[SingleDenotation] = track("fields") {
+ memberDenots(fieldFilter,
+ (name, buf) => buf ++= member(name).altsWith(x => !x.is(Method)))
+ }
+
/** The set of members of this type having at least one of `requiredFlags` but none of `excludedFlags` set */
final def membersBasedOnFlags(requiredFlags: FlagSet, excludedFlags: FlagSet)(implicit ctx: Context): Seq[SingleDenotation] = track("implicitMembers") {
memberDenots(takeAllFilter,
@@ -3049,6 +3054,11 @@ object Types {
def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean = name.isTypeName
}
+ object fieldFilter extends NameFilter {
+ def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean =
+ name.isTermName && (pre member name).hasAltWith(!_.symbol.is(Method))
+ }
+
object takeAllFilter extends NameFilter {
def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean = true
}
diff --git a/src/dotty/tools/dotc/transform/ExpandSAMs.scala b/src/dotty/tools/dotc/transform/ExpandSAMs.scala
new file mode 100644
index 000000000..1340a2067
--- /dev/null
+++ b/src/dotty/tools/dotc/transform/ExpandSAMs.scala
@@ -0,0 +1,91 @@
+package dotty.tools.dotc
+package transform
+
+import core._
+import Contexts._, Symbols._, Types._, Flags._, Decorators._, StdNames._, Constants._
+import SymDenotations.SymDenotation
+import TreeTransforms._
+import ast.untpd
+import ast.Trees._
+
+/** Expand SAM closures that cannot be represented by the JVM to anonymous classes.
+ * These fall into three categories
+ *
+ * 1. Partial function closures, we need to generate a isDefinedAt method for these.
+ * 2. Closures implementaing non-trait classes.
+ * 3. Closures that get synthesized abstract methods in the transformation pipeline. These methods can be
+ * (1) superaccessors, (2) outer references, (3) accessors for fields.
+ */
+class ExpandSAMs extends MiniPhaseTransform { thisTransformer =>
+ override def phaseName = "expandSAMs"
+
+ import ast.tpd._
+
+ def noJvmSam(cls: ClassSymbol)(implicit ctx: Context): Boolean =
+ !cls.is(Trait) || ExplicitOuter.needsOuterIfReferenced(cls) || cls.typeRef.fields.nonEmpty
+
+ override def transformBlock(tree: Block)(implicit ctx: Context, info: TransformerInfo): Tree = tree match {
+ case Block(stats @ (fn: DefDef) :: Nil, Closure(_, fnRef, tpt)) if fnRef.symbol == fn.symbol =>
+ tpt.tpe match {
+ case NoType => tree // it's a plain function
+ case tpe @ SAMType(_) if !noJvmSam(tpe.classSymbol.asClass) =>
+ if (tpe isRef defn.PartialFunctionClass) toPartialFunction(tree)
+ else tree
+ case tpe =>
+ cpy.Block(tree)(stats,
+ AnonClass(tpe, fn.symbol.asTerm :: Nil, nme.apply :: Nil))
+ }
+ case _ =>
+ tree
+ }
+
+ private def toPartialFunction(tree: Block)(implicit ctx: Context, info: TransformerInfo): Tree = {
+ val Block(
+ (applyDef @ DefDef(nme.ANON_FUN, Nil, List(params), _, _)) :: Nil,
+ Closure(_, _, tpt)) = tree
+ val List(param) = params
+ // Dotty problem: If we match instead List(List(param)) directly,
+ // we get:
+ // Exception in thread "main" java.lang.AssertionError: assertion failed: self instantiation of (A?
+ // ...
+ // at scala.Predef$.assert(Predef.scala:165)
+ // at dotty.tools.dotc.core.Types$TypeVar.instantiateWith(Types.scala:2308)
+ // at dotty.tools.dotc.core.Types$TypeVar.instantiate(Types.scala:2363)
+ // at dotty.tools.dotc.typer.Inferencing$$anonfun$interpolate$1$1$$anonfun$apply$mcV$sp$4.apply(Inferencing.scala:198)
+ // at dotty.tools.dotc.typer.Inferencing$$anonfun$interpolate$1$1$$anonfun$apply$mcV$sp$4.apply(Inferencing.scala:195)
+ //
+ // I think it has to do with the double :: (or List) pattern to extract `param`.
+
+ val applyRhs: Tree = applyDef.rhs
+ val applyFn = applyDef.symbol.asTerm
+
+ val MethodType(paramNames, paramTypes) = applyFn.info
+ val isDefinedAtFn = applyFn.copy(
+ name = nme.isDefinedAtImpl,
+ flags = Synthetic | Method,
+ info = MethodType(paramNames, paramTypes, defn.BooleanType)).asTerm
+ val tru = Literal(Constant(true))
+ def isDefinedAtRhs(paramRefss: List[List[Tree]]) = applyRhs match {
+ case Match(selector, cases) =>
+ assert(selector.symbol == param.symbol)
+ val paramRef = paramRefss.head.head
+ // Again, the alternative
+ // val List(List(paramRef)) = paramRefs
+ // fails with a similar self instantiation error
+ def translateCase(cdef: CaseDef): CaseDef =
+ cpy.CaseDef(cdef)(body = tru).changeOwner(applyFn, isDefinedAtFn)
+ val defaultSym = ctx.newSymbol(isDefinedAtFn, nme.WILDCARD, Synthetic, selector.tpe.widen)
+ val defaultCase =
+ CaseDef(
+ Bind(defaultSym, untpd.Ident(nme.WILDCARD).withType(selector.tpe.widen)),
+ EmptyTree,
+ Literal(Constant(false)))
+ cpy.Match(applyRhs)(paramRef, cases.map(translateCase) :+ defaultCase)
+ case _ =>
+ tru
+ }
+ val isDefinedAtDef = transformFollowingDeep(DefDef(isDefinedAtFn, isDefinedAtRhs(_)))
+ val anonCls = AnonClass(tpt.tpe, List(applyFn, isDefinedAtFn), List(nme.apply, nme.isDefinedAt))
+ cpy.Block(tree)(List(applyDef, isDefinedAtDef), anonCls)
+ }
+}