diff options
Diffstat (limited to 'src/dotty')
-rw-r--r-- | src/dotty/annotation/inline.scala | 8 | ||||
-rw-r--r-- | src/dotty/tools/dotc/config/Printers.scala | 1 | ||||
-rw-r--r-- | src/dotty/tools/dotc/config/ScalaSettings.scala | 1 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Contexts.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Definitions.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/NameOps.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/SymDenotations.scala | 17 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/PostTyper.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Inliner.scala | 200 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Namer.scala | 7 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/ReTyper.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Typer.scala | 18 | ||||
-rw-r--r-- | src/dotty/tools/dotc/util/SourceFile.scala | 1 |
13 files changed, 257 insertions, 12 deletions
diff --git a/src/dotty/annotation/inline.scala b/src/dotty/annotation/inline.scala new file mode 100644 index 000000000..ff4ca08b6 --- /dev/null +++ b/src/dotty/annotation/inline.scala @@ -0,0 +1,8 @@ +package dotty.annotation + +import scala.annotation.Annotation + +/** Unlike scala.inline, this one forces inlining in the Typer + * Should be replaced by keyword when we switch over completely to dotty + */ +class inline extends Annotation
\ No newline at end of file diff --git a/src/dotty/tools/dotc/config/Printers.scala b/src/dotty/tools/dotc/config/Printers.scala index 322bc82d9..4168cf5a6 100644 --- a/src/dotty/tools/dotc/config/Printers.scala +++ b/src/dotty/tools/dotc/config/Printers.scala @@ -30,4 +30,5 @@ object Printers { val completions: Printer = noPrinter val cyclicErrors: Printer = noPrinter val pickling: Printer = noPrinter + val inlining: Printer = new Printer } diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala index c090a5515..d05ae0803 100644 --- a/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -67,6 +67,7 @@ class ScalaSettings extends Settings.SettingGroup { val genPhaseGraph = StringSetting("-Xgenerate-phase-graph", "file", "Generate the phase graphs (outputs .dot files) to fileX.dot.", "") val XlogImplicits = BooleanSetting("-Xlog-implicits", "Show more detail on why some implicits are not applicable.") val XminImplicitSearchDepth = IntSetting("-Xmin-implicit-search-depth", "Set number of levels of implicit searches undertaken before checking for divergence.", 5) + val xmaxInlines = IntSetting("-Xmax-inlines", "Maximal number of successive inlines", 70) val logImplicitConv = BooleanSetting("-Xlog-implicit-conversions", "Print a message whenever an implicit conversion is inserted.") val logReflectiveCalls = BooleanSetting("-Xlog-reflective-calls", "Print a message when a reflective method call is generated") val logFreeTerms = BooleanSetting("-Xlog-free-terms", "Print a message when reification creates a free term.") diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index cd76fe88b..d8beb4b5c 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -667,6 +667,10 @@ object Contexts { */ private[dotty] var unsafeNonvariant: RunId = NoRunId + // Typer state + + private[dotty] var inlineCount = 0 + // Phases state private[core] var phasesPlan: List[List[Phase]] = _ diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index cb83fda04..c9a3ef4de 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -466,6 +466,8 @@ class Definitions { def DeprecatedAnnot(implicit ctx: Context) = DeprecatedAnnotType.symbol.asClass lazy val ImplicitNotFoundAnnotType = ctx.requiredClassRef("scala.annotation.implicitNotFound") def ImplicitNotFoundAnnot(implicit ctx: Context) = ImplicitNotFoundAnnotType.symbol.asClass + lazy val InlineAnnotType = ctx.requiredClassRef("dotty.annotation.inline") + def InlineAnnot(implicit ctx: Context) = InlineAnnotType.symbol.asClass lazy val InvariantBetweenAnnotType = ctx.requiredClassRef("dotty.annotation.internal.InvariantBetween") def InvariantBetweenAnnot(implicit ctx: Context) = InvariantBetweenAnnotType.symbol.asClass lazy val MigrationAnnotType = ctx.requiredClassRef("scala.annotation.migration") diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index f5e0eb8cd..ea255e5b3 100644 --- a/src/dotty/tools/dotc/core/NameOps.scala +++ b/src/dotty/tools/dotc/core/NameOps.scala @@ -166,7 +166,7 @@ object NameOps { // Hack to make super accessors from traits work. They would otherwise fail because of #765 // TODO: drop this once we have more robust name handling - if (name.slice(idx - FalseSuperLength, idx) == FalseSuper) + if (idx > FalseSuperLength && name.slice(idx - FalseSuperLength, idx) == FalseSuper) idx -= FalseSuper.length if (idx < 0) name else (name drop (idx + nme.EXPAND_SEPARATOR.length)).asInstanceOf[N] diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index ab45550a4..160d3bc30 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -670,9 +670,9 @@ object SymDenotations { val cls = owner.enclosingSubClass if (!cls.exists) fail( - i""" Access to protected $this not permitted because - | enclosing ${ctx.owner.enclosingClass.showLocated} is not a subclass of - | ${owner.showLocated} where target is defined""") + i""" + | Access to protected $this not permitted because enclosing ${ctx.owner.enclosingClass.showLocated} + | is not a subclass of ${owner.showLocated} where target is defined""") else if ( !( isType // allow accesses to types from arbitrary subclasses fixes #4737 || pre.baseTypeRef(cls).exists // ??? why not use derivesFrom ??? @@ -680,9 +680,9 @@ object SymDenotations { || (owner is ModuleClass) // don't perform this check for static members )) fail( - i""" Access to protected ${symbol.show} not permitted because - | prefix type ${pre.widen.show} does not conform to - | ${cls.showLocated} where the access takes place""") + i""" + | Access to protected ${symbol.show} not permitted because prefix type ${pre.widen.show} + | does not conform to ${cls.showLocated} where the access takes place""") else true } @@ -744,6 +744,11 @@ object SymDenotations { // def isOverridable: Boolean = !!! need to enforce that classes cannot be redefined def isSkolem: Boolean = name == nme.SKOLEM + def isInlineMethod(implicit ctx: Context): Boolean = + is(Method, butNot = Accessor) && + !isCompleting && // don't force method type; recursive inlines are ignored anyway. + hasAnnotation(defn.InlineAnnot) + // ------ access to related symbols --------------------------------- /* Modules and module classes are represented as follows: diff --git a/src/dotty/tools/dotc/transform/PostTyper.scala b/src/dotty/tools/dotc/transform/PostTyper.scala index 6af225035..51851a589 100644 --- a/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/src/dotty/tools/dotc/transform/PostTyper.scala @@ -207,6 +207,10 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran } case tree: Select => transformSelect(paramFwd.adaptRef(fixSignature(tree)), Nil) + case tree: Super => + if (ctx.owner.enclosingMethod.isInlineMethod) + ctx.error(em"super not allowed in inline ${ctx.owner}", tree.pos) + super.transform(tree) case tree: TypeApply => val tree1 @ TypeApply(fn, args) = normalizeTypeArgs(tree) Checking.checkBounds(args, fn.tpe.widen.asInstanceOf[PolyType]) diff --git a/src/dotty/tools/dotc/typer/Inliner.scala b/src/dotty/tools/dotc/typer/Inliner.scala new file mode 100644 index 000000000..852689a75 --- /dev/null +++ b/src/dotty/tools/dotc/typer/Inliner.scala @@ -0,0 +1,200 @@ +package dotty.tools +package dotc +package typer + +import dotty.tools.dotc.ast.Trees.NamedArg +import dotty.tools.dotc.ast.{Trees, untpd, tpd, TreeTypeMap} +import Trees._ +import core._ +import Flags._ +import Symbols._ +import Types._ +import Decorators._ +import StdNames.nme +import Contexts.Context +import Names.Name +import SymDenotations.SymDenotation +import Annotations.Annotation +import transform.ExplicitOuter +import config.Printers.inlining +import ErrorReporting.errorTree +import util.Attachment +import collection.mutable + +object Inliner { + import tpd._ + + private class InlinedBody(tree: => Tree) { + lazy val body = tree + } + + private val InlinedBody = new Attachment.Key[InlinedBody] + + def attachBody(inlineAnnot: Annotation, tree: => Tree)(implicit ctx: Context): Unit = + inlineAnnot.tree.putAttachment(InlinedBody, new InlinedBody(tree)) + + def inlinedBody(sym: SymDenotation)(implicit ctx: Context): Tree = + sym.getAnnotation(defn.InlineAnnot).get.tree + .attachment(InlinedBody).body + + private class Typer extends ReTyper { + override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = { + val acc = tree.symbol + super.typedSelect(tree, pt) match { + case res @ Select(qual, name) => + if (name.endsWith(nme.OUTER)) { + val outerAcc = tree.symbol + println(i"selecting $tree / ${acc} / ${qual.tpe.normalizedPrefix}") + res.withType(qual.tpe.widen.normalizedPrefix) + } + else { + ensureAccessible(res.tpe, qual.isInstanceOf[Super], tree.pos) + res + } + case res => res + } + } + } + + def inlineCall(tree: Tree, pt: Type)(implicit ctx: Context): Tree = { + if (ctx.inlineCount < ctx.settings.xmaxInlines.value) { + ctx.inlineCount += 1 + val rhs = inlinedBody(tree.symbol) + val inlined = new Inliner(tree, rhs).inlined + try new Typer().typedUnadapted(inlined, pt) + finally ctx.inlineCount -= 1 + } else errorTree(tree, + i"""Maximal number of successive inlines (${ctx.settings.xmaxInlines.value}) exceeded, + | Maybe this is caused by a recursive inline method? + | You can use -Xmax:inlines to change the limit.""") + } +} + +class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) { + import tpd._ + + private val meth = call.symbol + + 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 lazy val prefix = methPart match { + case Select(qual, _) => qual + case _ => tpd.This(ctx.owner.enclosingClass.asClass) + } + + private val replacement = new mutable.HashMap[Type, NamedType] + + private val paramBindings = paramBindingsOf(meth.info, targs, argss) + + private def paramBindingsOf(tp: Type, targs: List[Tree], argss: List[List[Tree]]): List[MemberDef] = tp match { + case tp: PolyType => + val bindings = + (tp.paramNames, targs).zipped.map { (name, arg) => + val tparam = newSym(name, EmptyFlags, TypeAlias(arg.tpe.stripTypeVar)).asType + TypeDef(tparam) + } + bindings ::: paramBindingsOf(tp.resultType, Nil, argss) + case tp: MethodType => + val bindings = + (tp.paramNames, tp.paramTypes, argss.head).zipped.map { (name, paramtp, arg) => + def isByName = paramtp.dealias.isInstanceOf[ExprType] + val (paramFlags, paramType) = + if (isByName) (Method, ExprType(arg.tpe)) else (EmptyFlags, arg.tpe) + val vparam = newSym(name, paramFlags, paramType).asTerm + if (isByName) DefDef(vparam, arg) else ValDef(vparam, arg) + } + bindings ::: paramBindingsOf(tp.resultType, targs, argss.tail) + case _ => + assert(targs.isEmpty) + assert(argss.isEmpty) + Nil + } + + private def newSym(name: Name, flags: FlagSet, info: Type): Symbol = + ctx.newSymbol(ctx.owner, name, flags, info, coord = call.pos) + + private def registerType(tpe: Type): Unit = + if (!replacement.contains(tpe)) tpe match { + case tpe: ThisType => + if (!ctx.owner.isContainedIn(tpe.cls) && !tpe.cls.is(Package)) + if (tpe.cls.isStaticOwner) + replacement(tpe) = tpe.cls.sourceModule.termRef + else { + def outerDistance(cls: Symbol): Int = { + assert(cls.exists, i"not encl: ${meth.owner.enclosingClass} ${tpe.cls}") + if (tpe.cls eq cls) 0 + else outerDistance(cls.owner.enclosingClass) + 1 + } + val n = outerDistance(meth.owner) + replacement(tpe) = newSym(nme.SELF ++ n.toString, EmptyFlags, tpe.widen).termRef + } + case tpe: NamedType if tpe.symbol.is(Param) && tpe.symbol.owner == meth => + val Some(binding) = paramBindings.find(_.name == tpe.name) + replacement(tpe) = + if (tpe.name.isTypeName) binding.symbol.typeRef else binding.symbol.termRef + case _ => + } + + private def registerLeaf(tree: Tree): Unit = tree match { + case _: This | _: Ident => registerType(tree.tpe) + case _ => + } + + private def outerLevel(sym: Symbol) = sym.name.drop(nme.SELF.length).toString.toInt + + val inlined = { + rhs.foreachSubTree(registerLeaf) + + val accessedSelfSyms = + (for ((tp: ThisType, ref) <- replacement) yield ref.symbol.asTerm).toSeq.sortBy(outerLevel) + + val outerBindings = new mutable.ListBuffer[MemberDef] + for (selfSym <- accessedSelfSyms) { + val rhs = + if (outerBindings.isEmpty) prefix + else { + val lastSelf = outerBindings.last.symbol + val outerDelta = outerLevel(selfSym) - outerLevel(lastSelf) + def outerSelect(ref: Tree, dummy: Int): Tree = ??? + //ref.select(ExplicitOuter.outerAccessorTBD(ref.tpe.widen.classSymbol.asClass)) + (ref(lastSelf) /: (0 until outerDelta))(outerSelect) + } + outerBindings += ValDef(selfSym, rhs.ensureConforms(selfSym.info)) + } + outerBindings ++= paramBindings + + val typeMap = new TypeMap { + def apply(t: Type) = t match { + case _: SingletonType => replacement.getOrElse(t, t) + case _ => mapOver(t) + } + } + + def treeMap(tree: Tree) = tree match { + case _: This | _: Ident => + replacement.get(tree.tpe) match { + case Some(t) => ref(t) + case None => tree + } + case _ => tree + } + + val inliner = new TreeTypeMap(typeMap, treeMap, meth :: Nil, ctx.owner :: Nil) + + val result = inliner(Block(outerBindings.toList, rhs)).withPos(call.pos) + + inlining.println(i"inlining $call\n --> \n$result") + result + } +} diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 6eca9be41..06e798bdb 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -566,10 +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) } case _ => } + private def addInlineInfo(inlineAnnot: Annotation, original: untpd.Tree) = original match { + case original: untpd.DefDef => + Inliner.attachBody(inlineAnnot, typedAheadExpr(original).asInstanceOf[tpd.DefDef].rhs) + case _ => + } + /** Intentionally left without `implicit ctx` parameter. We need * to pick up the context at the point where the completer was created. */ diff --git a/src/dotty/tools/dotc/typer/ReTyper.scala b/src/dotty/tools/dotc/typer/ReTyper.scala index 9750957bf..03b415a6f 100644 --- a/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/src/dotty/tools/dotc/typer/ReTyper.scala @@ -10,7 +10,7 @@ import typer.ProtoTypes._ import ast.{tpd, untpd} import ast.Trees._ import scala.util.control.NonFatal -import config.Printers +import config.Printers.typr /** A version of Typer that keeps all symbols defined and referenced in a * previously typed tree. @@ -87,7 +87,7 @@ class ReTyper extends Typer { try super.typedUnadapted(tree, pt) catch { case NonFatal(ex) => - println(i"exception while typing $tree of class ${tree.getClass} # ${tree.uniqueId}") + typr.println(i"exception while typing $tree of class ${tree.getClass} # ${tree.uniqueId}") throw ex } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 562af75f6..007eaa468 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -885,12 +885,15 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit (EmptyTree, WildcardType) } else if (owner != cx.outer.owner && owner.isRealMethod) { - if (owner.isCompleted) { + if (owner.isInlineMethod) + (EmptyTree, errorType(em"no explicit return allowed from inline $owner", tree.pos)) + else if (!owner.isCompleted) + (EmptyTree, errorType(em"$owner has return statement; needs result type", tree.pos)) + else { val from = Ident(TermRef(NoPrefix, owner.asTerm)) val proto = returnProto(owner, cx.scope) (from, proto) } - else (EmptyTree, errorType(em"$owner has return statement; needs result type", tree.pos)) } else enclMethInfo(cx.outer) } @@ -1154,6 +1157,13 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit rhsCtx.gadt.setBounds(tdef.symbol, TypeAlias(tparam.typeRef))) } val rhs1 = typedExpr(ddef.rhs, tpt1.tpe)(rhsCtx) + + // Overwrite inline body to make sure it is not evaluated twice + sym.getAnnotation(defn.InlineAnnot) match { + case Some(ann) => Inliner.attachBody(ann, rhs1) + case _ => + } + if (sym.isAnonymousFunction) { // If we define an anonymous function, make sure the return type does not // refer to parameters. This is necessary because closure types are @@ -1773,7 +1783,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit tree } else if (tree.tpe <:< pt) - if (ctx.typeComparer.GADTused && pt.isValueType) + if (tree.symbol.isInlineMethod && !ctx.owner.ownersIterator.exists(_.isInlineMethod)) + adapt(Inliner.inlineCall(tree, pt), pt) + else if (ctx.typeComparer.GADTused && pt.isValueType) // Insert an explicit cast, so that -Ycheck in later phases succeeds. // I suspect, but am not 100% sure that this might affect inferred types, // if the expected type is a supertype of the GADT bound. It would be good to come diff --git a/src/dotty/tools/dotc/util/SourceFile.scala b/src/dotty/tools/dotc/util/SourceFile.scala index 6b547203e..1d71552c0 100644 --- a/src/dotty/tools/dotc/util/SourceFile.scala +++ b/src/dotty/tools/dotc/util/SourceFile.scala @@ -139,5 +139,6 @@ case class SourceFile(file: AbstractFile, content: Array[Char]) extends interfac @sharable object NoSource extends SourceFile("<no source>", Nil) { override def exists = false + override def atPos(pos: Position): SourcePosition = NoSourcePosition } |