diff options
author | Martin Odersky <odersky@gmail.com> | 2016-09-08 20:11:45 +0200 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2016-10-02 16:11:21 +0200 |
commit | ae67c35263287cd4cd987d4221cb030004f548e6 (patch) | |
tree | 923c394d62ae74fdc9124171d41046657022dd96 /src | |
parent | bf3345a9d25e464975dd5000186c766de842f020 (diff) | |
download | dotty-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.scala | 14 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/NameOps.scala | 3 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/StdNames.scala | 1 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/SymDenotations.scala | 8 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Types.scala | 18 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/tasty/TreePickler.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/reporting/Reporter.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Inliner.scala | 148 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Namer.scala | 9 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Typer.scala | 10 | ||||
-rw-r--r-- | src/dotty/tools/dotc/util/Stats.scala | 2 |
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 |