diff options
author | Martin Odersky <odersky@gmail.com> | 2016-09-01 11:59:13 +0200 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2016-10-02 16:07:00 +0200 |
commit | c87a9dd1f34cd7afe3fba0edfa1463019eaa78bd (patch) | |
tree | 7500f567d08b96ccbf3a26dd02b76c0ca42aadda /src/dotty/tools/dotc/typer/Inliner.scala | |
parent | 56f01cdf9081b0b221a01e5ae1a9f10e4aa62da9 (diff) | |
download | dotty-c87a9dd1f34cd7afe3fba0edfa1463019eaa78bd.tar.gz dotty-c87a9dd1f34cd7afe3fba0edfa1463019eaa78bd.tar.bz2 dotty-c87a9dd1f34cd7afe3fba0edfa1463019eaa78bd.zip |
First version of inline scheme
To be done: outer accessors
To be done: error positions
Diffstat (limited to 'src/dotty/tools/dotc/typer/Inliner.scala')
-rw-r--r-- | src/dotty/tools/dotc/typer/Inliner.scala | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/src/dotty/tools/dotc/typer/Inliner.scala b/src/dotty/tools/dotc/typer/Inliner.scala new file mode 100644 index 000000000..852689a75 --- /dev/null +++ b/src/dotty/tools/dotc/typer/Inliner.scala @@ -0,0 +1,200 @@ +package dotty.tools +package dotc +package typer + +import dotty.tools.dotc.ast.Trees.NamedArg +import dotty.tools.dotc.ast.{Trees, untpd, tpd, TreeTypeMap} +import Trees._ +import core._ +import Flags._ +import Symbols._ +import Types._ +import Decorators._ +import StdNames.nme +import Contexts.Context +import Names.Name +import SymDenotations.SymDenotation +import Annotations.Annotation +import transform.ExplicitOuter +import config.Printers.inlining +import ErrorReporting.errorTree +import util.Attachment +import collection.mutable + +object Inliner { + import tpd._ + + private class InlinedBody(tree: => Tree) { + lazy val body = tree + } + + private val InlinedBody = new Attachment.Key[InlinedBody] + + def attachBody(inlineAnnot: Annotation, tree: => Tree)(implicit ctx: Context): Unit = + inlineAnnot.tree.putAttachment(InlinedBody, new InlinedBody(tree)) + + def inlinedBody(sym: SymDenotation)(implicit ctx: Context): Tree = + sym.getAnnotation(defn.InlineAnnot).get.tree + .attachment(InlinedBody).body + + private class Typer extends ReTyper { + override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = { + val acc = tree.symbol + super.typedSelect(tree, pt) match { + case res @ Select(qual, name) => + if (name.endsWith(nme.OUTER)) { + val outerAcc = tree.symbol + println(i"selecting $tree / ${acc} / ${qual.tpe.normalizedPrefix}") + res.withType(qual.tpe.widen.normalizedPrefix) + } + else { + ensureAccessible(res.tpe, qual.isInstanceOf[Super], tree.pos) + res + } + case res => res + } + } + } + + def inlineCall(tree: Tree, pt: Type)(implicit ctx: Context): Tree = { + if (ctx.inlineCount < ctx.settings.xmaxInlines.value) { + ctx.inlineCount += 1 + val rhs = inlinedBody(tree.symbol) + val inlined = new Inliner(tree, rhs).inlined + try new Typer().typedUnadapted(inlined, pt) + finally ctx.inlineCount -= 1 + } else errorTree(tree, + i"""Maximal number of successive inlines (${ctx.settings.xmaxInlines.value}) exceeded, + | Maybe this is caused by a recursive inline method? + | You can use -Xmax:inlines to change the limit.""") + } +} + +class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) { + import tpd._ + + private val meth = call.symbol + + private def decomposeCall(tree: Tree): (Tree, List[Tree], List[List[Tree]]) = tree match { + case Apply(fn, args) => + val (meth, targs, argss) = decomposeCall(fn) + (meth, targs, argss :+ args) + case TypeApply(fn, targs) => + val (meth, Nil, Nil) = decomposeCall(fn) + (meth, targs, Nil) + case _ => + (tree, Nil, Nil) + } + + private val (methPart, targs, argss) = decomposeCall(call) + + private lazy val prefix = methPart match { + case Select(qual, _) => qual + case _ => tpd.This(ctx.owner.enclosingClass.asClass) + } + + private val replacement = new mutable.HashMap[Type, NamedType] + + private val paramBindings = paramBindingsOf(meth.info, targs, argss) + + private def paramBindingsOf(tp: Type, targs: List[Tree], argss: List[List[Tree]]): List[MemberDef] = tp match { + case tp: PolyType => + val bindings = + (tp.paramNames, targs).zipped.map { (name, arg) => + val tparam = newSym(name, EmptyFlags, TypeAlias(arg.tpe.stripTypeVar)).asType + TypeDef(tparam) + } + bindings ::: paramBindingsOf(tp.resultType, Nil, argss) + case tp: MethodType => + val bindings = + (tp.paramNames, tp.paramTypes, argss.head).zipped.map { (name, paramtp, arg) => + def isByName = paramtp.dealias.isInstanceOf[ExprType] + val (paramFlags, paramType) = + if (isByName) (Method, ExprType(arg.tpe)) else (EmptyFlags, arg.tpe) + val vparam = newSym(name, paramFlags, paramType).asTerm + if (isByName) DefDef(vparam, arg) else ValDef(vparam, arg) + } + bindings ::: paramBindingsOf(tp.resultType, targs, argss.tail) + case _ => + assert(targs.isEmpty) + assert(argss.isEmpty) + Nil + } + + private def newSym(name: Name, flags: FlagSet, info: Type): Symbol = + ctx.newSymbol(ctx.owner, name, flags, info, coord = call.pos) + + private def registerType(tpe: Type): Unit = + if (!replacement.contains(tpe)) tpe match { + case tpe: ThisType => + if (!ctx.owner.isContainedIn(tpe.cls) && !tpe.cls.is(Package)) + if (tpe.cls.isStaticOwner) + replacement(tpe) = tpe.cls.sourceModule.termRef + else { + def outerDistance(cls: Symbol): Int = { + assert(cls.exists, i"not encl: ${meth.owner.enclosingClass} ${tpe.cls}") + if (tpe.cls eq cls) 0 + else outerDistance(cls.owner.enclosingClass) + 1 + } + val n = outerDistance(meth.owner) + replacement(tpe) = newSym(nme.SELF ++ n.toString, EmptyFlags, tpe.widen).termRef + } + case tpe: NamedType if tpe.symbol.is(Param) && tpe.symbol.owner == meth => + val Some(binding) = paramBindings.find(_.name == tpe.name) + replacement(tpe) = + if (tpe.name.isTypeName) binding.symbol.typeRef else binding.symbol.termRef + case _ => + } + + private def registerLeaf(tree: Tree): Unit = tree match { + case _: This | _: Ident => registerType(tree.tpe) + case _ => + } + + private def outerLevel(sym: Symbol) = sym.name.drop(nme.SELF.length).toString.toInt + + val inlined = { + rhs.foreachSubTree(registerLeaf) + + val accessedSelfSyms = + (for ((tp: ThisType, ref) <- replacement) yield ref.symbol.asTerm).toSeq.sortBy(outerLevel) + + val outerBindings = new mutable.ListBuffer[MemberDef] + for (selfSym <- accessedSelfSyms) { + val rhs = + if (outerBindings.isEmpty) prefix + else { + val lastSelf = outerBindings.last.symbol + val outerDelta = outerLevel(selfSym) - outerLevel(lastSelf) + def outerSelect(ref: Tree, dummy: Int): Tree = ??? + //ref.select(ExplicitOuter.outerAccessorTBD(ref.tpe.widen.classSymbol.asClass)) + (ref(lastSelf) /: (0 until outerDelta))(outerSelect) + } + outerBindings += ValDef(selfSym, rhs.ensureConforms(selfSym.info)) + } + outerBindings ++= paramBindings + + val typeMap = new TypeMap { + def apply(t: Type) = t match { + case _: SingletonType => replacement.getOrElse(t, t) + case _ => mapOver(t) + } + } + + def treeMap(tree: Tree) = tree match { + case _: This | _: Ident => + replacement.get(tree.tpe) match { + case Some(t) => ref(t) + case None => tree + } + case _ => tree + } + + val inliner = new TreeTypeMap(typeMap, treeMap, meth :: Nil, ctx.owner :: Nil) + + val result = inliner(Block(outerBindings.toList, rhs)).withPos(call.pos) + + inlining.println(i"inlining $call\n --> \n$result") + result + } +} |