|
| 1 | +package dotty.tools |
| 2 | +package dotc |
| 3 | +package typer |
| 4 | + |
| 5 | +import dotty.tools.dotc.ast.Trees.NamedArg |
| 6 | +import dotty.tools.dotc.ast.{Trees, untpd, tpd, TreeTypeMap} |
| 7 | +import Trees._ |
| 8 | +import core._ |
| 9 | +import Flags._ |
| 10 | +import Symbols._ |
| 11 | +import Types._ |
| 12 | +import Decorators._ |
| 13 | +import StdNames.nme |
| 14 | +import Contexts.Context |
| 15 | +import Names.Name |
| 16 | +import SymDenotations.SymDenotation |
| 17 | +import Annotations.Annotation |
| 18 | +import transform.ExplicitOuter |
| 19 | +import config.Printers.inlining |
| 20 | +import ErrorReporting.errorTree |
| 21 | +import util.Property |
| 22 | +import collection.mutable |
| 23 | + |
| 24 | +object Inliner { |
| 25 | + import tpd._ |
| 26 | + |
| 27 | + private class InlinedBody(tree: => Tree) { |
| 28 | + lazy val body = tree |
| 29 | + } |
| 30 | + |
| 31 | + private val InlinedBody = new Property.Key[InlinedBody] // to be used as attachment |
| 32 | + |
| 33 | + def attachBody(inlineAnnot: Annotation, tree: => Tree)(implicit ctx: Context): Unit = |
| 34 | + inlineAnnot.tree.putAttachment(InlinedBody, new InlinedBody(tree)) |
| 35 | + |
| 36 | + def inlinedBody(sym: SymDenotation)(implicit ctx: Context): Tree = |
| 37 | + sym.getAnnotation(defn.InlineAnnot).get.tree |
| 38 | + .attachment(InlinedBody).body |
| 39 | + |
| 40 | + private class Typer extends ReTyper { |
| 41 | + override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = { |
| 42 | + val acc = tree.symbol |
| 43 | + super.typedSelect(tree, pt) match { |
| 44 | + case res @ Select(qual, name) => |
| 45 | + if (name.endsWith(nme.OUTER)) { |
| 46 | + val outerAcc = tree.symbol |
| 47 | + println(i"selecting $tree / ${acc} / ${qual.tpe.normalizedPrefix}") |
| 48 | + res.withType(qual.tpe.widen.normalizedPrefix) |
| 49 | + } |
| 50 | + else { |
| 51 | + ensureAccessible(res.tpe, qual.isInstanceOf[Super], tree.pos) |
| 52 | + res |
| 53 | + } |
| 54 | + case res => res |
| 55 | + } |
| 56 | + } |
| 57 | + } |
| 58 | + |
| 59 | + def inlineCall(tree: Tree, pt: Type)(implicit ctx: Context): Tree = { |
| 60 | + if (ctx.inlineCount < ctx.settings.xmaxInlines.value) { |
| 61 | + ctx.inlineCount += 1 |
| 62 | + val rhs = inlinedBody(tree.symbol) |
| 63 | + val inlined = new Inliner(tree, rhs).inlined |
| 64 | + try new Typer().typedUnadapted(inlined, pt) |
| 65 | + finally ctx.inlineCount -= 1 |
| 66 | + } else errorTree(tree, |
| 67 | + i"""Maximal number of successive inlines (${ctx.settings.xmaxInlines.value}) exceeded, |
| 68 | + | Maybe this is caused by a recursive inline method? |
| 69 | + | You can use -Xmax:inlines to change the limit.""") |
| 70 | + } |
| 71 | +} |
| 72 | + |
| 73 | +class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) { |
| 74 | + import tpd._ |
| 75 | + |
| 76 | + private val meth = call.symbol |
| 77 | + |
| 78 | + private def decomposeCall(tree: Tree): (Tree, List[Tree], List[List[Tree]]) = tree match { |
| 79 | + case Apply(fn, args) => |
| 80 | + val (meth, targs, argss) = decomposeCall(fn) |
| 81 | + (meth, targs, argss :+ args) |
| 82 | + case TypeApply(fn, targs) => |
| 83 | + val (meth, Nil, Nil) = decomposeCall(fn) |
| 84 | + (meth, targs, Nil) |
| 85 | + case _ => |
| 86 | + (tree, Nil, Nil) |
| 87 | + } |
| 88 | + |
| 89 | + private val (methPart, targs, argss) = decomposeCall(call) |
| 90 | + |
| 91 | + private lazy val prefix = methPart match { |
| 92 | + case Select(qual, _) => qual |
| 93 | + case _ => tpd.This(ctx.owner.enclosingClass.asClass) |
| 94 | + } |
| 95 | + |
| 96 | + private val replacement = new mutable.HashMap[Type, NamedType] |
| 97 | + |
| 98 | + private val paramBindings = paramBindingsOf(meth.info, targs, argss) |
| 99 | + |
| 100 | + private def paramBindingsOf(tp: Type, targs: List[Tree], argss: List[List[Tree]]): List[MemberDef] = tp match { |
| 101 | + case tp: PolyType => |
| 102 | + val bindings = |
| 103 | + (tp.paramNames, targs).zipped.map { (name, arg) => |
| 104 | + val tparam = newSym(name, EmptyFlags, TypeAlias(arg.tpe.stripTypeVar)).asType |
| 105 | + TypeDef(tparam) |
| 106 | + } |
| 107 | + bindings ::: paramBindingsOf(tp.resultType, Nil, argss) |
| 108 | + case tp: MethodType => |
| 109 | + val bindings = |
| 110 | + (tp.paramNames, tp.paramTypes, argss.head).zipped.map { (name, paramtp, arg) => |
| 111 | + def isByName = paramtp.dealias.isInstanceOf[ExprType] |
| 112 | + val (paramFlags, paramType) = |
| 113 | + if (isByName) (Method, ExprType(arg.tpe)) else (EmptyFlags, arg.tpe) |
| 114 | + val vparam = newSym(name, paramFlags, paramType).asTerm |
| 115 | + if (isByName) DefDef(vparam, arg) else ValDef(vparam, arg) |
| 116 | + } |
| 117 | + bindings ::: paramBindingsOf(tp.resultType, targs, argss.tail) |
| 118 | + case _ => |
| 119 | + assert(targs.isEmpty) |
| 120 | + assert(argss.isEmpty) |
| 121 | + Nil |
| 122 | + } |
| 123 | + |
| 124 | + private def newSym(name: Name, flags: FlagSet, info: Type): Symbol = |
| 125 | + ctx.newSymbol(ctx.owner, name, flags, info, coord = call.pos) |
| 126 | + |
| 127 | + private def registerType(tpe: Type): Unit = |
| 128 | + if (!replacement.contains(tpe)) tpe match { |
| 129 | + case tpe: ThisType => |
| 130 | + if (!ctx.owner.isContainedIn(tpe.cls) && !tpe.cls.is(Package)) |
| 131 | + if (tpe.cls.isStaticOwner) |
| 132 | + replacement(tpe) = tpe.cls.sourceModule.termRef |
| 133 | + else { |
| 134 | + def outerDistance(cls: Symbol): Int = { |
| 135 | + assert(cls.exists, i"not encl: ${meth.owner.enclosingClass} ${tpe.cls}") |
| 136 | + if (tpe.cls eq cls) 0 |
| 137 | + else outerDistance(cls.owner.enclosingClass) + 1 |
| 138 | + } |
| 139 | + val n = outerDistance(meth.owner) |
| 140 | + replacement(tpe) = newSym(nme.SELF ++ n.toString, EmptyFlags, tpe.widen).termRef |
| 141 | + } |
| 142 | + case tpe: NamedType if tpe.symbol.is(Param) && tpe.symbol.owner == meth => |
| 143 | + val Some(binding) = paramBindings.find(_.name == tpe.name) |
| 144 | + replacement(tpe) = |
| 145 | + if (tpe.name.isTypeName) binding.symbol.typeRef else binding.symbol.termRef |
| 146 | + case _ => |
| 147 | + } |
| 148 | + |
| 149 | + private def registerLeaf(tree: Tree): Unit = tree match { |
| 150 | + case _: This | _: Ident => registerType(tree.tpe) |
| 151 | + case _ => |
| 152 | + } |
| 153 | + |
| 154 | + private def outerLevel(sym: Symbol) = sym.name.drop(nme.SELF.length).toString.toInt |
| 155 | + |
| 156 | + val inlined = { |
| 157 | + rhs.foreachSubTree(registerLeaf) |
| 158 | + |
| 159 | + val accessedSelfSyms = |
| 160 | + (for ((tp: ThisType, ref) <- replacement) yield ref.symbol.asTerm).toSeq.sortBy(outerLevel) |
| 161 | + |
| 162 | + val outerBindings = new mutable.ListBuffer[MemberDef] |
| 163 | + for (selfSym <- accessedSelfSyms) { |
| 164 | + val rhs = |
| 165 | + if (outerBindings.isEmpty) prefix |
| 166 | + else { |
| 167 | + val lastSelf = outerBindings.last.symbol |
| 168 | + val outerDelta = outerLevel(selfSym) - outerLevel(lastSelf) |
| 169 | + def outerSelect(ref: Tree, dummy: Int): Tree = ??? |
| 170 | + //ref.select(ExplicitOuter.outerAccessorTBD(ref.tpe.widen.classSymbol.asClass)) |
| 171 | + (ref(lastSelf) /: (0 until outerDelta))(outerSelect) |
| 172 | + } |
| 173 | + outerBindings += ValDef(selfSym, rhs.ensureConforms(selfSym.info)) |
| 174 | + } |
| 175 | + outerBindings ++= paramBindings |
| 176 | + |
| 177 | + val typeMap = new TypeMap { |
| 178 | + def apply(t: Type) = t match { |
| 179 | + case _: SingletonType => replacement.getOrElse(t, t) |
| 180 | + case _ => mapOver(t) |
| 181 | + } |
| 182 | + } |
| 183 | + |
| 184 | + def treeMap(tree: Tree) = tree match { |
| 185 | + case _: This | _: Ident => |
| 186 | + replacement.get(tree.tpe) match { |
| 187 | + case Some(t) => ref(t) |
| 188 | + case None => tree |
| 189 | + } |
| 190 | + case _ => tree |
| 191 | + } |
| 192 | + |
| 193 | + val inliner = new TreeTypeMap(typeMap, treeMap, meth :: Nil, ctx.owner :: Nil) |
| 194 | + |
| 195 | + val result = inliner(Block(outerBindings.toList, rhs)).withPos(call.pos) |
| 196 | + |
| 197 | + inlining.println(i"inlining $call\n --> \n$result") |
| 198 | + result |
| 199 | + } |
| 200 | +} |
0 commit comments