Skip to content

Commit

Permalink
Prepare to have both inlined and transparent methods
Browse files Browse the repository at this point in the history
All necessary changes except for those in the Inliner itself.
  • Loading branch information
odersky committed Jun 3, 2018
1 parent 62a5707 commit f25d48d
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 30 deletions.
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
/** An extractor for def of a closure contained the block of the closure. */
object closureDef {
def unapply(tree: Tree): Option[DefDef] = tree match {
case Block(Nil, expr) => unapply(expr)
case Block((meth @ DefDef(nme.ANON_FUN, _, _, _, _)) :: Nil, closure: Closure) =>
Some(meth)
case _ => None
Expand Down
6 changes: 4 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ class TreeTypeMap(
val (tmap2, vparamss1) = tmap1.transformVParamss(vparamss)
val res = cpy.DefDef(ddef)(name, tparams1, vparamss1, tmap2.transform(tpt), tmap2.transform(ddef.rhs))
res.symbol.transformAnnotations {
case ann: BodyAnnotation => ann.derivedAnnotation(res.rhs)
case ann: BodyAnnotation =>
if (res.symbol.isTransparentMethod) ann.derivedAnnotation(transform(ann.tree))
else ann.derivedAnnotation(res.rhs)
case ann => ann
}
res
Expand Down Expand Up @@ -126,7 +128,7 @@ class TreeTypeMap(
override def transformStats(trees: List[tpd.Tree])(implicit ctx: Context) =
transformDefs(trees)._2

private def transformDefs[TT <: tpd.Tree](trees: List[TT])(implicit ctx: Context): (TreeTypeMap, List[TT]) = {
def transformDefs[TT <: tpd.Tree](trees: List[TT])(implicit ctx: Context): (TreeTypeMap, List[TT]) = {
val tmap = withMappedSyms(tpd.localSyms(trees))
(tmap, tmap.transformSub(trees))
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import scala.io.Codec
/** Some creators for typed trees */
object tpd extends Trees.Instance[Type] with TypedTreeInfo {

case class UntypedSplice(splice: untpd.Tree) extends Tree

private def ta(implicit ctx: Context) = ctx.typeAssigner

def Ident(tp: NamedType)(implicit ctx: Context): Ident =
Expand Down Expand Up @@ -471,8 +473,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
} else foldOver(sym, tree)
}

case class UntypedSplice(splice: untpd.Tree) extends Tree

override val cpy: TypedTreeCopier = // Type ascription needed to pick up any new members in TreeCopier (currently there are none)
new TypedTreeCopier

Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -637,8 +637,8 @@ class TreePickler(pickler: TastyPickler) {
// a different toplevel class, it is impossible to pickle a reference to it.
// Such annotations will be reconstituted when unpickling the child class.
// See tests/pickling/i3149.scala
case _ => ann.symbol == defn.BodyAnnot
// inline bodies are reconstituted automatically when unpickling
case _ => ann.symbol == defn.BodyAnnot && owner.isInlinedMethod
// bodies of inlined (but not transparent) methods are reconstituted automatically when unpickling
}

def pickleAnnotation(owner: Symbol, ann: Annotation)(implicit ctx: Context) =
Expand Down
9 changes: 6 additions & 3 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -634,9 +634,12 @@ class TreeUnpickler(reader: TastyReader,
val end = readEnd()
val tp = readType()
val lazyAnnotTree = readLater(end, rdr => ctx => rdr.readTerm()(ctx))
Annotation.deferredSymAndTree(
implicit ctx => tp.typeSymbol,
implicit ctx => lazyAnnotTree.complete)
if (tp.isRef(defn.BodyAnnot))
LazyBodyAnnotation(implicit ctx => lazyAnnotTree.complete)
else
Annotation.deferredSymAndTree(
implicit ctx => tp.typeSymbol,
implicit ctx => lazyAnnotTree.complete)
}

/** Create symbols for the definitions in the statement sequence between
Expand Down
29 changes: 17 additions & 12 deletions compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1103,19 +1103,24 @@ trait Implicits { self: Typer =>
val eligible =
if (contextual) ctx.implicits.eligible(wildProto)
else implicitScope(wildProto).eligible
searchImplicits(eligible, contextual).recoverWith {
failure => failure.reason match {
case _: AmbiguousImplicits => failure
case reason =>
if (contextual)
bestImplicit(contextual = false).recoverWith {
failure2 => reason match {
case (_: DivergingImplicit) | (_: ShadowedImplicit) => failure
case _ => failure2
searchImplicits(eligible, contextual) match {
case result: SearchSuccess =>
if (contextual && ctx.mode.is(Mode.TransparentBody))
Inliner.markContextualImplicit(result.tree)
result
case failure: SearchFailure =>
failure.reason match {
case _: AmbiguousImplicits => failure
case reason =>
if (contextual)
bestImplicit(contextual = false).recoverWith {
failure2 => reason match {
case (_: DivergingImplicit) | (_: ShadowedImplicit) => failure
case _ => failure2
}
}
}
else failure
}
else failure
}
}
}

Expand Down
24 changes: 17 additions & 7 deletions compiler/src/dotty/tools/dotc/typer/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,20 @@ import collection.mutable
import transform.TypeUtils._
import reporting.trace
import util.Positions.Position
import util.Property

object Inliner {
import tpd._

/** Marks an implicit reference found in the context (as opposed to the implicit scope)
* from an inlineable body. Such references will be carried along with the body to
* the expansion site.
*/
private val ContextualImplicit = new Property.StickyKey[Unit]

def markContextualImplicit(tree: Tree)(implicit ctx: Context): Unit =
methPart(tree).putAttachment(ContextualImplicit, ())

class InlineAccessors extends AccessProxies {

/** A tree map which inserts accessors for all non-public term members accessed
Expand Down Expand Up @@ -104,7 +114,7 @@ object Inliner {
* to have the inlined method as owner.
*/
def registerInlineInfo(
sym: SymDenotation, treeExpr: Context => Tree)(implicit ctx: Context): Unit = {
sym: SymDenotation, originalBody: untpd.Tree, treeExpr: Context => Tree)(implicit ctx: Context): Unit = {
sym.unforcedAnnotation(defn.BodyAnnot) match {
case Some(ann: ConcreteBodyAnnotation) =>
case Some(ann: LazyBodyAnnotation) if ann.isEvaluated =>
Expand Down Expand Up @@ -173,10 +183,10 @@ object Inliner {

/** Produces an inlined version of `call` via its `inlined` method.
*
* @param call The original call to a `@inline` method
* @param rhs The body of the inline method that replaces the call.
* @param call the original call to a `@inline` method
* @param rhsToInline the body of the inline method that replaces the call.
*/
class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
import tpd._
import Inliner._

Expand All @@ -200,7 +210,7 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
*/
private val paramProxy = new mutable.HashMap[Type, Type]

/** A map from the classes of (direct and outer) this references in `rhs`
/** A map from the classes of (direct and outer) this references in `rhsToInline`
* to references of their proxies.
* Note that we can't index by the ThisType itself since there are several
* possible forms to express what is logicaly the same ThisType. E.g.
Expand Down Expand Up @@ -315,7 +325,7 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
if (!isIdempotentExpr(prefix)) registerType(meth.owner.thisType)

// Register types of all leaves of inlined body so that the `paramProxy` and `thisProxy` maps are defined.
rhs.foreachSubTree(registerLeaf)
rhsToInline.foreachSubTree(registerLeaf)

// The class that the this-proxy `selfSym` represents
def classOf(selfSym: Symbol) = selfSym.info.widen.classSymbol
Expand Down Expand Up @@ -387,7 +397,7 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
// the owner from the inlined method to the current owner.
val inliner = new TreeTypeMap(typeMap, treeMap, meth :: Nil, ctx.owner :: Nil)(inlineCtx)

val expansion = inliner(rhs.withPos(call.pos))
val expansion = inliner(rhsToInline.withPos(call.pos))
trace(i"inlining $call\n, BINDINGS =\n${bindingsBuf.toList}%\n%\nEXPANSION =\n$expansion", inlining, show = true) {

// The final expansion runs a typing pass over the inlined tree. See InlineTyper for details.
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,7 @@ class Namer { typer: Typer =>
case original: untpd.DefDef if denot.isInlineableMethod =>
Inliner.registerInlineInfo(
denot,
original.rhs,
implicit ctx => typedAheadExpr(original).asInstanceOf[tpd.DefDef].rhs
)(localContext(denot.symbol))
case _ =>
Expand Down
14 changes: 12 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1420,7 +1420,7 @@ class Typer extends Namer
val rhs1 = normalizeErasedRhs(typedExpr(ddef.rhs, tpt1.tpe)(rhsCtx), sym)

// Overwrite inline body to make sure it is not evaluated twice
if (sym.isInlineableMethod) Inliner.registerInlineInfo(sym, _ => rhs1)
if (sym.isInlineableMethod) Inliner.registerInlineInfo(sym, ddef.rhs, _ => rhs1)

if (sym.isConstructor && !sym.isPrimaryConstructor)
for (param <- tparams1 ::: vparamss1.flatten)
Expand Down Expand Up @@ -1885,7 +1885,17 @@ class Typer extends Namer
case none =>
typed(mdef) match {
case mdef1: DefDef if Inliner.hasBodyToInline(mdef1.symbol) =>
buf += inlineExpansion(mdef1)
if (mdef1.symbol.isInlinedMethod) {
buf += inlineExpansion(mdef1)
// replace body with expansion, because it will be used as inlined body
// from separately compiled files - the original BodyAnnotation is not kept.
}
else {
assert(mdef.symbol.isTransparentMethod)
Inliner.bodyToInline(mdef1.symbol) // just make sure accessors are computed,
buf += mdef1 // but keep original definition, since inline-expanded code
// is pickled in this case.
}
case mdef1 =>
import untpd.modsDeco
mdef match {
Expand Down

0 comments on commit f25d48d

Please sign in to comment.