aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2016-09-08 20:11:45 +0200
committerMartin Odersky <odersky@gmail.com>2016-10-02 16:11:21 +0200
commitae67c35263287cd4cd987d4221cb030004f548e6 (patch)
tree923c394d62ae74fdc9124171d41046657022dd96 /src
parentbf3345a9d25e464975dd5000186c766de842f020 (diff)
downloaddotty-ae67c35263287cd4cd987d4221cb030004f548e6.tar.gz
dotty-ae67c35263287cd4cd987d4221cb030004f548e6.tar.bz2
dotty-ae67c35263287cd4cd987d4221cb030004f548e6.zip
Add accessors for non-public members accessed from inline methods
This makes existsing uses of inline mostly compile. Todo: Verify that stdlib can be compiled. Todo: Implement accessors for assignments to priavte variables Todo: Figure out what to do with accesses to private types.
Diffstat (limited to 'src')
-rw-r--r--src/dotty/tools/dotc/ast/TreeInfo.scala14
-rw-r--r--src/dotty/tools/dotc/core/NameOps.scala3
-rw-r--r--src/dotty/tools/dotc/core/StdNames.scala1
-rw-r--r--src/dotty/tools/dotc/core/SymDenotations.scala8
-rw-r--r--src/dotty/tools/dotc/core/Types.scala18
-rw-r--r--src/dotty/tools/dotc/core/tasty/TreePickler.scala2
-rw-r--r--src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala4
-rw-r--r--src/dotty/tools/dotc/reporting/Reporter.scala2
-rw-r--r--src/dotty/tools/dotc/typer/Inliner.scala148
-rw-r--r--src/dotty/tools/dotc/typer/Namer.scala9
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala10
-rw-r--r--src/dotty/tools/dotc/util/Stats.scala2
12 files changed, 187 insertions, 34 deletions
diff --git a/src/dotty/tools/dotc/ast/TreeInfo.scala b/src/dotty/tools/dotc/ast/TreeInfo.scala
index 725838ef6..0d7ebc5e1 100644
--- a/src/dotty/tools/dotc/ast/TreeInfo.scala
+++ b/src/dotty/tools/dotc/ast/TreeInfo.scala
@@ -435,6 +435,20 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
}
}
+ /** Decompose a call fn[targs](vargs_1)...(vargs_n)
+ * into its constituents (where targs, vargss may be empty)
+ */
+ 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)
+ }
+
/** The variables defined by a pattern, in reverse order of their appearance. */
def patVars(tree: Tree)(implicit ctx: Context): List[Symbol] = {
val acc = new TreeAccumulator[List[Symbol]] {
diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala
index ddb0421bb..48e823e81 100644
--- a/src/dotty/tools/dotc/core/NameOps.scala
+++ b/src/dotty/tools/dotc/core/NameOps.scala
@@ -85,6 +85,7 @@ object NameOps {
def isSelectorName = name.startsWith(" ") && name.tail.forall(_.isDigit)
def isLazyLocal = name.endsWith(nme.LAZY_LOCAL)
def isOuterSelect = name.endsWith(nme.OUTER_SELECT)
+ def isInlineAccessor = name.startsWith(nme.INLINE_ACCESSOR_PREFIX)
/** Is name a variable name? */
def isVariableName: Boolean = name.length > 0 && {
@@ -420,6 +421,8 @@ object NameOps {
assert(name.isLazyLocal)
name.dropRight(nme.LAZY_LOCAL.length)
}
+
+ def inlineAccessorName = nme.INLINE_ACCESSOR_PREFIX ++ name ++ "$"
}
private final val FalseSuper = "$$super".toTermName
diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala
index fc26e0536..9ef7caa68 100644
--- a/src/dotty/tools/dotc/core/StdNames.scala
+++ b/src/dotty/tools/dotc/core/StdNames.scala
@@ -100,6 +100,7 @@ object StdNames {
val EXPAND_SEPARATOR: N = "$$"
val IMPL_CLASS_SUFFIX: N = "$class"
val IMPORT: N = "<import>"
+ val INLINE_ACCESSOR_PREFIX = "$inlineAccessor$"
val INTERPRETER_IMPORT_WRAPPER: N = "$iw"
val INTERPRETER_LINE_PREFIX: N = "line"
val INTERPRETER_VAR_PREFIX: N = "res"
diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala
index 160d3bc30..977c76668 100644
--- a/src/dotty/tools/dotc/core/SymDenotations.scala
+++ b/src/dotty/tools/dotc/core/SymDenotations.scala
@@ -1526,7 +1526,13 @@ object SymDenotations {
/** Enter a symbol in given `scope` without potentially replacing the old copy. */
def enterNoReplace(sym: Symbol, scope: MutableScope)(implicit ctx: Context): Unit = {
- require((sym.denot.flagsUNSAFE is Private) || !(this is Frozen) || (scope ne this.unforcedDecls) || sym.hasAnnotation(defn.ScalaStaticAnnot))
+
+ require(
+ (sym.denot.flagsUNSAFE is Private) ||
+ !(this is Frozen) ||
+ (scope ne this.unforcedDecls) ||
+ sym.hasAnnotation(defn.ScalaStaticAnnot) ||
+ sym.name.isInlineAccessor)
scope.enter(sym)
if (myMemberFingerPrint != FingerPrint.unknown)
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index ac8a91868..0af673561 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -2555,6 +2555,24 @@ object Types {
x => paramBounds mapConserve (_.subst(this, x).bounds),
x => resType.subst(this, x))
+ /** Merge nested polytypes into one polytype. nested polytypes are normally not suported
+ * but can arise as temporary data structures.
+ */
+ def flatten(implicit ctx: Context): PolyType = resType match {
+ case that: PolyType =>
+ val shift = new TypeMap {
+ def apply(t: Type) = t match {
+ case PolyParam(`that`, n) => PolyParam(that, n + paramNames.length)
+ case t => mapOver(t)
+ }
+ }
+ PolyType(paramNames ++ that.paramNames)(
+ x => this.paramBounds.mapConserve(_.subst(this, x).bounds) ++
+ that.paramBounds.mapConserve(shift(_).subst(that, x).bounds),
+ x => shift(that.resultType).subst(that, x).subst(this, x))
+ case _ => this
+ }
+
override def toString = s"PolyType($paramNames, $paramBounds, $resType)"
override def computeHash = doHash(paramNames, resType, paramBounds)
diff --git a/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala
index dc37485f9..12ab050f6 100644
--- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala
+++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala
@@ -308,7 +308,7 @@ class TreePickler(pickler: TastyPickler) {
if (!tree.isEmpty) pickleTree(tree)
def pickleDef(tag: Int, sym: Symbol, tpt: Tree, rhs: Tree = EmptyTree, pickleParams: => Unit = ())(implicit ctx: Context) = {
- assert(symRefs(sym) == NoAddr)
+ assert(symRefs(sym) == NoAddr, sym)
registerDef(sym)
writeByte(tag)
withLength {
diff --git a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
index 60cad0445..4149f800c 100644
--- a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
+++ b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
@@ -490,8 +490,8 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle
}
else (annots.find(_.symbol == defn.InlineAnnot)) match {
case Some(inlineAnnot) =>
- Inliner.attachBody(inlineAnnot,
- forkAt(rhsStart).readTerm()(localContext(sym).addMode(Mode.ReadPositions)))
+ val inlineCtx = localContext(sym).addMode(Mode.ReadPositions)
+ Inliner.attachBody(inlineAnnot, implicit ctx => forkAt(rhsStart).readTerm())(inlineCtx)
case none =>
}
goto(start)
diff --git a/src/dotty/tools/dotc/reporting/Reporter.scala b/src/dotty/tools/dotc/reporting/Reporter.scala
index f3777473d..75113d823 100644
--- a/src/dotty/tools/dotc/reporting/Reporter.scala
+++ b/src/dotty/tools/dotc/reporting/Reporter.scala
@@ -154,7 +154,7 @@ trait Reporting { this: Context =>
if (printer eq config.Printers.noPrinter) op
else doTraceIndented[T](question, printer, show)(op)
- def doTraceIndented[T](question: => String, printer: Printers.Printer = Printers.default, show: Boolean = false)(op: => T): T = {
+ private def doTraceIndented[T](question: => String, printer: Printers.Printer = Printers.default, show: Boolean = false)(op: => T): T = {
def resStr(res: Any): String = res match {
case res: printing.Showable if show => res.show
case _ => String.valueOf(res)
diff --git a/src/dotty/tools/dotc/typer/Inliner.scala b/src/dotty/tools/dotc/typer/Inliner.scala
index 763d1ee94..286d971c6 100644
--- a/src/dotty/tools/dotc/typer/Inliner.scala
+++ b/src/dotty/tools/dotc/typer/Inliner.scala
@@ -14,6 +14,7 @@ import Constants._
import StdNames.nme
import Contexts.Context
import Names.Name
+import NameOps._
import SymDenotations.SymDenotation
import Annotations.Annotation
import transform.ExplicitOuter
@@ -22,30 +23,144 @@ import config.Printers.inlining
import ErrorReporting.errorTree
import util.{Property, SourceFile, NoSource}
import collection.mutable
+import transform.TypeUtils._
object Inliner {
import tpd._
- private class InlinedBody(tree: => Tree) {
- lazy val body = tree
+ /** An attachment for inline methods, which contains
+ *
+ * - the inlined body, as a typed tree
+ * -
+ *
+ */
+ private final class InlinedBody(treeExpr: Context => Tree, var inlineCtx: Context) {
+ private val inlineMethod = inlineCtx.owner
+ private val myAccessors = new mutable.ListBuffer[MemberDef]
+ private var myBody: Tree = _
+ private var evaluated = false
+
+ private def prepareForInline = new TreeMap {
+ def needsAccessor(sym: Symbol)(implicit ctx: Context) =
+ sym.is(AccessFlags) || sym.privateWithin.exists
+ def accessorName(implicit ctx: Context) =
+ ctx.freshNames.newName(inlineMethod.name.asTermName.inlineAccessorName.toString)
+ def accessorSymbol(tree: Tree, accessorInfo: Type)(implicit ctx: Context): Symbol =
+ ctx.newSymbol(
+ owner = inlineMethod.owner,
+ name = if (tree.isTerm) accessorName.toTermName else accessorName.toTypeName,
+ flags = if (tree.isTerm) Synthetic | Method else Synthetic,
+ info = accessorInfo,
+ coord = tree.pos).entered
+ override def transform(tree: Tree)(implicit ctx: Context): Tree = super.transform {
+ tree match {
+ case _: Apply | _: TypeApply | _: RefTree if needsAccessor(tree.symbol) =>
+ if (tree.isTerm) {
+ val (methPart, targs, argss) = decomposeCall(tree)
+ val (accessorDef, accessorRef) =
+ if (methPart.symbol.isStatic) {
+ // Easy case: Reference to a static symbol
+ val accessorType = methPart.tpe.widen.ensureMethodic
+ val accessor = accessorSymbol(tree, accessorType).asTerm
+ val accessorDef = polyDefDef(accessor, tps => argss =>
+ methPart.appliedToTypes(tps).appliedToArgss(argss))
+ val accessorRef = ref(accessor).appliedToTypeTrees(targs).appliedToArgss(argss)
+ (accessorDef, accessorRef)
+ }
+ else {
+ // Hard case: Reference needs to go via a dyanmic prefix
+ val qual = qualifier(methPart)
+ inlining.println(i"adding inline accessor for $tree -> (${qual.tpe}, $methPart: ${methPart.getClass}, [$targs%, %], ($argss%, %))")
+ val dealiasMap = new TypeMap {
+ def apply(t: Type) = mapOver(t.dealias)
+ }
+ val qualType = dealiasMap(qual.tpe.widen)
+ def addQualType(tp: Type): Type = tp match {
+ case tp: PolyType => tp.derivedPolyType(tp.paramNames, tp.paramBounds, addQualType(tp.resultType))
+ case tp: ExprType => addQualType(tp.resultType)
+ case tp => MethodType(qualType :: Nil, tp)
+ }
+ val localRefs = qualType.namedPartsWith(_.symbol.isContainedIn(inlineMethod)).toList
+ def abstractQualType(mtpe: Type): Type =
+ if (localRefs.isEmpty) mtpe
+ else PolyType.fromSymbols(localRefs.map(_.symbol), mtpe).asInstanceOf[PolyType].flatten
+ val accessorType = abstractQualType(addQualType(dealiasMap(methPart.tpe.widen)))
+ val accessor = accessorSymbol(tree, accessorType).asTerm
+ val accessorDef = polyDefDef(accessor, tps => argss =>
+ argss.head.head.select(methPart.symbol)
+ .appliedToTypes(tps.drop(localRefs.length))
+ .appliedToArgss(argss.tail))
+ val accessorRef = ref(accessor)
+ .appliedToTypeTrees(localRefs.map(TypeTree(_)) ++ targs)
+ .appliedToArgss((qual :: Nil) :: argss)
+ (accessorDef, accessorRef)
+ }
+ myAccessors += accessorDef
+ inlining.println(i"added inline accessor: $accessorDef")
+ accessorRef
+ } else {
+ // TODO: Handle references to non-public types.
+ // This is quite tricky, as such types can appear anywhere, including as parts
+ // of types of other things. For the moment we do nothing and complain
+ // at the implicit expansion site if there's a reference to an inaccessible type.
+ // Draft code (incomplete):
+ //
+ // val accessor = accessorSymbol(tree, TypeAlias(tree.tpe)).asType
+ // myAccessors += TypeDef(accessor)
+ // ref(accessor)
+ //
+ tree
+ }
+ case _ => tree
+ }
+ }
+ }
+
+ def isEvaluated = evaluated
+ private def ensureEvaluated()(implicit ctx: Context) =
+ if (!evaluated) {
+ evaluated = true
+ myBody = treeExpr(inlineCtx)
+ myBody = prepareForInline.transform(myBody)(inlineCtx)
+ inlining.println(i"inlinable body of ${inlineCtx.owner} = $myBody")
+ inlineCtx = null
+ }
+
+ def body(implicit ctx: Context): Tree = {
+ ensureEvaluated()
+ myBody
+ }
+
+ def accessors(implicit ctx: Context): List[MemberDef] = {
+ ensureEvaluated()
+ myAccessors.toList
+ }
}
private val InlinedBody = new Property.Key[InlinedBody] // to be used as attachment
private val InlinedCalls = new Property.Key[List[Tree]] // to be used in context
- def attachBody(inlineAnnot: Annotation, tree: => Tree)(implicit ctx: Context): Unit =
- inlineAnnot.tree.putAttachment(InlinedBody, new InlinedBody(tree))
+ def attachBody(inlineAnnot: Annotation, treeExpr: Context => Tree)(implicit ctx: Context): Unit =
+ inlineAnnot.tree.getAttachment(InlinedBody) match {
+ case Some(inlinedBody) if inlinedBody.isEvaluated => // keep existing attachment
+ case _ =>
+ if (!ctx.isAfterTyper)
+ inlineAnnot.tree.putAttachment(InlinedBody, new InlinedBody(treeExpr, ctx))
+ }
private def inlinedBodyAttachment(sym: SymDenotation)(implicit ctx: Context): Option[InlinedBody] =
sym.getAnnotation(defn.InlineAnnot).get.tree.getAttachment(InlinedBody)
def hasInlinedBody(sym: SymDenotation)(implicit ctx: Context): Boolean =
- inlinedBodyAttachment(sym).isDefined
+ sym.isInlineMethod && inlinedBodyAttachment(sym).isDefined
def inlinedBody(sym: SymDenotation)(implicit ctx: Context): Tree =
inlinedBodyAttachment(sym).get.body
+ def inlineAccessors(sym: SymDenotation)(implicit ctx: Context): List[MemberDef] =
+ inlinedBodyAttachment(sym).get.accessors
+
def inlineCall(tree: Tree, pt: Type)(implicit ctx: Context): Tree =
if (enclosingInlineds.length < ctx.settings.xmaxInlines.value)
new Inliner(tree, inlinedBody(tree.symbol)).inlined(pt)
@@ -72,33 +187,24 @@ object Inliner {
val file = call.symbol.sourceFile
if (file != null && file.exists) new SourceFile(file) else NoSource
}
+
+ private def qualifier(tree: Tree)(implicit ctx: Context) = tree match {
+ case Select(qual, _) => qual
+ case SelectFromTypeTree(qual, _) => qual
+ case _ => This(ctx.owner.enclosingClass.asClass)
+ }
}
class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
import tpd._
import Inliner._
- 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 val meth = methPart.symbol
+ private val prefix = qualifier(methPart)
for (targ <- targs) fullyDefinedType(targ.tpe, "inlined type argument", targ.pos)
- private val prefix = methPart match {
- case Select(qual, _) => qual
- case _ => tpd.This(ctx.owner.enclosingClass.asClass)
- }
-
private val thisProxy = new mutable.HashMap[Type, TermRef]
private val paramProxy = new mutable.HashMap[Type, Type]
private val paramBinding = new mutable.HashMap[Name, Type]
diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala
index 034a1ff50..ab16ad414 100644
--- a/src/dotty/tools/dotc/typer/Namer.scala
+++ b/src/dotty/tools/dotc/typer/Namer.scala
@@ -566,14 +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)
+ if (cls == defn.InlineAnnot) addInlineInfo(denot.symbol, ann, original)
}
case _ =>
}
- private def addInlineInfo(inlineAnnot: Annotation, original: untpd.Tree) = original match {
+ private def addInlineInfo(inlineMethod: Symbol, inlineAnnot: Annotation, original: untpd.Tree) = original match {
case original: untpd.DefDef =>
- Inliner.attachBody(inlineAnnot, typedAheadExpr(original).asInstanceOf[tpd.DefDef].rhs)
+ Inliner.attachBody(
+ inlineAnnot,
+ implicit ctx => typedAheadExpr(original).asInstanceOf[tpd.DefDef].rhs
+ )(localContext(inlineMethod))
case _ =>
}
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index fab1a1dd0..b7b61e8de 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -1169,7 +1169,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
// Overwrite inline body to make sure it is not evaluated twice
sym.getAnnotation(defn.InlineAnnot) match {
- case Some(ann) => Inliner.attachBody(ann, rhs1)
+ case Some(ann) => Inliner.attachBody(ann, ctx => rhs1)
case _ =>
}
@@ -1493,7 +1493,10 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
case Some(xtree) =>
traverse(xtree :: rest)
case none =>
- buf += typed(mdef)
+ val mdef1 = typed(mdef)
+ buf += mdef1
+ if (Inliner.hasInlinedBody(mdef1.symbol))
+ buf ++= Inliner.inlineAccessors(mdef1.symbol)
traverse(rest)
}
case Thicket(stats) :: rest =>
@@ -1793,8 +1796,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
tree
}
else if (tree.tpe <:< pt)
- if (tree.symbol.isInlineMethod &&
- Inliner.hasInlinedBody(tree.symbol) &&
+ if (Inliner.hasInlinedBody(tree.symbol) &&
!ctx.owner.ownersIterator.exists(_.isInlineMethod) &&
!ctx.settings.YnoInline.value &&
!ctx.isAfterTyper)
diff --git a/src/dotty/tools/dotc/util/Stats.scala b/src/dotty/tools/dotc/util/Stats.scala
index e06695dfb..b7e0996f5 100644
--- a/src/dotty/tools/dotc/util/Stats.scala
+++ b/src/dotty/tools/dotc/util/Stats.scala
@@ -24,7 +24,7 @@ import collection.mutable
def record(fn: String, n: Int = 1) =
if (enabled) doRecord(fn, n)
- def doRecord(fn: String, n: Int) =
+ private def doRecord(fn: String, n: Int) =
if (monitored) {
val name = if (fn.startsWith("member-")) "member" else fn
hits(name) += n