aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala')
-rw-r--r--compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala86
1 files changed, 86 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala b/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala
new file mode 100644
index 000000000..91399f91a
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala
@@ -0,0 +1,86 @@
+package dotty.tools.dotc
+package transform
+
+import core._
+import Contexts._, Symbols._, Types._, Flags._, Decorators._, StdNames._, Constants._
+import SymDenotations.SymDenotation
+import TreeTransforms._
+import SymUtils._
+import ast.untpd
+import ast.Trees._
+
+/** Expand SAM closures that cannot be represented by the JVM as lambdas to anonymous classes.
+ * These fall into five categories
+ *
+ * 1. Partial function closures, we need to generate a isDefinedAt method for these.
+ * 2. Closures implementing non-trait classes.
+ * 3. Closures implementing classes that inherit from a class other than Object
+ * (a lambda cannot not be a run-time subtype of such a class)
+ * 4. Closures that implement traits which run initialization code.
+ * 5. 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._
+
+ /** Is the SAMType `cls` also a SAM under the rules of the platform? */
+ def isPlatformSam(cls: ClassSymbol)(implicit ctx: Context): Boolean =
+ ctx.platform.isSam(cls)
+
+ 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 tpe.isRef(defn.PartialFunctionClass) =>
+ toPartialFunction(tree)
+ case tpe @ SAMType(_) if isPlatformSam(tpe.classSymbol.asClass) =>
+ tree
+ case tpe =>
+ val Seq(samDenot) = tpe.abstractTermMembers.filter(!_.symbol.is(SuperAccessor))
+ cpy.Block(tree)(stats,
+ AnonClass(tpe :: Nil, fn.symbol.asTerm :: Nil, samDenot.symbol.asTerm.name :: Nil))
+ }
+ case _ =>
+ tree
+ }
+
+ private def toPartialFunction(tree: Block)(implicit ctx: Context, info: TransformerInfo): Tree = {
+ val Block(
+ (applyDef @ DefDef(nme.ANON_FUN, Nil, List(List(param)), _, _)) :: Nil,
+ Closure(_, _, tpt)) = tree
+ val applyRhs: Tree = applyDef.rhs
+ val applyFn = applyDef.symbol.asTerm
+
+ val MethodType(paramNames, paramTypes) = applyFn.info
+ val isDefinedAtFn = applyFn.copy(
+ name = nme.isDefinedAt,
+ 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, Underscore(selector.tpe.widen)),
+ EmptyTree,
+ Literal(Constant(false)))
+ val annotated = Annotated(paramRef, New(ref(defn.UncheckedAnnotType)))
+ cpy.Match(applyRhs)(annotated, cases.map(translateCase) :+ defaultCase)
+ case _ =>
+ tru
+ }
+ val isDefinedAtDef = transformFollowingDeep(DefDef(isDefinedAtFn, isDefinedAtRhs(_)))
+ val anonCls = AnonClass(tpt.tpe :: Nil, List(applyFn, isDefinedAtFn), List(nme.apply, nme.isDefinedAt))
+ cpy.Block(tree)(List(applyDef, isDefinedAtDef), anonCls)
+ }
+}