|
| 1 | +package dotty.tools.dotc |
| 2 | +package transform |
| 3 | + |
| 4 | +import TreeTransforms._ |
| 5 | +import core.DenotTransformers.IdentityDenotTransformer |
| 6 | +import core.Symbols._ |
| 7 | +import core.Contexts._ |
| 8 | +import core.Types._ |
| 9 | +import core.Flags._ |
| 10 | +import core.Decorators._ |
| 11 | +import core.StdNames.nme |
| 12 | +import core.Names._ |
| 13 | +import core.NameOps._ |
| 14 | +import ast.Trees._ |
| 15 | +import ast.tpd |
| 16 | +import collection.mutable |
| 17 | + |
| 18 | +/** This phase optimizes code using implicit function types, by applying two rewrite rules. |
| 19 | + * Let IF be the implicit function type |
| 20 | + * |
| 21 | + * implicit Us => R |
| 22 | + * |
| 23 | + * (1) A method definition |
| 24 | + * |
| 25 | + * def m(xs: Ts): IF = implicit (ys: Us) => E |
| 26 | + * |
| 27 | + * is expanded to two methods: |
| 28 | + * |
| 29 | + * def m(xs: Ts): IF = implicit (ys: Us) => m$direct(xs)(ys) |
| 30 | + * def m$direct(xs: Ts)(ys: Us): R = E |
| 31 | + * |
| 32 | + * (and equivalently for methods with type parameters or a different number of value parameter lists). |
| 33 | + * An abstract method definition |
| 34 | + * |
| 35 | + * def m(xs: Ts): IF |
| 36 | + * |
| 37 | + * is expanded to: |
| 38 | + * |
| 39 | + * def m(xs: Ts): IF |
| 40 | + * def m$direct(xs: Ts, ys: Us): R |
| 41 | + * |
| 42 | + * (2) A reference `qual.apply` where `qual` has implicit function type and |
| 43 | + * `qual` refers to a method `m` is rewritten to a reference to `m$direct`, |
| 44 | + * keeping the same type and value arguments as they are found in `qual`. |
| 45 | + */ |
| 46 | +class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisTransform => |
| 47 | + import tpd._ |
| 48 | + |
| 49 | + override def phaseName: String = "shortcutImplicits" |
| 50 | + val treeTransform = new Transform |
| 51 | + |
| 52 | + class Transform extends TreeTransform { |
| 53 | + def phase = thisTransform |
| 54 | + |
| 55 | + override def prepareForUnit(tree: Tree)(implicit ctx: Context) = new Transform |
| 56 | + |
| 57 | + /** A map to cache mapping local methods to their direct counterparts. |
| 58 | + * A fresh map is created for each unit. |
| 59 | + */ |
| 60 | + private val directMeth = new mutable.HashMap[Symbol, Symbol] |
| 61 | + |
| 62 | + /** @pre The type's final result type is an implicit function type `implicit Ts => R`. |
| 63 | + * @return The type of the `apply` member of `implicit Ts => R`. |
| 64 | + */ |
| 65 | + private def directInfo(info: Type)(implicit ctx: Context): Type = info match { |
| 66 | + case info: PolyType => info.derivedPolyType(resType = directInfo(info.resultType)) |
| 67 | + case info: MethodType => info.derivedMethodType(resType = directInfo(info.resultType)) |
| 68 | + case info: ExprType => directInfo(info.resultType) |
| 69 | + case info => info.member(nme.apply).info |
| 70 | + } |
| 71 | + |
| 72 | + /** A new `m$direct` method to accompany the given method `m` */ |
| 73 | + private def newDirectMethod(sym: Symbol)(implicit ctx: Context): Symbol = |
| 74 | + sym.copy( |
| 75 | + name = sym.name.directName, |
| 76 | + flags = sym.flags | Synthetic, |
| 77 | + info = directInfo(sym.info)) |
| 78 | + |
| 79 | + /** The direct method `m$direct` that accompanies the given method `m`. |
| 80 | + * Create one if it does not exist already. |
| 81 | + */ |
| 82 | + private def directMethod(sym: Symbol)(implicit ctx: Context): Symbol = |
| 83 | + if (sym.owner.isClass) { |
| 84 | + val direct = sym.owner.info.member(sym.name.directName) |
| 85 | + .suchThat(_.info matches directInfo(sym.info)).symbol |
| 86 | + if (direct.maybeOwner == sym.owner) direct |
| 87 | + else newDirectMethod(sym).enteredAfter(thisTransform) |
| 88 | + } |
| 89 | + else directMeth.getOrElseUpdate(sym, newDirectMethod(sym)) |
| 90 | + |
| 91 | + |
| 92 | + /** Transform `qual.apply` occurrences according to rewrite rule (2) above */ |
| 93 | + override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) = |
| 94 | + if (tree.name == nme.apply && |
| 95 | + defn.isImplicitFunctionType(tree.qualifier.tpe.widen) && |
| 96 | + tree.qualifier.symbol.is(Method, butNot = Accessor)) { |
| 97 | + def directQual(tree: Tree): Tree = tree match { |
| 98 | + case Apply(fn, args) => cpy.Apply(tree)(directQual(fn), args) |
| 99 | + case TypeApply(fn, args) => cpy.TypeApply(tree)(directQual(fn), args) |
| 100 | + case Block(stats, expr) => cpy.Block(tree)(stats, directQual(expr)) |
| 101 | + case tree: RefTree => |
| 102 | + cpy.Ref(tree)(tree.name.directName) |
| 103 | + .withType(directMethod(tree.symbol).termRef) |
| 104 | + } |
| 105 | + directQual(tree.qualifier) |
| 106 | + } else tree |
| 107 | + |
| 108 | + /** Transform methods with implicit function type result according to rewrite rule (1) above */ |
| 109 | + override def transformDefDef(mdef: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { |
| 110 | + val original = mdef.symbol |
| 111 | + if (defn.isImplicitFunctionType(original.info.finalResultType)) { |
| 112 | + val direct = directMethod(original) |
| 113 | + |
| 114 | + def splitClosure(tree: Tree): (List[Type] => List[List[Tree]] => Tree, Tree) = tree match { |
| 115 | + case Block(Nil, expr) => splitClosure(expr) |
| 116 | + case Block((meth @ DefDef(nme.ANON_FUN, Nil, clparams :: Nil, _, _)) :: Nil, cl: Closure) => |
| 117 | + val tparamSyms = mdef.tparams.map(_.symbol) |
| 118 | + val vparamSymss = mdef.vparamss.map(_.map(_.symbol)) |
| 119 | + val clparamSyms = clparams.map(_.symbol) |
| 120 | + val remappedCore = (ts: List[Type]) => (prefss: List[List[Tree]]) => |
| 121 | + meth.rhs |
| 122 | + .subst(tparamSyms ::: (vparamSymss.flatten ++ clparamSyms), |
| 123 | + ts.map(_.typeSymbol) ::: prefss.flatten.map(_.symbol)) |
| 124 | + .changeOwnerAfter(original, direct, thisTransform) |
| 125 | + .changeOwnerAfter(meth.symbol, direct, thisTransform) |
| 126 | + val forwarder = ref(direct) |
| 127 | + .appliedToTypeTrees(tparamSyms.map(ref(_))) |
| 128 | + .appliedToArgss(vparamSymss.map(_.map(ref(_))) :+ clparamSyms.map(ref(_))) |
| 129 | + val fwdClosure = cpy.Block(tree)(cpy.DefDef(meth)(rhs = forwarder) :: Nil, cl) |
| 130 | + (remappedCore, fwdClosure) |
| 131 | + case EmptyTree => |
| 132 | + (_ => _ => EmptyTree, EmptyTree) |
| 133 | + } |
| 134 | + |
| 135 | + val (remappedCore, fwdClosure) = splitClosure(mdef.rhs) |
| 136 | + val originalDef = cpy.DefDef(mdef)(rhs = fwdClosure) |
| 137 | + val directDef = polyDefDef(direct.asTerm, remappedCore) |
| 138 | + Thicket(originalDef, directDef) |
| 139 | + } |
| 140 | + else mdef |
| 141 | + } |
| 142 | + } |
| 143 | +} |
0 commit comments