aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/typer
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2016-09-01 11:59:13 +0200
committerMartin Odersky <odersky@gmail.com>2016-10-02 16:07:00 +0200
commitc87a9dd1f34cd7afe3fba0edfa1463019eaa78bd (patch)
tree7500f567d08b96ccbf3a26dd02b76c0ca42aadda /src/dotty/tools/dotc/typer
parent56f01cdf9081b0b221a01e5ae1a9f10e4aa62da9 (diff)
downloaddotty-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')
-rw-r--r--src/dotty/tools/dotc/typer/Inliner.scala200
-rw-r--r--src/dotty/tools/dotc/typer/Namer.scala7
-rw-r--r--src/dotty/tools/dotc/typer/ReTyper.scala4
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala18
4 files changed, 224 insertions, 5 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
+ }
+}
diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala
index 6eca9be41..06e798bdb 100644
--- a/src/dotty/tools/dotc/typer/Namer.scala
+++ b/src/dotty/tools/dotc/typer/Namer.scala
@@ -566,10 +566,17 @@ class Namer { typer: Typer =>
val cls = typedAheadAnnotation(annotTree)
val ann = Annotation.deferred(cls, implicit ctx => typedAnnotation(annotTree))
denot.addAnnotation(ann)
+ if (cls == defn.InlineAnnot) addInlineInfo(ann, original)
}
case _ =>
}
+ private def addInlineInfo(inlineAnnot: Annotation, original: untpd.Tree) = original match {
+ case original: untpd.DefDef =>
+ Inliner.attachBody(inlineAnnot, typedAheadExpr(original).asInstanceOf[tpd.DefDef].rhs)
+ case _ =>
+ }
+
/** Intentionally left without `implicit ctx` parameter. We need
* to pick up the context at the point where the completer was created.
*/
diff --git a/src/dotty/tools/dotc/typer/ReTyper.scala b/src/dotty/tools/dotc/typer/ReTyper.scala
index 9750957bf..03b415a6f 100644
--- a/src/dotty/tools/dotc/typer/ReTyper.scala
+++ b/src/dotty/tools/dotc/typer/ReTyper.scala
@@ -10,7 +10,7 @@ import typer.ProtoTypes._
import ast.{tpd, untpd}
import ast.Trees._
import scala.util.control.NonFatal
-import config.Printers
+import config.Printers.typr
/** A version of Typer that keeps all symbols defined and referenced in a
* previously typed tree.
@@ -87,7 +87,7 @@ class ReTyper extends Typer {
try super.typedUnadapted(tree, pt)
catch {
case NonFatal(ex) =>
- println(i"exception while typing $tree of class ${tree.getClass} # ${tree.uniqueId}")
+ typr.println(i"exception while typing $tree of class ${tree.getClass} # ${tree.uniqueId}")
throw ex
}
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index 562af75f6..007eaa468 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -885,12 +885,15 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
(EmptyTree, WildcardType)
}
else if (owner != cx.outer.owner && owner.isRealMethod) {
- if (owner.isCompleted) {
+ if (owner.isInlineMethod)
+ (EmptyTree, errorType(em"no explicit return allowed from inline $owner", tree.pos))
+ else if (!owner.isCompleted)
+ (EmptyTree, errorType(em"$owner has return statement; needs result type", tree.pos))
+ else {
val from = Ident(TermRef(NoPrefix, owner.asTerm))
val proto = returnProto(owner, cx.scope)
(from, proto)
}
- else (EmptyTree, errorType(em"$owner has return statement; needs result type", tree.pos))
}
else enclMethInfo(cx.outer)
}
@@ -1154,6 +1157,13 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
rhsCtx.gadt.setBounds(tdef.symbol, TypeAlias(tparam.typeRef)))
}
val rhs1 = typedExpr(ddef.rhs, tpt1.tpe)(rhsCtx)
+
+ // Overwrite inline body to make sure it is not evaluated twice
+ sym.getAnnotation(defn.InlineAnnot) match {
+ case Some(ann) => Inliner.attachBody(ann, rhs1)
+ case _ =>
+ }
+
if (sym.isAnonymousFunction) {
// If we define an anonymous function, make sure the return type does not
// refer to parameters. This is necessary because closure types are
@@ -1773,7 +1783,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
tree
}
else if (tree.tpe <:< pt)
- if (ctx.typeComparer.GADTused && pt.isValueType)
+ if (tree.symbol.isInlineMethod && !ctx.owner.ownersIterator.exists(_.isInlineMethod))
+ adapt(Inliner.inlineCall(tree, pt), pt)
+ else if (ctx.typeComparer.GADTused && pt.isValueType)
// Insert an explicit cast, so that -Ycheck in later phases succeeds.
// I suspect, but am not 100% sure that this might affect inferred types,
// if the expected type is a supertype of the GADT bound. It would be good to come