aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillaume Martres <smarter@ubuntu.com>2016-10-06 15:02:17 +0200
committerGitHub <noreply@github.com>2016-10-06 15:02:17 +0200
commit87a775724173bd803a0c4956408e61fd0d5812af (patch)
treec564a236f9247b085ed26c1fb007dad74ed049dd
parenta3064622e7ce4d73ddd91de0fc6bebfe0ec23ae9 (diff)
parente0a14e7939eda6a7f4914831975b2ac8877696f2 (diff)
downloaddotty-87a775724173bd803a0c4956408e61fd0d5812af.tar.gz
dotty-87a775724173bd803a0c4956408e61fd0d5812af.tar.bz2
dotty-87a775724173bd803a0c4956408e61fd0d5812af.zip
Merge pull request #1492 from dotty-staging/add-inline
Implement inline
-rw-r--r--bridge/src/sbt-test/source-dependencies/inline/A.scala3
-rw-r--r--bridge/src/sbt-test/source-dependencies/inline/C.scala5
-rw-r--r--bridge/src/sbt-test/source-dependencies/inline/changes/B1.scala4
-rw-r--r--bridge/src/sbt-test/source-dependencies/inline/changes/B2.scala4
-rw-r--r--bridge/src/sbt-test/source-dependencies/inline/changes/B3.scala4
-rw-r--r--bridge/src/sbt-test/source-dependencies/inline/project/DottyInjectedPlugin.scala17
-rw-r--r--bridge/src/sbt-test/source-dependencies/inline/test14
-rw-r--r--docs/SyntaxSummary.txt5
-rw-r--r--dottydoc/test/BaseTest.scala1
-rw-r--r--project/Build.scala3
-rw-r--r--src/dotty/annotation/internal/Body.scala8
-rw-r--r--src/dotty/annotation/internal/InlineParam.scala6
-rw-r--r--src/dotty/tools/dotc/Compiler.scala1
-rw-r--r--src/dotty/tools/dotc/FromTasty.scala2
-rw-r--r--src/dotty/tools/dotc/ast/Desugar.scala14
-rw-r--r--src/dotty/tools/dotc/ast/TreeInfo.scala44
-rw-r--r--src/dotty/tools/dotc/ast/TreeTypeMap.scala16
-rw-r--r--src/dotty/tools/dotc/ast/Trees.scala34
-rw-r--r--src/dotty/tools/dotc/ast/tpd.scala25
-rw-r--r--src/dotty/tools/dotc/ast/untpd.scala26
-rw-r--r--src/dotty/tools/dotc/config/Printers.scala1
-rw-r--r--src/dotty/tools/dotc/config/ScalaSettings.scala3
-rw-r--r--src/dotty/tools/dotc/core/Annotations.scala32
-rw-r--r--src/dotty/tools/dotc/core/Contexts.scala17
-rw-r--r--src/dotty/tools/dotc/core/Decorators.scala14
-rw-r--r--src/dotty/tools/dotc/core/Definitions.scala6
-rw-r--r--src/dotty/tools/dotc/core/Flags.scala17
-rw-r--r--src/dotty/tools/dotc/core/Mode.scala3
-rw-r--r--src/dotty/tools/dotc/core/NameOps.scala6
-rw-r--r--src/dotty/tools/dotc/core/StdNames.scala4
-rw-r--r--src/dotty/tools/dotc/core/SymDenotations.scala42
-rw-r--r--src/dotty/tools/dotc/core/SymbolLoaders.scala2
-rw-r--r--src/dotty/tools/dotc/core/TyperState.scala12
-rw-r--r--src/dotty/tools/dotc/core/Types.scala73
-rw-r--r--src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala23
-rw-r--r--src/dotty/tools/dotc/core/tasty/PositionPickler.scala92
-rw-r--r--src/dotty/tools/dotc/core/tasty/PositionUnpickler.scala40
-rw-r--r--src/dotty/tools/dotc/core/tasty/TastyFormat.scala23
-rw-r--r--src/dotty/tools/dotc/core/tasty/TastyPickler.scala1
-rw-r--r--src/dotty/tools/dotc/core/tasty/TastyPrinter.scala4
-rw-r--r--src/dotty/tools/dotc/core/tasty/TreePickler.scala24
-rw-r--r--src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala80
-rw-r--r--src/dotty/tools/dotc/parsing/Parsers.scala34
-rw-r--r--src/dotty/tools/dotc/parsing/Scanners.scala29
-rw-r--r--src/dotty/tools/dotc/parsing/Tokens.scala6
-rw-r--r--src/dotty/tools/dotc/printing/RefinedPrinter.scala12
-rw-r--r--src/dotty/tools/dotc/printing/SyntaxHighlighting.scala2
-rw-r--r--src/dotty/tools/dotc/reporting/ConsoleReporter.scala19
-rw-r--r--src/dotty/tools/dotc/reporting/Reporter.scala28
-rw-r--r--src/dotty/tools/dotc/sbt/ExtractAPI.scala20
-rw-r--r--src/dotty/tools/dotc/sbt/ExtractDependencies.scala4
-rw-r--r--src/dotty/tools/dotc/transform/DropInlined.scala15
-rw-r--r--src/dotty/tools/dotc/transform/ElimErasedValueType.scala3
-rw-r--r--src/dotty/tools/dotc/transform/ExplicitOuter.scala10
-rw-r--r--src/dotty/tools/dotc/transform/ExplicitSelf.scala5
-rw-r--r--src/dotty/tools/dotc/transform/FirstTransform.scala4
-rw-r--r--src/dotty/tools/dotc/transform/PatternMatcher.scala1
-rw-r--r--src/dotty/tools/dotc/transform/Pickler.scala5
-rw-r--r--src/dotty/tools/dotc/transform/PostTyper.scala4
-rw-r--r--src/dotty/tools/dotc/transform/SuperAccessors.scala2
-rw-r--r--src/dotty/tools/dotc/transform/TreeChecker.scala8
-rw-r--r--src/dotty/tools/dotc/transform/TreeTransform.scala31
-rw-r--r--src/dotty/tools/dotc/transform/patmat/Space.scala2
-rw-r--r--src/dotty/tools/dotc/typer/Applications.scala2
-rw-r--r--src/dotty/tools/dotc/typer/Checking.scala10
-rw-r--r--src/dotty/tools/dotc/typer/EtaExpansion.scala2
-rw-r--r--src/dotty/tools/dotc/typer/Inliner.scala521
-rw-r--r--src/dotty/tools/dotc/typer/Namer.scala96
-rw-r--r--src/dotty/tools/dotc/typer/ProtoTypes.scala2
-rw-r--r--src/dotty/tools/dotc/typer/ReTyper.scala17
-rw-r--r--src/dotty/tools/dotc/typer/TypeAssigner.scala8
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala85
-rw-r--r--src/dotty/tools/dotc/util/Attachment.scala4
-rw-r--r--src/dotty/tools/dotc/util/Property.scala10
-rw-r--r--src/dotty/tools/dotc/util/SourceFile.scala1
-rw-r--r--src/dotty/tools/dotc/util/SourcePosition.scala6
-rw-r--r--src/dotty/tools/dotc/util/Stats.scala17
-rw-r--r--test/dotc/tests.scala4
-rw-r--r--test/test/DottyBytecodeTest.scala2
-rw-r--r--test/test/InlineBytecodeTests.scala32
-rw-r--r--tests/neg/inlineAccess/C_1.scala8
-rw-r--r--tests/neg/inlineAccess/Test_2.scala7
-rw-r--r--tests/neg/inlinevals.scala24
-rw-r--r--tests/neg/power.scala15
-rw-r--r--tests/pos/rbtree.scala10
-rw-r--r--tests/run/inline.check9
-rw-r--r--tests/run/inline/Test_2.scala21
-rw-r--r--tests/run/inline/inlines_1.scala41
-rw-r--r--tests/run/inlineAccess/C_1.scala7
-rw-r--r--tests/run/inlineAccess/Test_2.scala7
-rw-r--r--tests/run/inlineArrowAssoc.scala24
-rw-r--r--tests/run/inlineForeach.check137
-rw-r--r--tests/run/inlineForeach.scala48
-rw-r--r--tests/run/inlinePower.check2
-rw-r--r--tests/run/inlinePower/Test_2.scala9
-rw-r--r--tests/run/inlinePower/power_1.scala12
-rw-r--r--tests/run/inlinePrivates.scala36
-rw-r--r--tests/run/inlinedAssign.scala24
-rw-r--r--tests/run/outerPatternMatch/Outer_1.scala6
-rw-r--r--tests/run/outerPatternMatch/Test_2.scala14
100 files changed, 1903 insertions, 376 deletions
diff --git a/bridge/src/sbt-test/source-dependencies/inline/A.scala b/bridge/src/sbt-test/source-dependencies/inline/A.scala
new file mode 100644
index 000000000..e889eef79
--- /dev/null
+++ b/bridge/src/sbt-test/source-dependencies/inline/A.scala
@@ -0,0 +1,3 @@
+object A {
+ def get: Int = 1
+}
diff --git a/bridge/src/sbt-test/source-dependencies/inline/C.scala b/bridge/src/sbt-test/source-dependencies/inline/C.scala
new file mode 100644
index 000000000..caeb61535
--- /dev/null
+++ b/bridge/src/sbt-test/source-dependencies/inline/C.scala
@@ -0,0 +1,5 @@
+object C {
+ def main(args: Array[String]): Unit = {
+ val i: Int = B.getInline
+ }
+}
diff --git a/bridge/src/sbt-test/source-dependencies/inline/changes/B1.scala b/bridge/src/sbt-test/source-dependencies/inline/changes/B1.scala
new file mode 100644
index 000000000..5685152b3
--- /dev/null
+++ b/bridge/src/sbt-test/source-dependencies/inline/changes/B1.scala
@@ -0,0 +1,4 @@
+object B {
+ @inline def getInline: Int =
+ A.get
+}
diff --git a/bridge/src/sbt-test/source-dependencies/inline/changes/B2.scala b/bridge/src/sbt-test/source-dependencies/inline/changes/B2.scala
new file mode 100644
index 000000000..1de104357
--- /dev/null
+++ b/bridge/src/sbt-test/source-dependencies/inline/changes/B2.scala
@@ -0,0 +1,4 @@
+object B {
+ @inline def getInline: Double =
+ A.get
+}
diff --git a/bridge/src/sbt-test/source-dependencies/inline/changes/B3.scala b/bridge/src/sbt-test/source-dependencies/inline/changes/B3.scala
new file mode 100644
index 000000000..991bd17b8
--- /dev/null
+++ b/bridge/src/sbt-test/source-dependencies/inline/changes/B3.scala
@@ -0,0 +1,4 @@
+object B {
+ @inline def getInline: Int =
+ sys.error("This is an expected failure when running C")
+}
diff --git a/bridge/src/sbt-test/source-dependencies/inline/project/DottyInjectedPlugin.scala b/bridge/src/sbt-test/source-dependencies/inline/project/DottyInjectedPlugin.scala
new file mode 100644
index 000000000..3433779b6
--- /dev/null
+++ b/bridge/src/sbt-test/source-dependencies/inline/project/DottyInjectedPlugin.scala
@@ -0,0 +1,17 @@
+import sbt._
+import Keys._
+
+object DottyInjectedPlugin extends AutoPlugin {
+ override def requires = plugins.JvmPlugin
+ override def trigger = allRequirements
+
+ override val projectSettings = Seq(
+ scalaVersion := "0.1-SNAPSHOT",
+ scalaOrganization := "ch.epfl.lamp",
+ scalacOptions += "-language:Scala2",
+ scalaBinaryVersion := "2.11",
+ autoScalaLibrary := false,
+ libraryDependencies ++= Seq("org.scala-lang" % "scala-library" % "2.11.5"),
+ scalaCompilerBridgeSource := ("ch.epfl.lamp" % "dotty-bridge" % "0.1.1-SNAPSHOT" % "component").sources()
+ )
+}
diff --git a/bridge/src/sbt-test/source-dependencies/inline/test b/bridge/src/sbt-test/source-dependencies/inline/test
new file mode 100644
index 000000000..56fdb0486
--- /dev/null
+++ b/bridge/src/sbt-test/source-dependencies/inline/test
@@ -0,0 +1,14 @@
+$ copy-file changes/B1.scala B.scala
+> compile
+
+$ copy-file changes/B2.scala B.scala
+# Compilation of C.scala should fail because B.getInline now has type Double instead of Int
+-> compile
+
+$ copy-file changes/B1.scala B.scala
+> run
+
+$ copy-file changes/B3.scala B.scala
+# The body of B.getInline was changed so C.scala should be recompiled
+# If it was recompiled, run should fail since B.getInline now throws an exception
+-> run
diff --git a/docs/SyntaxSummary.txt b/docs/SyntaxSummary.txt
index 6c83c71ab..519180775 100644
--- a/docs/SyntaxSummary.txt
+++ b/docs/SyntaxSummary.txt
@@ -231,14 +231,15 @@ grammar.
ClsParamClauses ::= {ClsParamClause} [[nl] `(' `implicit' ClsParams `)']
ClsParamClause ::= [nl] `(' [ClsParams] ')'
ClsParams ::= ClsParam {`' ClsParam}
- ClsParam ::= {Annotation} [{Modifier} (`val' | `var')] Param ValDef(mods, id, tpe, expr) -- point of mods on val/var
+ ClsParam ::= {Annotation}
+ [{Modifier} (`val' | `var') | `inline'] Param ValDef(mods, id, tpe, expr) -- point of mods on val/var
Param ::= id `:' ParamType [`=' Expr]
| INT
DefParamClauses ::= {DefParamClause} [[nl] `(' `implicit' DefParams `)']
DefParamClause ::= [nl] `(' [DefParams] ')'
DefParams ::= DefParam {`,' DefParam}
- DefParam ::= {Annotation} Param ValDef(mods, id, tpe, expr) -- point of mods at id.
+ DefParam ::= {Annotation} [`inline'] Param ValDef(mods, id, tpe, expr) -- point of mods at id.
Bindings ::= `(' Binding {`,' Binding `)' bindings
Binding ::= (id | `_') [`:' Type] ValDef(_, id, tpe, EmptyTree)
diff --git a/dottydoc/test/BaseTest.scala b/dottydoc/test/BaseTest.scala
index 808387a44..46a24c579 100644
--- a/dottydoc/test/BaseTest.scala
+++ b/dottydoc/test/BaseTest.scala
@@ -18,6 +18,7 @@ trait DottyTest {
val ctx = base.initialCtx.fresh
ctx.setSetting(ctx.settings.language, List("Scala2"))
ctx.setSetting(ctx.settings.YkeepComments, true)
+ ctx.setSetting(ctx.settings.YnoInline, true)
base.initialize()(ctx)
ctx
}
diff --git a/project/Build.scala b/project/Build.scala
index 7c57bd862..5ee082800 100644
--- a/project/Build.scala
+++ b/project/Build.scala
@@ -64,6 +64,9 @@ object DottyBuild extends Build {
lazy val dotty = project.in(file(".")).
dependsOn(`dotty-interfaces`).
settings(
+ // Disable scaladoc generation, makes publishLocal much faster
+ publishArtifact in packageDoc := false,
+
overrideScalaVersionSetting,
// set sources to src/, tests to test/ and resources to resources/
diff --git a/src/dotty/annotation/internal/Body.scala b/src/dotty/annotation/internal/Body.scala
new file mode 100644
index 000000000..7e26b02f2
--- /dev/null
+++ b/src/dotty/annotation/internal/Body.scala
@@ -0,0 +1,8 @@
+package dotty.annotation.internal
+
+import scala.annotation.Annotation
+
+/** The class associated with a `BodyAnnotation`, which indicates
+ * an inline method's right hand side
+ */
+final class Body() extends Annotation
diff --git a/src/dotty/annotation/internal/InlineParam.scala b/src/dotty/annotation/internal/InlineParam.scala
new file mode 100644
index 000000000..a144f9edb
--- /dev/null
+++ b/src/dotty/annotation/internal/InlineParam.scala
@@ -0,0 +1,6 @@
+package dotty.annotation.internal
+
+import scala.annotation.Annotation
+
+/** An annotation produced by Namer to indicate an inline parameter */
+final class InlineParam() extends Annotation
diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala
index 2120fa73e..178cba7c4 100644
--- a/src/dotty/tools/dotc/Compiler.scala
+++ b/src/dotty/tools/dotc/Compiler.scala
@@ -94,6 +94,7 @@ class Compiler {
new SelectStatic, // get rid of selects that would be compiled into GetStatic
new CollectEntryPoints, // Find classes with main methods
new CollectSuperCalls, // Find classes that are called with super
+ new DropInlined, // Drop Inlined nodes, since backend has no use for them
new MoveStatics, // Move static methods to companion classes
new LabelDefs), // Converts calls to labels to jumps
List(new GenSJSIR), // Generate .js code
diff --git a/src/dotty/tools/dotc/FromTasty.scala b/src/dotty/tools/dotc/FromTasty.scala
index 05e97f30a..b060a2054 100644
--- a/src/dotty/tools/dotc/FromTasty.scala
+++ b/src/dotty/tools/dotc/FromTasty.scala
@@ -86,7 +86,7 @@ object FromTasty extends Driver {
case info: ClassfileLoader =>
info.load(clsd) match {
case Some(unpickler: DottyUnpickler) =>
- val List(unpickled) = unpickler.body(readPositions = true)
+ val List(unpickled) = unpickler.body(ctx.addMode(Mode.ReadPositions))
val unit1 = new CompilationUnit(new SourceFile(clsd.symbol.sourceFile, Seq()))
unit1.tpdTree = unpickled
unit1.unpicklers += (clsd.classSymbol -> unpickler.unpickler)
diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala
index edd6da5c9..ecb6a3212 100644
--- a/src/dotty/tools/dotc/ast/Desugar.scala
+++ b/src/dotty/tools/dotc/ast/Desugar.scala
@@ -8,7 +8,7 @@ import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._
import Decorators._
import language.higherKinds
import collection.mutable.ListBuffer
-import util.Attachment
+import util.Property
object desugar {
import untpd._
@@ -16,7 +16,7 @@ object desugar {
/** Tags a .withFilter call generated by desugaring a for expression.
* Such calls can alternatively be rewritten to use filter.
*/
- val MaybeFilter = new Attachment.Key[Unit]
+ val MaybeFilter = new Property.Key[Unit]
/** Info of a variable in a pattern: The named tree and its type */
private type VarInfo = (NameTree, Tree)
@@ -607,11 +607,17 @@ object desugar {
* ==>
* def $anonfun(params) = body
* Closure($anonfun)
+ *
+ * If `inlineable` is true, tag $anonfun with an @inline annotation.
*/
- def makeClosure(params: List[ValDef], body: Tree, tpt: Tree = TypeTree()) =
+ def makeClosure(params: List[ValDef], body: Tree, tpt: Tree = TypeTree(), inlineable: Boolean)(implicit ctx: Context) = {
+ var mods = synthetic
+ if (inlineable)
+ mods = mods.withAddedAnnotation(New(ref(defn.InlineAnnotType), Nil).withPos(body.pos))
Block(
- DefDef(nme.ANON_FUN, Nil, params :: Nil, tpt, body).withMods(synthetic),
+ DefDef(nme.ANON_FUN, Nil, params :: Nil, tpt, body).withMods(mods),
Closure(Nil, Ident(nme.ANON_FUN), EmptyTree))
+ }
/** If `nparams` == 1, expand partial function
*
diff --git a/src/dotty/tools/dotc/ast/TreeInfo.scala b/src/dotty/tools/dotc/ast/TreeInfo.scala
index 725838ef6..7911840c6 100644
--- a/src/dotty/tools/dotc/ast/TreeInfo.scala
+++ b/src/dotty/tools/dotc/ast/TreeInfo.scala
@@ -88,12 +88,6 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
case mp => mp
}
- /** If tree is a closure, it's body, otherwise tree itself */
- def closureBody(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match {
- case Block((meth @ DefDef(nme.ANON_FUN, _, _, _, _)) :: Nil, Closure(_, _, _)) => meth.rhs
- case _ => tree
- }
-
/** If this is an application, its function part, stripping all
* Apply nodes (but leaving TypeApply nodes in). Otherwise the tree itself.
*/
@@ -311,6 +305,8 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
if (vdef.symbol.flags is Mutable) Impure else exprPurity(vdef.rhs)
case _ =>
Impure
+ // TODO: It seem like this should be exprPurity(tree)
+ // But if we do that the repl/vars test break. Need to figure out why that's the case.
}
/** The purity level of this expression.
@@ -327,13 +323,13 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
case EmptyTree
| This(_)
| Super(_, _)
- | Literal(_) =>
+ | Literal(_)
+ | Closure(_, _, _) =>
Pure
case Ident(_) =>
refPurity(tree)
case Select(qual, _) =>
- refPurity(tree).min(
- if (tree.symbol.is(Inline)) Pure else exprPurity(qual))
+ refPurity(tree).min(exprPurity(qual))
case TypeApply(fn, _) =>
exprPurity(fn)
/*
@@ -435,6 +431,36 @@ 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)
+ }
+
+ /** An extractor for closures, either contained in a block or standalone.
+ */
+ object closure {
+ def unapply(tree: Tree): Option[(List[Tree], Tree, Tree)] = tree match {
+ case Block(_, Closure(env, meth, tpt)) => Some(env, meth, tpt)
+ case Closure(env, meth, tpt) => Some(env, meth, tpt)
+ case _ => None
+ }
+ }
+
+ /** If tree is a closure, its body, otherwise tree itself */
+ def closureBody(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match {
+ case Block((meth @ DefDef(nme.ANON_FUN, _, _, _, _)) :: Nil, Closure(_, _, _)) => meth.rhs
+ case _ => tree
+ }
+
/** 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/ast/TreeTypeMap.scala b/src/dotty/tools/dotc/ast/TreeTypeMap.scala
index a35fe2e8f..cf529dfda 100644
--- a/src/dotty/tools/dotc/ast/TreeTypeMap.scala
+++ b/src/dotty/tools/dotc/ast/TreeTypeMap.scala
@@ -92,11 +92,20 @@ final class TreeTypeMap(
case ddef @ DefDef(name, tparams, vparamss, tpt, _) =>
val (tmap1, tparams1) = transformDefs(ddef.tparams)
val (tmap2, vparamss1) = tmap1.transformVParamss(vparamss)
- cpy.DefDef(ddef)(name, tparams1, vparamss1, tmap2.transform(tpt), tmap2.transform(ddef.rhs))
+ 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 => ann
+ }
+ res
case blk @ Block(stats, expr) =>
val (tmap1, stats1) = transformDefs(stats)
val expr1 = tmap1.transform(expr)
cpy.Block(blk)(stats1, expr1)
+ case inlined @ Inlined(call, bindings, expanded) =>
+ val (tmap1, bindings1) = transformDefs(bindings)
+ val expanded1 = tmap1.transform(expanded)
+ cpy.Inlined(inlined)(call, bindings1, expanded1)
case cdef @ CaseDef(pat, guard, rhs) =>
val tmap = withMappedSyms(patVars(pat))
val pat1 = tmap.transform(pat)
@@ -127,10 +136,7 @@ final class TreeTypeMap(
def apply[ThisTree <: tpd.Tree](tree: ThisTree): ThisTree = transform(tree).asInstanceOf[ThisTree]
- def apply(annot: Annotation): Annotation = {
- val tree1 = apply(annot.tree)
- if (tree1 eq annot.tree) annot else ConcreteAnnotation(tree1)
- }
+ def apply(annot: Annotation): Annotation = annot.derivedAnnotation(apply(annot.tree))
/** The current tree map composed with a substitution [from -> to] */
def withSubstitution(from: List[Symbol], to: List[Symbol]): TreeTypeMap =
diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala
index bb6fbd5ba..6986e40e7 100644
--- a/src/dotty/tools/dotc/ast/Trees.scala
+++ b/src/dotty/tools/dotc/ast/Trees.scala
@@ -12,7 +12,7 @@ import collection.immutable.IndexedSeq
import collection.mutable.ListBuffer
import parsing.Tokens.Token
import printing.Printer
-import util.{Stats, Attachment, DotClass}
+import util.{Stats, Attachment, Property, DotClass}
import annotation.unchecked.uncheckedVariance
import language.implicitConversions
import parsing.Scanners.Comment
@@ -30,8 +30,8 @@ object Trees {
/** The total number of created tree nodes, maintained if Stats.enabled */
@sharable var ntrees = 0
- /** Attachment key for trees with documentation strings attached */
- val DocComment = new Attachment.Key[Comment]
+ /** Property key for trees with documentation strings attached */
+ val DocComment = new Property.Key[Comment]
@sharable private var nextId = 0 // for debugging
@@ -503,6 +503,25 @@ object Trees {
override def toString = s"JavaSeqLiteral($elems, $elemtpt)"
}
+ /** A tree representing inlined code.
+ *
+ * @param call The original call that was inlined
+ * @param bindings Bindings for proxies to be used in the inlined code
+ * @param expansion The inlined tree, minus bindings.
+ *
+ * The full inlined code is equivalent to
+ *
+ * { bindings; expansion }
+ *
+ * The reason to keep `bindings` separate is because they are typed in a
+ * different context: `bindings` represent the arguments to the inlined
+ * call, whereas `expansion` represents the body of the inlined function.
+ */
+ case class Inlined[-T >: Untyped] private[ast] (call: tpd.Tree, bindings: List[MemberDef[T]], expansion: Tree[T])
+ extends Tree[T] {
+ type ThisTree[-T >: Untyped] = Inlined[T]
+ }
+
/** A type tree that represents an existing or inferred type */
case class TypeTree[-T >: Untyped] private[ast] (original: Tree[T])
extends DenotingTree[T] with TypTree[T] {
@@ -797,6 +816,7 @@ object Trees {
type Try = Trees.Try[T]
type SeqLiteral = Trees.SeqLiteral[T]
type JavaSeqLiteral = Trees.JavaSeqLiteral[T]
+ type Inlined = Trees.Inlined[T]
type TypeTree = Trees.TypeTree[T]
type SingletonTypeTree = Trees.SingletonTypeTree[T]
type AndTypeTree = Trees.AndTypeTree[T]
@@ -939,6 +959,10 @@ object Trees {
case tree: SeqLiteral if (elems eq tree.elems) && (elemtpt eq tree.elemtpt) => tree
case _ => finalize(tree, untpd.SeqLiteral(elems, elemtpt))
}
+ def Inlined(tree: Tree)(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(implicit ctx: Context): Inlined = tree match {
+ case tree: Inlined if (call eq tree.call) && (bindings eq tree.bindings) && (expansion eq tree.expansion) => tree
+ case _ => finalize(tree, untpd.Inlined(call, bindings, expansion))
+ }
def TypeTree(tree: Tree)(original: Tree): TypeTree = tree match {
case tree: TypeTree if original eq tree.original => tree
case _ => finalize(tree, untpd.TypeTree(original))
@@ -1083,6 +1107,8 @@ object Trees {
cpy.Try(tree)(transform(block), transformSub(cases), transform(finalizer))
case SeqLiteral(elems, elemtpt) =>
cpy.SeqLiteral(tree)(transform(elems), transform(elemtpt))
+ case Inlined(call, bindings, expansion) =>
+ cpy.Inlined(tree)(call, transformSub(bindings), transform(expansion))
case TypeTree(original) =>
tree
case SingletonTypeTree(ref) =>
@@ -1185,6 +1211,8 @@ object Trees {
this(this(this(x, block), handler), finalizer)
case SeqLiteral(elems, elemtpt) =>
this(this(x, elems), elemtpt)
+ case Inlined(call, bindings, expansion) =>
+ this(this(x, bindings), expansion)
case TypeTree(original) =>
x
case SingletonTypeTree(ref) =>
diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala
index f59bb7a47..8ba7bc54d 100644
--- a/src/dotty/tools/dotc/ast/tpd.scala
+++ b/src/dotty/tools/dotc/ast/tpd.scala
@@ -10,6 +10,7 @@ import util.Positions._, Types._, Contexts._, Constants._, Names._, Flags._
import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Symbols._
import Denotations._, Decorators._, DenotTransformers._
import collection.mutable
+import util.{Property, SourceFile, NoSource}
import typer.ErrorReporting._
import scala.annotation.tailrec
@@ -116,6 +117,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit ctx: Context): JavaSeqLiteral =
ta.assignType(new untpd.JavaSeqLiteral(elems, elemtpt), elems, elemtpt).asInstanceOf[JavaSeqLiteral]
+ def Inlined(call: Tree, bindings: List[MemberDef], expansion: Tree)(implicit ctx: Context): Inlined =
+ ta.assignType(untpd.Inlined(call, bindings, expansion), bindings, expansion)
+
def TypeTree(original: Tree)(implicit ctx: Context): TypeTree =
TypeTree(original.tpe, original)
@@ -918,8 +922,25 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
}
}
- // ensure that constructors are fully applied?
- // ensure that normal methods are fully applied?
+ /** A key to be used in a context property that tracks enclosing inlined calls */
+ private val InlinedCalls = new Property.Key[List[Tree]]
+
+ /** A context derived form `ctx` that records `call` as innermost enclosing
+ * call for which the inlined version is currently processed.
+ */
+ def inlineContext(call: Tree)(implicit ctx: Context): Context =
+ ctx.fresh.setProperty(InlinedCalls, call :: enclosingInlineds)
+
+ /** All enclosing calls that are currently inlined, from innermost to outermost */
+ def enclosingInlineds(implicit ctx: Context): List[Tree] =
+ ctx.property(InlinedCalls).getOrElse(Nil)
+ /** The source file where the symbol of the `@inline` method referred to by `call`
+ * is defined
+ */
+ def sourceFile(call: Tree)(implicit ctx: Context) = {
+ val file = call.symbol.sourceFile
+ if (file != null && file.exists) new SourceFile(file) else NoSource
+ }
}
diff --git a/src/dotty/tools/dotc/ast/untpd.scala b/src/dotty/tools/dotc/ast/untpd.scala
index 61c3a79a4..cc7cefbac 100644
--- a/src/dotty/tools/dotc/ast/untpd.scala
+++ b/src/dotty/tools/dotc/ast/untpd.scala
@@ -6,7 +6,7 @@ import core._
import util.Positions._, Types._, Contexts._, Constants._, Names._, NameOps._, Flags._
import Denotations._, SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._
import Decorators._
-import util.Attachment
+import util.Property
import language.higherKinds
import collection.mutable.ListBuffer
@@ -20,11 +20,18 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
override def isType = op.isTypeName
}
- /** A typed subtree of an untyped tree needs to be wrapped in a TypedSlice */
- case class TypedSplice(tree: tpd.Tree) extends ProxyTree {
+ /** A typed subtree of an untyped tree needs to be wrapped in a TypedSlice
+ * @param owner The current owner at the time the tree was defined
+ */
+ abstract case class TypedSplice(tree: tpd.Tree)(val owner: Symbol) extends ProxyTree {
def forwardTo = tree
}
+ object TypedSplice {
+ def apply(tree: tpd.Tree)(implicit ctx: Context): TypedSplice =
+ new TypedSplice(tree)(ctx.owner) {}
+ }
+
/** mods object name impl */
case class ModuleDef(name: TermName, impl: Template)
extends MemberDef {
@@ -161,17 +168,17 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
def derivedType(originalSym: Symbol)(implicit ctx: Context): Type
}
- /** Attachment key containing TypeTrees whose type is computed
+ /** Property key containing TypeTrees whose type is computed
* from the symbol in this type. These type trees have marker trees
* TypeRefOfSym or InfoOfSym as their originals.
*/
- val References = new Attachment.Key[List[Tree]]
+ val References = new Property.Key[List[Tree]]
- /** Attachment key for TypeTrees marked with TypeRefOfSym or InfoOfSym
+ /** Property key for TypeTrees marked with TypeRefOfSym or InfoOfSym
* which contains the symbol of the original tree from which this
* TypeTree is derived.
*/
- val OriginalSymbol = new Attachment.Key[Symbol]
+ val OriginalSymbol = new Property.Key[Symbol]
// ------ Creation methods for untyped only -----------------
@@ -197,6 +204,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
def Try(expr: Tree, cases: List[CaseDef], finalizer: Tree): Try = new Try(expr, cases, finalizer)
def SeqLiteral(elems: List[Tree], elemtpt: Tree): SeqLiteral = new SeqLiteral(elems, elemtpt)
def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree): JavaSeqLiteral = new JavaSeqLiteral(elems, elemtpt)
+ def Inlined(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree): Inlined = new Inlined(call, bindings, expansion)
def TypeTree(original: Tree): TypeTree = new TypeTree(original)
def TypeTree() = new TypeTree(EmptyTree)
def SingletonTypeTree(ref: Tree): SingletonTypeTree = new SingletonTypeTree(ref)
@@ -231,7 +239,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
case AppliedTypeTree(tycon, targs) =>
(tycon, targs)
case TypedSplice(AppliedTypeTree(tycon, targs)) =>
- (TypedSplice(tycon), targs map TypedSplice)
+ (TypedSplice(tycon), targs map (TypedSplice(_)))
case TypedSplice(tpt1: Tree) =>
val argTypes = tpt1.tpe.argTypes
val tycon = tpt1.tpe.withoutArgs(argTypes)
@@ -259,7 +267,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
def AppliedTypeTree(tpt: Tree, arg: Tree): AppliedTypeTree =
AppliedTypeTree(tpt, arg :: Nil)
- def TypeTree(tpe: Type): TypedSplice = TypedSplice(TypeTree().withTypeUnchecked(tpe))
+ def TypeTree(tpe: Type)(implicit ctx: Context): TypedSplice = TypedSplice(TypeTree().withTypeUnchecked(tpe))
def TypeDef(name: TypeName, tparams: List[TypeDef], rhs: Tree): TypeDef =
if (tparams.isEmpty) TypeDef(name, rhs) else new PolyTypeDef(name, tparams, rhs)
diff --git a/src/dotty/tools/dotc/config/Printers.scala b/src/dotty/tools/dotc/config/Printers.scala
index 322bc82d9..002d0f933 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 = noPrinter
}
diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala
index c090a5515..ff17a9939 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.")
@@ -120,7 +121,6 @@ class ScalaSettings extends Settings.SettingGroup {
val debugOwners = BooleanSetting("-Ydebug-owners", "Print all owners of definitions (requires -Yprint-syms)")
//val doc = BooleanSetting ("-Ydoc", "Generate documentation")
val termConflict = ChoiceSetting("-Yresolve-term-conflict", "strategy", "Resolve term conflicts", List("package", "object", "error"), "error")
- val inline = BooleanSetting("-Yinline", "Perform inlining when possible.")
val inlineHandlers = BooleanSetting("-Yinline-handlers", "Perform exception handler inlining when possible.")
val YinlinerWarnings = BooleanSetting("-Yinline-warnings", "Emit inlining warnings. (Normally surpressed due to high volume)")
val Ylinearizer = ChoiceSetting("-Ylinearizer", "which", "Linearizer to use", List("normal", "dfs", "rpo", "dump"), "rpo")
@@ -185,6 +185,7 @@ class ScalaSettings extends Settings.SettingGroup {
val Yexplainlowlevel = BooleanSetting("-Yexplain-lowlevel", "When explaining type errors, show types at a lower level.")
val YnoDoubleBindings = BooleanSetting("-Yno-double-bindings", "Assert no namedtype is bound twice (should be enabled only if program is error-free).")
val YshowVarBounds = BooleanSetting("-Yshow-var-bounds", "Print type variables with their bounds")
+ val YnoInline = BooleanSetting("-Yno-inline", "Suppress inlining.")
val optimise = BooleanSetting("-optimise", "Generates faster bytecode by applying optimisations to the program") withAbbreviation "-optimize"
diff --git a/src/dotty/tools/dotc/core/Annotations.scala b/src/dotty/tools/dotc/core/Annotations.scala
index 5f96a60e6..0e8e5a1f0 100644
--- a/src/dotty/tools/dotc/core/Annotations.scala
+++ b/src/dotty/tools/dotc/core/Annotations.scala
@@ -26,6 +26,8 @@ object Annotations {
}
def argumentConstant(i: Int)(implicit ctx: Context): Option[Constant] =
for (ConstantType(c) <- argument(i) map (_.tpe)) yield c
+
+ def ensureCompleted(implicit ctx: Context): Unit = tree
}
case class ConcreteAnnotation(t: Tree) extends Annotation {
@@ -42,6 +44,36 @@ object Annotations {
override def symbol(implicit ctx: Context): Symbol = sym
}
+ /** An annotation indicating the body of a right-hand side,
+ * typically of an inline method. Treated specially in
+ * pickling/unpickling and TypeTreeMaps
+ */
+ abstract class BodyAnnotation extends Annotation {
+ override def symbol(implicit ctx: Context) = defn.BodyAnnot
+ override def derivedAnnotation(tree: Tree)(implicit ctx: Context) =
+ if (tree eq this.tree) this else ConcreteBodyAnnotation(tree)
+ override def arguments(implicit ctx: Context) = Nil
+ override def ensureCompleted(implicit ctx: Context) = ()
+ }
+
+ case class ConcreteBodyAnnotation(body: Tree) extends BodyAnnotation {
+ def tree(implicit ctx: Context) = body
+ }
+
+ case class LazyBodyAnnotation(bodyExpr: Context => Tree) extends BodyAnnotation {
+ private var evaluated = false
+ private var myBody: Tree = _
+ def tree(implicit ctx: Context) = {
+ if (evaluated) assert(myBody != null)
+ else {
+ evaluated = true
+ myBody = bodyExpr(ctx)
+ }
+ myBody
+ }
+ def isEvaluated = evaluated
+ }
+
object Annotation {
def apply(tree: Tree) = ConcreteAnnotation(tree)
diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala
index cd76fe88b..313ea3124 100644
--- a/src/dotty/tools/dotc/core/Contexts.scala
+++ b/src/dotty/tools/dotc/core/Contexts.scala
@@ -18,7 +18,7 @@ import util.Positions._
import ast.Trees._
import ast.untpd
import util.{FreshNameCreator, SimpleMap, SourceFile, NoSource}
-import typer.{Implicits, ImplicitRunInfo, ImportInfo, NamerContextOps, SearchHistory, TypeAssigner, Typer}
+import typer.{Implicits, ImplicitRunInfo, ImportInfo, Inliner, NamerContextOps, SearchHistory, TypeAssigner, Typer}
import Implicits.ContextualImplicits
import config.Settings._
import config.Config
@@ -30,6 +30,7 @@ import config.{Settings, ScalaSettings, Platform, JavaPlatform, SJSPlatform}
import language.implicitConversions
import DenotTransformers.DenotTransformer
import parsing.Scanners.Comment
+import util.Property.Key
import xsbti.AnalysisCallback
object Contexts {
@@ -177,9 +178,12 @@ object Contexts {
def freshName(prefix: Name): String = freshName(prefix.toString)
/** A map in which more contextual properties can be stored */
- private var _moreProperties: Map[String, Any] = _
- protected def moreProperties_=(moreProperties: Map[String, Any]) = _moreProperties = moreProperties
- def moreProperties: Map[String, Any] = _moreProperties
+ private var _moreProperties: Map[Key[Any], Any] = _
+ protected def moreProperties_=(moreProperties: Map[Key[Any], Any]) = _moreProperties = moreProperties
+ def moreProperties: Map[Key[Any], Any] = _moreProperties
+
+ def property[T](key: Key[T]): Option[T] =
+ moreProperties.get(key).asInstanceOf[Option[T]]
private var _typeComparer: TypeComparer = _
protected def typeComparer_=(typeComparer: TypeComparer) = _typeComparer = typeComparer
@@ -459,9 +463,10 @@ object Contexts {
def setTypeComparerFn(tcfn: Context => TypeComparer): this.type = { this.typeComparer = tcfn(this); this }
def setSearchHistory(searchHistory: SearchHistory): this.type = { this.searchHistory = searchHistory; this }
def setFreshNames(freshNames: FreshNameCreator): this.type = { this.freshNames = freshNames; this }
- def setMoreProperties(moreProperties: Map[String, Any]): this.type = { this.moreProperties = moreProperties; this }
+ def setMoreProperties(moreProperties: Map[Key[Any], Any]): this.type = { this.moreProperties = moreProperties; this }
- def setProperty(prop: (String, Any)): this.type = setMoreProperties(moreProperties + prop)
+ def setProperty[T](key: Key[T], value: T): this.type =
+ setMoreProperties(moreProperties.updated(key, value))
def setPhase(pid: PhaseId): this.type = setPeriod(Period(runId, pid))
def setPhase(phase: Phase): this.type = setPeriod(Period(runId, phase.start, phase.end))
diff --git a/src/dotty/tools/dotc/core/Decorators.scala b/src/dotty/tools/dotc/core/Decorators.scala
index 387e7e466..3bf17730a 100644
--- a/src/dotty/tools/dotc/core/Decorators.scala
+++ b/src/dotty/tools/dotc/core/Decorators.scala
@@ -7,6 +7,7 @@ import Contexts._, Names._, Phases._, printing.Texts._, printing.Printer, printi
import util.Positions.Position, util.SourcePosition
import collection.mutable.ListBuffer
import dotty.tools.dotc.transform.TreeTransforms._
+import ast.tpd._
import scala.language.implicitConversions
import printing.Formatting._
@@ -40,7 +41,7 @@ object Decorators {
*/
implicit class ListDecorator[T](val xs: List[T]) extends AnyVal {
- @inline final def mapconserve[U](f: T => U): List[U] = {
+ final def mapconserve[U](f: T => U): List[U] = {
@tailrec
def loop(mapped: ListBuffer[U], unchanged: List[U], pending: List[T]): List[U] =
if (pending.isEmpty) {
@@ -148,8 +149,15 @@ object Decorators {
}
}
- implicit def sourcePos(pos: Position)(implicit ctx: Context): SourcePosition =
- ctx.source.atPos(pos)
+ implicit def sourcePos(pos: Position)(implicit ctx: Context): SourcePosition = {
+ def recur(inlinedCalls: List[Tree], pos: Position): SourcePosition = inlinedCalls match {
+ case inlinedCall :: rest =>
+ sourceFile(inlinedCall).atPos(pos).withOuter(recur(rest, inlinedCall.pos))
+ case empty =>
+ ctx.source.atPos(pos)
+ }
+ recur(enclosingInlineds, pos)
+ }
implicit class StringInterpolators(val sc: StringContext) extends AnyVal {
diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala
index cb83fda04..75b75d3d5 100644
--- a/src/dotty/tools/dotc/core/Definitions.scala
+++ b/src/dotty/tools/dotc/core/Definitions.scala
@@ -456,6 +456,8 @@ class Definitions {
def AliasAnnot(implicit ctx: Context) = AliasAnnotType.symbol.asClass
lazy val AnnotationDefaultAnnotType = ctx.requiredClassRef("dotty.annotation.internal.AnnotationDefault")
def AnnotationDefaultAnnot(implicit ctx: Context) = AnnotationDefaultAnnotType.symbol.asClass
+ lazy val BodyAnnotType = ctx.requiredClassRef("dotty.annotation.internal.Body")
+ def BodyAnnot(implicit ctx: Context) = BodyAnnotType.symbol.asClass
lazy val ChildAnnotType = ctx.requiredClassRef("dotty.annotation.internal.Child")
def ChildAnnot(implicit ctx: Context) = ChildAnnotType.symbol.asClass
lazy val CovariantBetweenAnnotType = ctx.requiredClassRef("dotty.annotation.internal.CovariantBetween")
@@ -466,6 +468,10 @@ 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("scala.inline")
+ def InlineAnnot(implicit ctx: Context) = InlineAnnotType.symbol.asClass
+ lazy val InlineParamAnnotType = ctx.requiredClassRef("dotty.annotation.internal.InlineParam")
+ def InlineParamAnnot(implicit ctx: Context) = InlineParamAnnotType.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/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala
index 0cdae6b98..3f4433708 100644
--- a/src/dotty/tools/dotc/core/Flags.scala
+++ b/src/dotty/tools/dotc/core/Flags.scala
@@ -431,7 +431,7 @@ object Flags {
/** Flags representing source modifiers */
final val SourceModifierFlags =
- commonFlags(Private, Protected, Abstract, Final,
+ commonFlags(Private, Protected, Abstract, Final, Inline,
Sealed, Case, Implicit, Override, AbsOverride, Lazy, JavaStatic)
/** Flags representing modifiers that can appear in trees */
@@ -450,7 +450,7 @@ object Flags {
AccessFlags | Module | Package | Deferred | Final | MethodOrHKCommon | Param | ParamAccessor | Scala2ExistentialCommon |
Mutable.toCommonFlags | InSuperCall | Touched | JavaStatic | CovariantOrOuter | ContravariantOrLabel | ExpandedName | AccessorOrSealed |
CaseAccessorOrBaseTypeArg | Fresh | Frozen | Erroneous | ImplicitCommon | Permanent | Synthetic |
- LazyOrTrait | SuperAccessorOrScala2x | SelfNameOrImplClass
+ Inline | LazyOrTrait | SuperAccessorOrScala2x | SelfNameOrImplClass
assert(FromStartFlags.isTermFlags && FromStartFlags.isTypeFlags)
// TODO: Should check that FromStartFlags do not change in completion
@@ -529,8 +529,8 @@ object Flags {
/** Either method or lazy or deferred */
final val MethodOrLazyOrDeferred = Method | Lazy | Deferred
- /** Labeled `private` or `final` */
- final val PrivateOrFinal = Private | Final
+ /** Labeled `private`, `final`, or `inline` */
+ final val PrivateOrFinalOrInline = Private | Final | Inline
/** A private method */
final val PrivateMethod = allOf(Private, Method)
@@ -541,6 +541,9 @@ object Flags {
/** A type parameter with synthesized name */
final val ExpandedTypeParam = allOf(ExpandedName, TypeParam)
+ /** An inline method */
+ final val InlineMethod = allOf(Inline, Method)
+
/** A parameter or parameter accessor */
final val ParamOrAccessor = Param | ParamAccessor
@@ -553,6 +556,12 @@ object Flags {
/** A type parameter or type parameter accessor */
final val TypeParamOrAccessor = TypeParam | TypeParamAccessor
+ /** A deferred member or a parameter accessor (these don't have right hand sides) */
+ final val DeferredOrParamAccessor = Deferred | ParamAccessor
+
+ /** value that's final or inline */
+ final val FinalOrInline = Final | Inline
+
/** If symbol of a type alias has these flags, prefer the alias */
final val AliasPreferred = TypeParam | BaseTypeArg | ExpandedName
diff --git a/src/dotty/tools/dotc/core/Mode.scala b/src/dotty/tools/dotc/core/Mode.scala
index 3e9b7effe..7a9bb0572 100644
--- a/src/dotty/tools/dotc/core/Mode.scala
+++ b/src/dotty/tools/dotc/core/Mode.scala
@@ -89,5 +89,8 @@ object Mode {
*/
val AllowLambdaWildcardApply = newMode(15, "AllowHKApplyToWildcards")
+ /** Read original positions when unpickling from TASTY */
+ val ReadPositions = newMode(16, "ReadPositions")
+
val PatternOrType = Pattern | Type
}
diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala
index f5e0eb8cd..48e823e81 100644
--- a/src/dotty/tools/dotc/core/NameOps.scala
+++ b/src/dotty/tools/dotc/core/NameOps.scala
@@ -84,6 +84,8 @@ object NameOps {
name.stripAnonNumberSuffix endsWith MODULE_VAR_SUFFIX
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 && {
@@ -166,7 +168,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]
@@ -419,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 f47ab1744..c52264637 100644
--- a/src/dotty/tools/dotc/core/StdNames.scala
+++ b/src/dotty/tools/dotc/core/StdNames.scala
@@ -46,6 +46,7 @@ object StdNames {
final val IFkw: N = kw("if")
final val IMPLICITkw: N = kw("implicit")
final val IMPORTkw: N = kw("import")
+ final val INLINEkw: N = kw("inline")
final val LAZYkw: N = kw("lazy")
final val MACROkw: N = kw("macro")
final val MATCHkw: N = kw("match")
@@ -100,6 +101,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"
@@ -252,7 +254,7 @@ object StdNames {
val MODULE_INSTANCE_FIELD: N = NameTransformer.MODULE_INSTANCE_NAME // "MODULE$"
val OUTER: N = "$outer"
val OUTER_LOCAL: N = "$outer "
- val OUTER_SYNTH: N = "<outer>" // emitted by virtual pattern matcher, replaced by outer accessor in explicitouter
+ val OUTER_SELECT: N = "_<outer>" // emitted by inliner, replaced by outer path in explicitouter
val REFINE_CLASS: N = "<refinement>"
val ROOTPKG: N = "_root_"
val SELECTOR_DUMMY: N = "<unapply-selector>"
diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala
index ab45550a4..969d09c3e 100644
--- a/src/dotty/tools/dotc/core/SymDenotations.scala
+++ b/src/dotty/tools/dotc/core/SymDenotations.scala
@@ -281,6 +281,15 @@ object SymDenotations {
case nil => None
}
+ /** The same as getAnnotation, but without ensuring
+ * that the symbol carrying the annotation is completed
+ */
+ final def unforcedAnnotation(cls: Symbol)(implicit ctx: Context): Option[Annotation] =
+ dropOtherAnnotations(myAnnotations, cls) match {
+ case annot :: _ => Some(annot)
+ case nil => None
+ }
+
/** Add given annotation to the annotations of this denotation */
final def addAnnotation(annot: Annotation): Unit =
annotations = annot :: myAnnotations
@@ -289,6 +298,12 @@ object SymDenotations {
final def removeAnnotation(cls: Symbol)(implicit ctx: Context): Unit =
annotations = myAnnotations.filterNot(_ matches cls)
+ /** Remove any annotations with same class as `annot`, and add `annot` */
+ final def updateAnnotation(annot: Annotation)(implicit ctx: Context): Unit = {
+ removeAnnotation(annot.symbol)
+ addAnnotation(annot)
+ }
+
/** Add all given annotations to this symbol */
final def addAnnotations(annots: TraversableOnce[Annotation])(implicit ctx: Context): Unit =
annots.foreach(addAnnotation)
@@ -670,9 +685,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 +695,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 +759,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:
@@ -851,7 +871,7 @@ object SymDenotations {
/** A symbol is effectively final if it cannot be overridden in a subclass */
final def isEffectivelyFinal(implicit ctx: Context): Boolean =
- is(PrivateOrFinal) || !owner.isClass || owner.is(ModuleOrFinal) || owner.isAnonymousClass
+ is(PrivateOrFinalOrInline) || !owner.isClass || owner.is(ModuleOrFinal) || owner.isAnonymousClass
/** The class containing this denotation which has the given effective name. */
final def enclosingClassNamed(name: Name)(implicit ctx: Context): Symbol = {
@@ -1521,7 +1541,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/SymbolLoaders.scala b/src/dotty/tools/dotc/core/SymbolLoaders.scala
index 3f801bda5..4ae28c10b 100644
--- a/src/dotty/tools/dotc/core/SymbolLoaders.scala
+++ b/src/dotty/tools/dotc/core/SymbolLoaders.scala
@@ -198,7 +198,7 @@ abstract class SymbolLoader extends LazyType {
try {
val start = currentTime
if (ctx.settings.debugTrace.value)
- ctx.traceIndented(s">>>> loading ${root.debugString}", _ => s"<<<< loaded ${root.debugString}") {
+ ctx.doTraceIndented(s">>>> loading ${root.debugString}", _ => s"<<<< loaded ${root.debugString}") {
doComplete(root)
}
else
diff --git a/src/dotty/tools/dotc/core/TyperState.scala b/src/dotty/tools/dotc/core/TyperState.scala
index 7b8867ccc..7e332b412 100644
--- a/src/dotty/tools/dotc/core/TyperState.scala
+++ b/src/dotty/tools/dotc/core/TyperState.scala
@@ -122,6 +122,18 @@ extends TyperState(r) {
* type variables changes from this typer state to the current one. (2) Variables
* that were temporarily instantiated in the current typer state are permanently
* instantiated instead.
+ *
+ * A note on merging: An interesting test case is isApplicableSafe.scala. It turns out that this
+ * requires a context merge using the new `&' operator. Sequence of actions:
+ * 1) Typecheck argument in typerstate 1.
+ * 2) Cache argument.
+ * 3) Evolve same typer state (to typecheck other arguments, say)
+ * leading to a different constraint.
+ * 4) Take typechecked argument in same state.
+ *
+ * It turns out that the merge is needed not just for
+ * isApplicableSafe but also for (e.g. erased-lubs.scala) as well as
+ * many parts of dotty itself.
*/
override def commit()(implicit ctx: Context) = {
val targetState = ctx.typerState
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index 30d1c0136..2f1b6b829 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -228,8 +228,8 @@ object Types {
!existsPart(!p(_))
/** Performs operation on all parts of this type */
- final def foreachPart(p: Type => Unit)(implicit ctx: Context): Unit =
- new ForeachAccumulator(p).apply((), this)
+ final def foreachPart(p: Type => Unit, stopAtStatic: Boolean = false)(implicit ctx: Context): Unit =
+ new ForeachAccumulator(p, stopAtStatic).apply((), this)
/** The parts of this type which are type or term refs */
final def namedParts(implicit ctx: Context): collection.Set[NamedType] =
@@ -848,30 +848,42 @@ object Types {
case tp => tp
}
- /** Follow aliases and dereferences LazyRefs and instantiated TypeVars until type
- * is no longer alias type, LazyRef, or instantiated type variable.
- */
- final def dealias(implicit ctx: Context): Type = this match {
+ private def dealias(keepAnnots: Boolean)(implicit ctx: Context): Type = this match {
case tp: TypeRef =>
if (tp.symbol.isClass) tp
else tp.info match {
- case TypeAlias(tp) => tp.dealias
+ case TypeAlias(tp) => tp.dealias(keepAnnots)
case _ => tp
}
case tp: TypeVar =>
val tp1 = tp.instanceOpt
- if (tp1.exists) tp1.dealias else tp
+ if (tp1.exists) tp1.dealias(keepAnnots) else tp
case tp: AnnotatedType =>
- tp.derivedAnnotatedType(tp.tpe.dealias, tp.annot)
+ val tp1 = tp.tpe.dealias(keepAnnots)
+ if (keepAnnots) tp.derivedAnnotatedType(tp1, tp.annot) else tp1
case tp: LazyRef =>
- tp.ref.dealias
+ tp.ref.dealias(keepAnnots)
case app @ HKApply(tycon, args) =>
- val tycon1 = tycon.dealias
- if (tycon1 ne tycon) app.superType.dealias
+ val tycon1 = tycon.dealias(keepAnnots)
+ if (tycon1 ne tycon) app.superType.dealias(keepAnnots)
else this
case _ => this
}
+ /** Follow aliases and dereferences LazyRefs and instantiated TypeVars until type
+ * is no longer alias type, LazyRef, or instantiated type variable.
+ * Goes through annotated types and rewraps annotations on the result.
+ */
+ final def dealiasKeepAnnots(implicit ctx: Context): Type =
+ dealias(keepAnnots = true)
+
+ /** Follow aliases and dereferences LazyRefs, annotated types and instantiated
+ * TypeVars until type is no longer alias type, annotated type, LazyRef,
+ * or instantiated type variable.
+ */
+ final def dealias(implicit ctx: Context): Type =
+ dealias(keepAnnots = false)
+
/** Perform successive widenings and dealiasings until none can be applied anymore */
final def widenDealias(implicit ctx: Context): Type = {
val res = this.widen.dealias
@@ -1788,6 +1800,7 @@ object Types {
false
}
override def computeHash = doHash((name, sig), prefix)
+ override def toString = super.toString ++ s"/withSig($sig)"
}
trait WithFixedSym extends NamedType {
@@ -2418,7 +2431,12 @@ object Types {
apply(nme.syntheticParamNames(paramTypes.length), paramTypes)(resultTypeExp)
def apply(paramTypes: List[Type], resultType: Type)(implicit ctx: Context): MethodType =
apply(nme.syntheticParamNames(paramTypes.length), paramTypes, resultType)
+
+ /** Produce method type from parameter symbols, with special mappings for repeated
+ * and inline parameters.
+ */
def fromSymbols(params: List[Symbol], resultType: Type)(implicit ctx: Context) = {
+ /** Replace @repeated annotations on Seq or Array types by <repeated> types */
def translateRepeated(tp: Type): Type = tp match {
case tp @ ExprType(tp1) => tp.derivedExprType(translateRepeated(tp1))
case AnnotatedType(tp, annot) if annot matches defn.RepeatedAnnot =>
@@ -2428,7 +2446,15 @@ object Types {
case tp =>
tp
}
- def paramInfo(param: Symbol): Type = translateRepeated(param.info)
+ /** Add @inlineParam to inline call-by-value parameters */
+ def translateInline(tp: Type): Type = tp match {
+ case _: ExprType => tp
+ case _ => AnnotatedType(tp, Annotation(defn.InlineParamAnnot))
+ }
+ def paramInfo(param: Symbol): Type = {
+ val paramType = translateRepeated(param.info)
+ if (param.is(Inline)) translateInline(paramType) else paramType
+ }
def transformResult(mt: MethodType) =
resultType.subst(params, (0 until params.length).toList map (MethodParam(mt, _)))
apply(params map (_.name.asTermName), params map paramInfo)(transformResult _)
@@ -2554,6 +2580,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 supported
+ * 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)
@@ -3704,8 +3748,7 @@ object Types {
x || p(tp) || (forceLazy || !tp.isInstanceOf[LazyRef]) && foldOver(x, tp)
}
- class ForeachAccumulator(p: Type => Unit)(implicit ctx: Context) extends TypeAccumulator[Unit] {
- override def stopAtStatic = false
+ class ForeachAccumulator(p: Type => Unit, override val stopAtStatic: Boolean)(implicit ctx: Context) extends TypeAccumulator[Unit] {
def apply(x: Unit, tp: Type): Unit = foldOver(p(tp), tp)
}
diff --git a/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala b/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala
index 0ad5d6966..2c93819d5 100644
--- a/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala
+++ b/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala
@@ -8,8 +8,8 @@ import dotty.tools.dotc.ast.tpd
import TastyUnpickler._, TastyBuffer._
import util.Positions._
import util.{SourceFile, NoSource}
-import PositionUnpickler._
import Annotations.Annotation
+import core.Mode
import classfile.ClassfileParser
object DottyUnpickler {
@@ -17,14 +17,15 @@ object DottyUnpickler {
/** Exception thrown if classfile is corrupted */
class BadSignature(msg: String) extends RuntimeException(msg)
- class TreeSectionUnpickler extends SectionUnpickler[TreeUnpickler]("ASTs") {
+ class TreeSectionUnpickler(posUnpickler: Option[PositionUnpickler])
+ extends SectionUnpickler[TreeUnpickler]("ASTs") {
def unpickle(reader: TastyReader, tastyName: TastyName.Table) =
- new TreeUnpickler(reader, tastyName)
+ new TreeUnpickler(reader, tastyName, posUnpickler)
}
- class PositionsSectionUnpickler extends SectionUnpickler[(Position, AddrToPosition)]("Positions") {
+ class PositionsSectionUnpickler extends SectionUnpickler[PositionUnpickler]("Positions") {
def unpickle(reader: TastyReader, tastyName: TastyName.Table) =
- new PositionUnpickler(reader).unpickle()
+ new PositionUnpickler(reader)
}
}
@@ -36,7 +37,8 @@ class DottyUnpickler(bytes: Array[Byte]) extends ClassfileParser.Embedded {
import DottyUnpickler._
val unpickler = new TastyUnpickler(bytes)
- private val treeUnpickler = unpickler.unpickle(new TreeSectionUnpickler).get
+ private val posUnpicklerOpt = unpickler.unpickle(new PositionsSectionUnpickler)
+ private val treeUnpickler = unpickler.unpickle(new TreeSectionUnpickler(posUnpicklerOpt)).get
/** Enter all toplevel classes and objects into their scopes
* @param roots a set of SymDenotations that should be overwritten by unpickling
@@ -44,13 +46,8 @@ class DottyUnpickler(bytes: Array[Byte]) extends ClassfileParser.Embedded {
def enter(roots: Set[SymDenotation])(implicit ctx: Context): Unit =
treeUnpickler.enterTopLevel(roots)
- /** The unpickled trees, and the source file they come from
- * @param readPositions if true, trees get decorated with position information.
- */
- def body(readPositions: Boolean = false)(implicit ctx: Context): List[Tree] = {
- if (readPositions)
- for ((totalRange, positions) <- unpickler.unpickle(new PositionsSectionUnpickler))
- treeUnpickler.usePositions(totalRange, positions)
+ /** The unpickled trees, and the source file they come from. */
+ def body(implicit ctx: Context): List[Tree] = {
treeUnpickler.unpickle()
}
}
diff --git a/src/dotty/tools/dotc/core/tasty/PositionPickler.scala b/src/dotty/tools/dotc/core/tasty/PositionPickler.scala
index b0550b70a..63bb00a71 100644
--- a/src/dotty/tools/dotc/core/tasty/PositionPickler.scala
+++ b/src/dotty/tools/dotc/core/tasty/PositionPickler.scala
@@ -3,7 +3,8 @@ package dotc
package core
package tasty
-import ast.tpd._
+import ast._
+import ast.Trees._
import ast.Trees.WithLazyField
import TastyFormat._
import core._
@@ -12,64 +13,47 @@ import collection.mutable
import TastyBuffer._
import util.Positions._
-object PositionPickler {
-
- trait DeferredPosition {
- var parentPos: Position = NoPosition
- }
-
- def traverse(x: Any, parentPos: Position, op: (Tree, Position) => Unit)(implicit ctx: Context): Unit =
- if (parentPos.exists)
- x match {
- case x: Tree @unchecked =>
- op(x, parentPos)
- x match {
- case x: MemberDef @unchecked => traverse(x.symbol.annotations, x.pos, op)
- case _ =>
- }
- traverse(x.productIterator, x.pos, op)
- case x: DeferredPosition =>
- x.parentPos = parentPos
- case xs: TraversableOnce[_] =>
- xs.foreach(traverse(_, parentPos, op))
- case _ =>
- }
-}
-import PositionPickler._
-
-class PositionPickler(pickler: TastyPickler, addrOfTree: Tree => Option[Addr]) {
+class PositionPickler(pickler: TastyPickler, addrOfTree: tpd.Tree => Option[Addr]) {
val buf = new TastyBuffer(5000)
pickler.newSection("Positions", buf)
import buf._
+ import ast.tpd._
+
+ def header(addrDelta: Int, hasStartDelta: Boolean, hasEndDelta: Boolean) = {
+ def toInt(b: Boolean) = if (b) 1 else 0
+ (addrDelta << 2) | (toInt(hasStartDelta) << 1) | toInt(hasEndDelta)
+ }
- def picklePositions(roots: List[Tree], totalRange: Position)(implicit ctx: Context) = {
+ def picklePositions(roots: List[Tree])(implicit ctx: Context) = {
var lastIndex = 0
- def record(tree: Tree, parentPos: Position): Unit =
- if (tree.pos.exists) {
- def msg = s"failure to pickle $tree at ${tree.pos}, parent = $parentPos"
- val endPos = tree.pos.end min parentPos.end
- // end positions can be larger than their parents
- // e.g. in the case of synthetic empty ranges, which are placed at the next token after
- // the current construct.
- val endDelta = endPos - parentPos.end
- val startPos =
- if (endDelta == 0) tree.pos.start max parentPos.start else tree.pos.start min endPos
- // Since end positions are corrected above, start positions have to follow suit.
- val startDelta = startPos - parentPos.start
- if (startDelta != 0 || endDelta != 0)
- for (addr <- addrOfTree(tree)) {
- buf.writeInt(addr.index - lastIndex)
- lastIndex = addr.index
- if (startDelta != 0) buf.writeInt(startDelta)
- if (endDelta != 0) {
- assert(endDelta < 0, msg)
- buf.writeInt(endDelta)
- } else
- assert(startDelta >= 0, msg)
+ var lastPos = Position(0, 0)
+ def pickleDeltas(index: Int, pos: Position) = {
+ val addrDelta = index - lastIndex
+ val startDelta = pos.start - lastPos.start
+ val endDelta = pos.end - lastPos.end
+ buf.writeInt(header(addrDelta, startDelta != 0, endDelta != 0))
+ if (startDelta != 0) buf.writeInt(startDelta)
+ if (endDelta != 0) buf.writeInt(endDelta)
+ lastIndex = index
+ lastPos = pos
+ }
+ def traverse(x: Any, parentPos: Position): Unit = x match {
+ case x: Tree @unchecked =>
+ if (x.pos.exists && x.pos.toSynthetic != parentPos.toSynthetic) {
+ addrOfTree(x) match {
+ case Some(addr) => pickleDeltas(addr.index, x.pos)
+ case _ =>
}
- }
-
- buf.writeNat(totalRange.end)
- traverse(roots, totalRange, record)
+ }
+ x match {
+ case x: MemberDef @unchecked => traverse(x.symbol.annotations, x.pos)
+ case _ =>
+ }
+ traverse(x.productIterator, x.pos)
+ case xs: TraversableOnce[_] =>
+ xs.foreach(traverse(_, parentPos))
+ case _ =>
+ }
+ traverse(roots, NoPosition)
}
}
diff --git a/src/dotty/tools/dotc/core/tasty/PositionUnpickler.scala b/src/dotty/tools/dotc/core/tasty/PositionUnpickler.scala
index fa80a2769..c29aeba70 100644
--- a/src/dotty/tools/dotc/core/tasty/PositionUnpickler.scala
+++ b/src/dotty/tools/dotc/core/tasty/PositionUnpickler.scala
@@ -6,33 +6,31 @@ package tasty
import util.Positions._
import collection.mutable
-import TastyBuffer.Addr
-
-object PositionUnpickler {
- type AddrToPosition = mutable.HashMap[Addr, Position]
-}
+import TastyBuffer.{Addr, NoAddr}
/** Unpickler for tree positions */
class PositionUnpickler(reader: TastyReader) {
- import PositionUnpickler._
import reader._
- def unpickle(): (Position, AddrToPosition) = {
- val positions = new mutable.HashMap[Addr, Position] // Dotty deviation: Can't use new AddrToPosition here. TODO: fix this!
- val sourceLength = readNat()
- def readDelta() = if (isAtEnd) 0 else readInt()
- var curIndex: Addr = Addr(readDelta())
+ private[tasty] lazy val positions = {
+ val positions = new mutable.HashMap[Addr, Position]
+ var curIndex = 0
+ var curStart = 0
+ var curEnd = 0
while (!isAtEnd) {
- val delta1 = readDelta()
- val delta2 = readDelta()
- val (startDelta, endDelta, indexDelta) =
- if (delta2 <= 0) (delta1, -delta2, readDelta())
- else if (delta1 < 0) (0, -delta1, delta2)
- else (delta1, 0, delta2)
- positions(curIndex) = Position(startDelta, endDelta, startDelta)
- // make non-synthetic position; will be made synthetic by normalization.
- curIndex += indexDelta
+ val header = readInt()
+ val addrDelta = header >> 2
+ val hasStart = (header & 2) != 0
+ val hasEnd = (header & 1) != 0
+ curIndex += addrDelta
+ assert(curIndex >= 0)
+ if (hasStart) curStart += readInt()
+ if (hasEnd) curEnd += readInt()
+ positions(Addr(curIndex)) = Position(curStart, curEnd)
}
- (Position(0, sourceLength), positions)
+ positions
}
+
+ def posAt(addr: Addr) = positions.getOrElse(addr, NoPosition)
}
+
diff --git a/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/src/dotty/tools/dotc/core/tasty/TastyFormat.scala
index e9de68e7f..8e8d58b47 100644
--- a/src/dotty/tools/dotc/core/tasty/TastyFormat.scala
+++ b/src/dotty/tools/dotc/core/tasty/TastyFormat.scala
@@ -185,21 +185,16 @@ Note: Tree tags are grouped into 5 categories that determine what follows, and t
Category 4 (tags 112-127): tag Nat AST
Category 5 (tags 128-255): tag Length <payload>
-Standard Section: "Positions" sourceLength_Nat Assoc*
-
- Assoc = addr_Delta offset_Delta offset_Delta?
- // addr_Delta :
- // Difference of address to last recorded node.
- // All but the first addr_Deltas are > 0, the first is >= 0.
- // 2nd offset_Delta:
- // Difference of end offset of addressed node vs parent node. Always <= 0
- // 1st offset Delta, if delta >= 0 or 2nd offset delta exists
- // Difference of start offset of addressed node vs parent node.
- // 1st offset Delta, if delta < 0 and 2nd offset delta does not exist:
- // Difference of end offset of addressed node vs parent node.
- // Offsets and addresses are difference encoded.
+Standard Section: "Positions" Assoc*
+
+ Assoc = Header offset_Delta? offset_Delta?
+ Header = addr_Delta + // in one Nat: difference of address to last recorded node << 2 +
+ hasStartDiff + // one bit indicating whether there follows a start address delta << 1
+ hasEndDiff // one bit indicating whether there follows an end address delta
// Nodes which have the same positions as their parents are omitted.
- Delta = Int // Difference between consecutive offsets / tree addresses,
+ // offset_Deltas give difference of start/end offset wrt to the
+ // same offset in the previously recorded node (or 0 for the first recorded node)
+ Delta = Int // Difference between consecutive offsets,
**************************************************************************************/
diff --git a/src/dotty/tools/dotc/core/tasty/TastyPickler.scala b/src/dotty/tools/dotc/core/tasty/TastyPickler.scala
index 83e6020d5..98b0dc7c6 100644
--- a/src/dotty/tools/dotc/core/tasty/TastyPickler.scala
+++ b/src/dotty/tools/dotc/core/tasty/TastyPickler.scala
@@ -31,7 +31,6 @@ class TastyPickler {
sections += ((nameBuffer.nameIndex(name), buf))
def assembleParts(): Array[Byte] = {
- treePkl.compactify()
def lengthWithLength(buf: TastyBuffer) = {
buf.assemble()
buf.length + natSize(buf.length)
diff --git a/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala b/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala
index 915ae3f21..7fcd7c29e 100644
--- a/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala
+++ b/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala
@@ -113,8 +113,8 @@ class TastyPrinter(bytes: Array[Byte])(implicit ctx: Context) {
class PositionSectionUnpickler extends SectionUnpickler[Unit]("Positions") {
def unpickle(reader: TastyReader, tastyName: TastyName.Table): Unit = {
print(s"${reader.endAddr.index - reader.currentAddr.index}")
- val (totalRange, positions) = new PositionUnpickler(reader).unpickle()
- println(s" position bytes in $totalRange:")
+ val positions = new PositionUnpickler(reader).positions
+ println(s" position bytes:")
val sorted = positions.toSeq.sortBy(_._1.index)
for ((addr, pos) <- sorted) println(s"${addr.index}: ${offsetToInt(pos.start)} .. ${pos.end}")
}
diff --git a/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala
index e5cacfc00..b6f52c0ec 100644
--- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala
+++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala
@@ -7,6 +7,7 @@ import ast.Trees._
import TastyFormat._
import Contexts._, Symbols._, Types._, Names._, Constants._, Decorators._, Annotations._, StdNames.tpnme, NameOps._
import collection.mutable
+import typer.Inliner
import NameOps._
import TastyBuffer._
import TypeApplications._
@@ -307,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 {
@@ -430,6 +431,15 @@ class TreePickler(pickler: TastyPickler) {
case SeqLiteral(elems, elemtpt) =>
writeByte(REPEATED)
withLength { pickleTree(elemtpt); elems.foreach(pickleTree) }
+ case tree: Inlined =>
+ // Why drop Inlined info when pickling?
+ // Since we never inline inside an inlined method, we know that
+ // any code that continas an Inlined tree is not inlined itself.
+ // So position information for inline expansion is no longer needed.
+ // The only reason to keep the inline info around would be to have fine-grained
+ // position information in the linker. We should come back to this
+ // point once we know more what we would do with such information.
+ pickleTree(Inliner.dropInlined(tree))
case TypeTree(original) =>
pickleTpt(tree)
case Bind(name, body) =>
@@ -555,19 +565,19 @@ class TreePickler(pickler: TastyPickler) {
sym.annotations.foreach(pickleAnnotation)
}
- def pickleAnnotation(ann: Annotation)(implicit ctx: Context) = {
- writeByte(ANNOTATION)
- withLength { pickleType(ann.symbol.typeRef); pickleTree(ann.tree) }
- }
+ def pickleAnnotation(ann: Annotation)(implicit ctx: Context) =
+ if (ann.symbol != defn.BodyAnnot) { // inline bodies are reconstituted automatically when unpickling
+ writeByte(ANNOTATION)
+ withLength { pickleType(ann.symbol.typeRef); pickleTree(ann.tree) }
+ }
def pickle(trees: List[Tree])(implicit ctx: Context) = {
trees.foreach(tree => if (!tree.isEmpty) pickleTree(tree))
- assert(forwardSymRefs.isEmpty, i"unresolved symbols: ${forwardSymRefs.keySet.toList}%, %")
+ assert(forwardSymRefs.isEmpty, i"unresolved symbols: ${forwardSymRefs.keySet.toList}%, % when pickling ${ctx.source}")
}
def compactify() = {
buf.compactify()
- assert(forwardSymRefs.isEmpty, s"unresolved symbols: ${forwardSymRefs.keySet.toList}%, %")
def updateMapWithDeltas[T](mp: collection.mutable.Map[T, Addr]) =
for (key <- mp.keysIterator.toBuffer[T]) mp(key) = adjusted(mp(key))
diff --git a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
index 56bb8498a..09f2c0d1f 100644
--- a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
+++ b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
@@ -9,38 +9,23 @@ import util.Positions._
import ast.{tpd, Trees, untpd}
import Trees._
import Decorators._
-import TastyUnpickler._, TastyBuffer._, PositionPickler._
+import TastyUnpickler._, TastyBuffer._
import scala.annotation.{tailrec, switch}
import scala.collection.mutable.ListBuffer
import scala.collection.{ mutable, immutable }
import config.Printers.pickling
/** Unpickler for typed trees
- * @param reader the reader from which to unpickle
- * @param tastyName the nametable
+ * @param reader the reader from which to unpickle
+ * @param tastyName the nametable
+ * @param posUNpicklerOpt the unpickler for positions, if it exists
*/
-class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
+class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpicklerOpt: Option[PositionUnpickler]) {
import TastyFormat._
import TastyName._
import TreeUnpickler._
import tpd._
- private var readPositions = false
- private var totalRange = NoPosition
- private var positions: collection.Map[Addr, Position] = _
-
- /** Make a subsequent call to `unpickle` return trees with positions
- * @param totalRange the range position enclosing all returned trees,
- * or NoPosition if positions should not be unpickled
- * @param positions a map from tree addresses to their positions relative
- * to positions of parent nodes.
- */
- def usePositions(totalRange: Position, positions: collection.Map[Addr, Position]): Unit = {
- readPositions = true
- this.totalRange = totalRange
- this.positions = positions
- }
-
/** A map from addresses of definition entries to the symbols they define */
private val symAtAddr = new mutable.HashMap[Addr, Symbol]
@@ -85,10 +70,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
/** The unpickled trees */
def unpickle()(implicit ctx: Context): List[Tree] = {
assert(roots != null, "unpickle without previous enterTopLevel")
- val stats = new TreeReader(reader)
- .readTopLevel()(ctx.addMode(Mode.AllowDependentFunctions))
- normalizePos(stats, totalRange)
- stats
+ new TreeReader(reader).readTopLevel()(ctx.addMode(Mode.AllowDependentFunctions))
}
def toTermName(tname: TastyName): TermName = tname match {
@@ -468,6 +450,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
val isClass = ttag == TEMPLATE
val templateStart = currentAddr
skipTree() // tpt
+ val rhsStart = currentAddr
val rhsIsEmpty = noRhs(end)
if (!rhsIsEmpty) skipTree()
val (givenFlags, annots, privateWithin) = readModifiers(end)
@@ -504,6 +487,12 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
sym.completer.withDecls(newScope)
forkAt(templateStart).indexTemplateParams()(localContext(sym))
}
+ else if (annots.exists(_.symbol == defn.InlineAnnot))
+ sym.addAnnotation(LazyBodyAnnotation { ctx0 =>
+ implicit val ctx: Context = localContext(sym)(ctx0).addMode(Mode.ReadPositions)
+ // avoids space leaks by not capturing the current context
+ forkAt(rhsStart).readTerm()
+ })
goto(start)
sym
}
@@ -1010,44 +999,29 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
new LazyReader(localReader, op)
}
-// ------ Hooks for positions ------------------------------------------------
+// ------ Setting positions ------------------------------------------------
- /** Record address from which tree was created as a temporary position in the tree.
- * The temporary position contains deltas relative to the position of the (as yet unknown)
- * parent node. It is marked as a non-synthetic source position.
- */
- def setPos[T <: Tree](addr: Addr, tree: T): T = {
- if (readPositions)
- tree.setPosUnchecked(positions.getOrElse(addr, Position(0, 0, 0)))
- tree
- }
+ /** Set position of `tree` at given `addr`. */
+ def setPos[T <: Tree](addr: Addr, tree: T)(implicit ctx: Context): tree.type =
+ if (ctx.mode.is(Mode.ReadPositions)) {
+ posUnpicklerOpt match {
+ case Some(posUnpickler) => tree.withPos(posUnpickler.posAt(addr))
+ case _ => tree
+ }
+ }
+ else tree
}
- private def setNormalized(tree: Tree, parentPos: Position): Unit =
- tree.setPosUnchecked(
- if (tree.pos.exists)
- Position(parentPos.start + offsetToInt(tree.pos.start), parentPos.end - tree.pos.end)
- else
- parentPos)
-
- def normalizePos(x: Any, parentPos: Position)(implicit ctx: Context): Unit =
- traverse(x, parentPos, setNormalized)
-
- class LazyReader[T <: AnyRef](reader: TreeReader, op: TreeReader => Context => T) extends Trees.Lazy[T] with DeferredPosition {
+ class LazyReader[T <: AnyRef](reader: TreeReader, op: TreeReader => Context => T) extends Trees.Lazy[T] {
def complete(implicit ctx: Context): T = {
pickling.println(i"starting to read at ${reader.reader.currentAddr}")
- val res = op(reader)(ctx.addMode(Mode.AllowDependentFunctions).withPhaseNoLater(ctx.picklerPhase))
- normalizePos(res, parentPos)
- res
+ op(reader)(ctx.addMode(Mode.AllowDependentFunctions).withPhaseNoLater(ctx.picklerPhase))
}
}
- class LazyAnnotationReader(sym: Symbol, reader: TreeReader)
- extends LazyAnnotation(sym) with DeferredPosition {
+ class LazyAnnotationReader(sym: Symbol, reader: TreeReader) extends LazyAnnotation(sym) {
def complete(implicit ctx: Context) = {
- val res = reader.readTerm()(ctx.withPhaseNoLater(ctx.picklerPhase))
- normalizePos(res, parentPos)
- res
+ reader.readTerm()(ctx.withPhaseNoLater(ctx.picklerPhase))
}
}
diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala
index 86330f3ab..0a25bf801 100644
--- a/src/dotty/tools/dotc/parsing/Parsers.scala
+++ b/src/dotty/tools/dotc/parsing/Parsers.scala
@@ -275,21 +275,12 @@ object Parsers {
} finally inFunReturnType = saved
}
- private val isScala2Mode =
- ctx.settings.language.value.contains(nme.Scala2.toString)
-
def migrationWarningOrError(msg: String, offset: Int = in.offset) =
- if (isScala2Mode)
+ if (in.isScala2Mode)
ctx.migrationWarning(msg, source atPos Position(offset))
else
syntaxError(msg, offset)
- /** Cannot use ctx.featureEnabled because accessing the context would force too much */
- private def testScala2Mode(msg: String, pos: Position = Position(in.offset)) = {
- if (isScala2Mode) ctx.migrationWarning(msg, source atPos pos)
- isScala2Mode
- }
-
/* ---------- TREE CONSTRUCTION ------------------------------------------- */
/** Convert tree to formal parameter list
@@ -1467,6 +1458,7 @@ object Parsers {
case ABSTRACT => Abstract
case FINAL => Final
case IMPLICIT => ImplicitCommon
+ case INLINE => Inline
case LAZY => Lazy
case OVERRIDE => Override
case PRIVATE => Private
@@ -1570,7 +1562,10 @@ object Parsers {
/** Annotation ::= `@' SimpleType {ParArgumentExprs}
*/
def annot() =
- adjustStart(accept(AT)) { ensureApplied(parArgumentExprss(wrapNew(simpleType()))) }
+ adjustStart(accept(AT)) {
+ if (in.token == INLINE) in.token = BACKQUOTED_IDENT // allow for now
+ ensureApplied(parArgumentExprss(wrapNew(simpleType())))
+ }
def annotations(skipNewLines: Boolean = false): List[Tree] = {
if (skipNewLines) newLineOptWhenFollowedBy(AT)
@@ -1646,12 +1641,13 @@ object Parsers {
/** ClsParamClauses ::= {ClsParamClause} [[nl] `(' `implicit' ClsParams `)']
* ClsParamClause ::= [nl] `(' [ClsParams] ')'
* ClsParams ::= ClsParam {`' ClsParam}
- * ClsParam ::= {Annotation} [{Modifier} (`val' | `var')] id `:' ParamType [`=' Expr]
+ * ClsParam ::= {Annotation} [{Modifier} (`val' | `var') | `inline'] Param
* DefParamClauses ::= {DefParamClause} [[nl] `(' `implicit' DefParams `)']
* DefParamClause ::= [nl] `(' [DefParams] ')'
* DefParams ::= DefParam {`,' DefParam}
- * DefParam ::= {Annotation} id `:' ParamType [`=' Expr]
- */
+ * DefParam ::= {Annotation} [`inline'] Param
+ * Param ::= id `:' ParamType [`=' Expr]
+ */
def paramClauses(owner: Name, ofCaseClass: Boolean = false): List[List[ValDef]] = {
var implicitFlag = EmptyFlags
var firstClauseOfCaseClass = ofCaseClass
@@ -1670,12 +1666,16 @@ object Parsers {
in.nextToken()
addFlag(mods, Mutable)
} else {
- if (!(mods.flags &~ ParamAccessor).isEmpty) syntaxError("`val' or `var' expected")
+ if (!(mods.flags &~ (ParamAccessor | Inline)).isEmpty)
+ syntaxError("`val' or `var' expected")
if (firstClauseOfCaseClass) mods else mods | PrivateLocal
}
}
}
- else mods = atPos(start) { mods | Param }
+ else {
+ if (in.token == INLINE) mods = addModifier(mods)
+ mods = atPos(start) { mods | Param }
+ }
atPos(start, nameStart) {
val name = ident()
val tpt =
@@ -1856,7 +1856,7 @@ object Parsers {
val toInsert =
if (in.token == LBRACE) s"$resultTypeStr ="
else ": Unit " // trailing space ensures that `def f()def g()` works.
- testScala2Mode(s"Procedure syntax no longer supported; `$toInsert' should be inserted here") && {
+ in.testScala2Mode(s"Procedure syntax no longer supported; `$toInsert' should be inserted here") && {
patch(source, Position(in.lastOffset), toInsert)
true
}
diff --git a/src/dotty/tools/dotc/parsing/Scanners.scala b/src/dotty/tools/dotc/parsing/Scanners.scala
index b46ab6348..e16aa670f 100644
--- a/src/dotty/tools/dotc/parsing/Scanners.scala
+++ b/src/dotty/tools/dotc/parsing/Scanners.scala
@@ -12,7 +12,7 @@ import scala.annotation.{ switch, tailrec }
import scala.collection.mutable
import mutable.ListBuffer
import Utility.isNameStart
-
+import rewrite.Rewrites.patch
object Scanners {
@@ -108,6 +108,7 @@ object Scanners {
target.token = toToken(idx)
}
}
+
def toToken(idx: Int): Token
/** Clear buffer and set string */
@@ -212,8 +213,22 @@ object Scanners {
/** A buffer for comments */
val commentBuf = new StringBuilder
+ private def handleMigration(keyword: Token): Token =
+ if (!isScala2Mode) keyword
+ else if (keyword == INLINE) treatAsIdent()
+ else keyword
+
+
+ private def treatAsIdent() = {
+ testScala2Mode(i"$name is now a keyword, write `$name` instead of $name to keep it as an identifier")
+ patch(source, Position(offset), "`")
+ patch(source, Position(offset + name.length), "`")
+ IDENTIFIER
+ }
+
def toToken(idx: Int): Token =
- if (idx >= 0 && idx <= lastKeywordStart) kwArray(idx) else IDENTIFIER
+ if (idx >= 0 && idx <= lastKeywordStart) handleMigration(kwArray(idx))
+ else IDENTIFIER
private class TokenData0 extends TokenData
@@ -235,6 +250,16 @@ object Scanners {
*/
var sepRegions: List[Token] = List()
+// Scala 2 compatibility
+
+ val isScala2Mode = ctx.settings.language.value.contains(nme.Scala2.toString)
+
+ /** Cannot use ctx.featureEnabled because accessing the context would force too much */
+ def testScala2Mode(msg: String, pos: Position = Position(offset)) = {
+ if (isScala2Mode) ctx.migrationWarning(msg, source atPos pos)
+ isScala2Mode
+ }
+
// Get next token ------------------------------------------------------------
/** Are we directly in a string interpolation expression?
diff --git a/src/dotty/tools/dotc/parsing/Tokens.scala b/src/dotty/tools/dotc/parsing/Tokens.scala
index b490cd133..5324207db 100644
--- a/src/dotty/tools/dotc/parsing/Tokens.scala
+++ b/src/dotty/tools/dotc/parsing/Tokens.scala
@@ -91,6 +91,7 @@ abstract class TokensCommon {
//final val LAZY = 59; enter(LAZY, "lazy")
//final val THEN = 60; enter(THEN, "then")
//final val FORSOME = 61; enter(FORSOME, "forSome") // TODO: deprecate
+ //final val INLINE = 62; enter(INLINE, "inline")
/** special symbols */
final val COMMA = 70; enter(COMMA, "','")
@@ -171,6 +172,7 @@ object Tokens extends TokensCommon {
final val LAZY = 59; enter(LAZY, "lazy")
final val THEN = 60; enter(THEN, "then")
final val FORSOME = 61; enter(FORSOME, "forSome") // TODO: deprecate
+ final val INLINE = 62; enter(INLINE, "inline")
/** special symbols */
final val NEWLINE = 78; enter(NEWLINE, "end of statement", "new line")
@@ -188,7 +190,7 @@ object Tokens extends TokensCommon {
/** XML mode */
final val XMLSTART = 96; enter(XMLSTART, "$XMLSTART$<") // TODO: deprecate
- final val alphaKeywords = tokenRange(IF, FORSOME)
+ final val alphaKeywords = tokenRange(IF, INLINE)
final val symbolicKeywords = tokenRange(USCORE, VIEWBOUND)
final val symbolicTokens = tokenRange(COMMA, VIEWBOUND)
final val keywords = alphaKeywords | symbolicKeywords
@@ -214,7 +216,7 @@ object Tokens extends TokensCommon {
final val defIntroTokens = templateIntroTokens | dclIntroTokens
final val localModifierTokens = BitSet(
- ABSTRACT, FINAL, SEALED, IMPLICIT, LAZY)
+ ABSTRACT, FINAL, SEALED, IMPLICIT, INLINE, LAZY)
final val accessModifierTokens = BitSet(
PRIVATE, PROTECTED)
diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala
index 205d2b6b9..4f3a8d272 100644
--- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala
+++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala
@@ -7,7 +7,7 @@ import TypeErasure.ErasedValueType
import Contexts.Context, Scopes.Scope, Denotations._, SymDenotations._, Annotations.Annotation
import StdNames.{nme, tpnme}
import ast.{Trees, untpd, tpd}
-import typer.Namer
+import typer.{Namer, Inliner}
import typer.ProtoTypes.{SelectionProto, ViewProto, FunProto, IgnoredProto, dummyTreeOfType}
import Trees._
import TypeApplications._
@@ -154,7 +154,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
}
def blockText[T >: Untyped](trees: List[Tree[T]]): Text =
- "{" ~ toText(trees, "\n") ~ "}"
+ ("{" ~ toText(trees, "\n") ~ "}").close
override def toText[T >: Untyped](tree: Tree[T]): Text = controlled {
@@ -277,7 +277,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
if (homogenizedView && pid.hasType) toTextLocal(pid.tpe)
else toTextLocal(pid)
- var txt: Text = tree match {
+ def toTextCore(tree: Tree): Text = tree match {
case id: Trees.BackquotedIdent[_] if !homogenizedView =>
"`" ~ toText(id.name) ~ "`"
case Ident(name) =>
@@ -351,6 +351,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
}
case SeqLiteral(elems, elemtpt) =>
"[" ~ toTextGlobal(elems, ",") ~ " : " ~ toText(elemtpt) ~ "]"
+ case tree @ Inlined(call, bindings, body) =>
+ if (homogenizedView) toTextCore(Inliner.dropInlined(tree.asInstanceOf[tpd.Inlined]))
+ else "/* inlined from " ~ toText(call) ~ "*/ " ~ blockText(bindings :+ body)
case tpt: untpd.DerivedTypeTree =>
"<derived typetree watching " ~ summarized(toText(tpt.watched)) ~ ">"
case TypeTree(orig) =>
@@ -514,13 +517,14 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
case _ =>
tree.fallbackToText(this)
}
+ var txt = toTextCore(tree)
if (ctx.settings.printtypes.value && tree.hasType) {
val tp = tree.typeOpt match {
case tp: TermRef if tree.isInstanceOf[RefTree] && !tp.denot.isOverloaded => tp.underlying
case tp => tp
}
if (tree.isType) txt = toText(tp)
- else if (!tree.isDef) txt = "<" ~ txt ~ ":" ~ toText(tp) ~ ">"
+ else if (!tree.isDef) txt = ("<" ~ txt ~ ":" ~ toText(tp) ~ ">").close
}
if (ctx.settings.Yprintpos.value && !tree.isInstanceOf[WithoutTypeOrPos[_]])
txt = txt ~ "@" ~ tree.pos.toString
diff --git a/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala b/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala
index 67aa24243..83c428976 100644
--- a/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala
+++ b/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala
@@ -22,7 +22,7 @@ object SyntaxHighlighting {
private def annotation(str: String) = AnnotationColor + str + NoColor
private val keywords: Seq[String] = for {
- index <- IF to FORSOME // All alpha keywords
+ index <- IF to INLINE // All alpha keywords
} yield tokenString(index)
private val interpolationPrefixes =
diff --git a/src/dotty/tools/dotc/reporting/ConsoleReporter.scala b/src/dotty/tools/dotc/reporting/ConsoleReporter.scala
index f35293d8d..deb772db5 100644
--- a/src/dotty/tools/dotc/reporting/ConsoleReporter.scala
+++ b/src/dotty/tools/dotc/reporting/ConsoleReporter.scala
@@ -21,11 +21,15 @@ class ConsoleReporter(
/** maximal number of error messages to be printed */
protected def ErrorLimit = 100
- def printSourceLine(pos: SourcePosition) =
- printMessage(pos.lineContent.stripLineEnd)
-
- def printColumnMarker(pos: SourcePosition) =
- if (pos.exists) { printMessage(" " * pos.column + "^") }
+ def printPos(pos: SourcePosition): Unit =
+ if (pos.exists) {
+ printMessage(pos.lineContent.stripLineEnd)
+ printMessage(" " * pos.column + "^")
+ if (pos.outer.exists) {
+ printMessage(s"\n... this location is in code that was inlined at ${pos.outer}:\n")
+ printPos(pos.outer)
+ }
+ }
/** Prints the message. */
def printMessage(msg: String): Unit = { writer.print(msg + "\n"); writer.flush() }
@@ -34,10 +38,7 @@ class ConsoleReporter(
def printMessageAndPos(msg: String, pos: SourcePosition)(implicit ctx: Context): Unit = {
val posStr = if (pos.exists) s"$pos: " else ""
printMessage(posStr + msg)
- if (pos.exists) {
- printSourceLine(pos)
- printColumnMarker(pos)
- }
+ printPos(pos)
}
override def doReport(d: Diagnostic)(implicit ctx: Context): Unit = d match {
diff --git a/src/dotty/tools/dotc/reporting/Reporter.scala b/src/dotty/tools/dotc/reporting/Reporter.scala
index b3d173a42..75113d823 100644
--- a/src/dotty/tools/dotc/reporting/Reporter.scala
+++ b/src/dotty/tools/dotc/reporting/Reporter.scala
@@ -140,28 +140,32 @@ trait Reporting { this: Context =>
def debugwarn(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
if (this.settings.debug.value) warning(msg, pos)
- def debugTraceIndented[T](question: => String, printer: Printers.Printer = Printers.default, show: Boolean = false)(op: => T): T =
+ @inline
+ def debugTraceIndented[TD](question: => String, printer: Printers.Printer = Printers.default, show: Boolean = false)(op: => TD): TD =
conditionalTraceIndented(this.settings.debugTrace.value, question, printer, show)(op)
- def conditionalTraceIndented[T](cond: Boolean, question: => String, printer: Printers.Printer = Printers.default, show: Boolean = false)(op: => T): T =
- if (cond) traceIndented(question, printer, show)(op)
+ @inline
+ def conditionalTraceIndented[TC](cond: Boolean, question: => String, printer: Printers.Printer = Printers.default, show: Boolean = false)(op: => TC): TC =
+ if (cond) traceIndented[TC](question, printer, show)(op)
else op
- def traceIndented[T](question: => String, printer: Printers.Printer = Printers.default, show: Boolean = false)(op: => T): T = {
+ @inline
+ def traceIndented[T](question: => String, printer: Printers.Printer = Printers.default, show: Boolean = false)(op: => T): T =
+ if (printer eq config.Printers.noPrinter) op
+ else doTraceIndented[T](question, printer, show)(op)
+
+ 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)
}
- if (printer eq config.Printers.noPrinter) op
- else {
- // Avoid evaluating question multiple time, since each evaluation
- // may cause some extra logging output.
- lazy val q: String = question
- traceIndented[T](s"==> $q?", (res: Any) => s"<== $q = ${resStr(res)}")(op)
- }
+ // Avoid evaluating question multiple time, since each evaluation
+ // may cause some extra logging output.
+ lazy val q: String = question
+ doTraceIndented[T](s"==> $q?", (res: Any) => s"<== $q = ${resStr(res)}")(op)
}
- def traceIndented[T](leading: => String, trailing: Any => String)(op: => T): T =
+ def doTraceIndented[T](leading: => String, trailing: Any => String)(op: => T): T =
if (ctx.mode.is(Mode.Printing)) op
else {
var finalized = false
diff --git a/src/dotty/tools/dotc/sbt/ExtractAPI.scala b/src/dotty/tools/dotc/sbt/ExtractAPI.scala
index 437e36bb9..a7b18b6d6 100644
--- a/src/dotty/tools/dotc/sbt/ExtractAPI.scala
+++ b/src/dotty/tools/dotc/sbt/ExtractAPI.scala
@@ -5,6 +5,7 @@ import ast.{Trees, tpd}
import core._, core.Decorators._
import Contexts._, Flags._, Phases._, Trees._, Types._, Symbols._
import Names._, NameOps._, StdNames._
+import typer.Inliner
import dotty.tools.io.Path
import java.io.PrintWriter
@@ -497,6 +498,21 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder
sym.is(Implicit), sym.is(Lazy), sym.is(Macro), sym.is(SuperAccessor))
}
- // TODO: Annotation support
- def apiAnnotations(s: Symbol): List[api.Annotation] = Nil
+ // TODO: Support other annotations
+ def apiAnnotations(s: Symbol): List[api.Annotation] = {
+ val annots = new mutable.ListBuffer[api.Annotation]
+
+ if (Inliner.hasBodyToInline(s)) {
+ // FIXME: If the body of an inline method changes, all the reverse
+ // dependencies of this method need to be recompiled. sbt has no way
+ // of tracking method bodies, so as a hack we include the pretty-printed
+ // typed tree of the method as part of the signature we send to sbt.
+ // To do this properly we would need a way to hash trees and types in
+ // dotty itself.
+ val printTypesCtx = ctx.fresh.setSetting(ctx.settings.printtypes, true)
+ annots += marker(Inliner.bodyToInline(s).show(printTypesCtx).toString)
+ }
+
+ annots.toList
+ }
}
diff --git a/src/dotty/tools/dotc/sbt/ExtractDependencies.scala b/src/dotty/tools/dotc/sbt/ExtractDependencies.scala
index a36b47aa8..229e35360 100644
--- a/src/dotty/tools/dotc/sbt/ExtractDependencies.scala
+++ b/src/dotty/tools/dotc/sbt/ExtractDependencies.scala
@@ -190,6 +190,10 @@ private class ExtractDependenciesCollector(implicit val ctx: Context) extends tp
addUsedName(rename)
case _ =>
}
+ case Inlined(call, _, _) =>
+ // The inlined call is normally ignored by TreeTraverser but we need to
+ // record it as a dependency
+ traverse(call)
case t: TypeTree =>
usedTypeTraverser.traverse(t.tpe)
case ref: RefTree =>
diff --git a/src/dotty/tools/dotc/transform/DropInlined.scala b/src/dotty/tools/dotc/transform/DropInlined.scala
new file mode 100644
index 000000000..775663b5c
--- /dev/null
+++ b/src/dotty/tools/dotc/transform/DropInlined.scala
@@ -0,0 +1,15 @@
+package dotty.tools.dotc
+package transform
+
+import typer.Inliner
+import core.Contexts.Context
+import TreeTransforms.{MiniPhaseTransform, TransformerInfo}
+
+/** Drop Inlined nodes */
+class DropInlined extends MiniPhaseTransform {
+ import ast.tpd._
+ override def phaseName = "dropInlined"
+
+ override def transformInlined(tree: Inlined)(implicit ctx: Context, info: TransformerInfo): Tree =
+ Inliner.dropInlined(tree)
+}
diff --git a/src/dotty/tools/dotc/transform/ElimErasedValueType.scala b/src/dotty/tools/dotc/transform/ElimErasedValueType.scala
index a3f8b56ff..24c8cdc8d 100644
--- a/src/dotty/tools/dotc/transform/ElimErasedValueType.scala
+++ b/src/dotty/tools/dotc/transform/ElimErasedValueType.scala
@@ -67,6 +67,9 @@ class ElimErasedValueType extends MiniPhaseTransform with InfoTransformer {
transformTypeOfTree(t)
}
+ override def transformInlined(tree: Inlined)(implicit ctx: Context, info: TransformerInfo): Tree =
+ transformTypeOfTree(tree)
+
// FIXME: transformIf and transformBlock won't be required anymore once #444 is fixed.
override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree =
transformTypeOfTree(tree)
diff --git a/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/src/dotty/tools/dotc/transform/ExplicitOuter.scala
index 6a52b128c..3f235dca7 100644
--- a/src/dotty/tools/dotc/transform/ExplicitOuter.scala
+++ b/src/dotty/tools/dotc/transform/ExplicitOuter.scala
@@ -15,7 +15,7 @@ import ast.Trees._
import SymUtils._
import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.core.Phases.Phase
-import util.Attachment
+import util.Property
import collection.mutable
/** This phase adds outer accessors to classes and traits that need them.
@@ -36,7 +36,7 @@ class ExplicitOuter extends MiniPhaseTransform with InfoTransformer { thisTransf
import ExplicitOuter._
import ast.tpd._
- val Outer = new Attachment.Key[Tree]
+ val Outer = new Property.Key[Tree]
override def phaseName: String = "explicitOuter"
@@ -57,6 +57,12 @@ class ExplicitOuter extends MiniPhaseTransform with InfoTransformer { thisTransf
override def mayChange(sym: Symbol)(implicit ctx: Context): Boolean = sym.isClass
+ /** Convert a selection of the form `qual.C_<OUTER>` to an outer path from `qual` to `C` */
+ override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) =
+ if (tree.name.isOuterSelect)
+ outer.path(tree.tpe.widen.classSymbol, tree.qualifier).ensureConforms(tree.tpe)
+ else tree
+
/** First, add outer accessors if a class does not have them yet and it references an outer this.
* If the class has outer accessors, implement them.
* Furthermore, if a parent trait might have an outer accessor,
diff --git a/src/dotty/tools/dotc/transform/ExplicitSelf.scala b/src/dotty/tools/dotc/transform/ExplicitSelf.scala
index c6a218157..618a0f108 100644
--- a/src/dotty/tools/dotc/transform/ExplicitSelf.scala
+++ b/src/dotty/tools/dotc/transform/ExplicitSelf.scala
@@ -16,9 +16,10 @@ import Flags._
* where `C` is a class with explicit self type and `C` is not a
* subclass of the owner of `m` to
*
- * C.this.asInstanceOf[S].m
+ * C.this.asInstanceOf[S & C.this.type].m
*
* where `S` is the self type of `C`.
+ * See run/i789.scala for a test case why this is needed.
*/
class ExplicitSelf extends MiniPhaseTransform { thisTransform =>
import ast.tpd._
@@ -30,7 +31,7 @@ class ExplicitSelf extends MiniPhaseTransform { thisTransform =>
val cls = thiz.symbol.asClass
val cinfo = cls.classInfo
if (cinfo.givenSelfType.exists && !cls.derivesFrom(tree.symbol.owner))
- cpy.Select(tree)(thiz.asInstance(cinfo.selfType), name)
+ cpy.Select(tree)(thiz.asInstance(AndType(cinfo.selfType, thiz.tpe)), name)
else tree
case _ => tree
}
diff --git a/src/dotty/tools/dotc/transform/FirstTransform.scala b/src/dotty/tools/dotc/transform/FirstTransform.scala
index 6e1fed607..74dc9b9d6 100644
--- a/src/dotty/tools/dotc/transform/FirstTransform.scala
+++ b/src/dotty/tools/dotc/transform/FirstTransform.scala
@@ -72,8 +72,8 @@ class FirstTransform extends MiniPhaseTransform with InfoTransformer with Annota
override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = {
tree match {
- case Select(qual, _) if tree.symbol.exists =>
- assert(qual.tpe derivesFrom tree.symbol.owner, i"non member selection of ${tree.symbol.showLocated} from ${qual.tpe}")
+ case Select(qual, name) if !name.isOuterSelect && tree.symbol.exists =>
+ assert(qual.tpe derivesFrom tree.symbol.owner, i"non member selection of ${tree.symbol.showLocated} from ${qual.tpe} in $tree")
case _: TypeTree =>
case _: Import | _: NamedArg | _: TypTree =>
assert(false, i"illegal tree: $tree")
diff --git a/src/dotty/tools/dotc/transform/PatternMatcher.scala b/src/dotty/tools/dotc/transform/PatternMatcher.scala
index 490feb7d0..49c0eabec 100644
--- a/src/dotty/tools/dotc/transform/PatternMatcher.scala
+++ b/src/dotty/tools/dotc/transform/PatternMatcher.scala
@@ -782,6 +782,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {
val expectedClass = expectedTp.dealias.classSymbol.asClass
val test = codegen._asInstanceOf(testedBinder, expectedTp)
+ // TODO: Use nme.OUTER_SELECT, like the Inliner does?
val outerAccessorTested = ctx.atPhase(ctx.explicitOuterPhase.next) { implicit ctx =>
ExplicitOuter.ensureOuterAccessors(expectedClass)
test.select(ExplicitOuter.outerAccessor(expectedClass)).select(defn.Object_eq).appliedTo(expectedOuter)
diff --git a/src/dotty/tools/dotc/transform/Pickler.scala b/src/dotty/tools/dotc/transform/Pickler.scala
index 4bcc90a41..90e62b65c 100644
--- a/src/dotty/tools/dotc/transform/Pickler.scala
+++ b/src/dotty/tools/dotc/transform/Pickler.scala
@@ -45,10 +45,11 @@ class Pickler extends Phase {
unit.picklers += (cls -> pickler)
val treePkl = pickler.treePkl
treePkl.pickle(tree :: Nil)
+ treePkl.compactify()
pickler.addrOfTree = treePkl.buf.addrOfTree
pickler.addrOfSym = treePkl.addrOfSym
if (tree.pos.exists)
- new PositionPickler(pickler, treePkl.buf.addrOfTree).picklePositions(tree :: Nil, tree.pos)
+ new PositionPickler(pickler, treePkl.buf.addrOfTree).picklePositions(tree :: Nil)
def rawBytes = // not needed right now, but useful to print raw format.
pickler.assembleParts().iterator.grouped(10).toList.zipWithIndex.map {
@@ -80,7 +81,7 @@ class Pickler extends Phase {
}
pickling.println("************* entered toplevel ***********")
for ((cls, unpickler) <- unpicklers) {
- val unpickled = unpickler.body(readPositions = true)
+ val unpickled = unpickler.body(ctx.addMode(Mode.ReadPositions))
testSame(i"$unpickled%\n%", beforePickling(cls), cls)
}
}
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/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala
index 6af991f27..10be6db65 100644
--- a/src/dotty/tools/dotc/transform/SuperAccessors.scala
+++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala
@@ -148,7 +148,7 @@ class SuperAccessors(thisTransformer: DenotTransformer) {
*/
private def ensureProtectedAccessOK(sel: Select, targs: List[Tree])(implicit ctx: Context) = {
val sym = sel.symbol
- if (sym.exists && needsProtectedAccessor(sym, sel.pos)) {
+ if (sym.isTerm && !sel.name.isOuterSelect && needsProtectedAccessor(sym, sel.pos)) {
ctx.debuglog("Adding protected accessor for " + sel)
protectedAccessorCall(sel, targs)
} else sel
diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala
index e7342aec9..4b3927ccf 100644
--- a/src/dotty/tools/dotc/transform/TreeChecker.scala
+++ b/src/dotty/tools/dotc/transform/TreeChecker.scala
@@ -396,6 +396,9 @@ class TreeChecker extends Phase with SymTransformer {
override def typedBlock(tree: untpd.Block, pt: Type)(implicit ctx: Context) =
withDefinedSyms(tree.stats) { super.typedBlock(tree, pt) }
+ override def typedInlined(tree: untpd.Inlined, pt: Type)(implicit ctx: Context) =
+ withDefinedSyms(tree.bindings) { super.typedInlined(tree, pt) }
+
/** Check that all defined symbols have legal owners.
* An owner is legal if it is either the same as the context's owner
* or there's an owner chain of valdefs starting at the context's owner and
@@ -423,8 +426,9 @@ class TreeChecker extends Phase with SymTransformer {
!isPrimaryConstructorReturn &&
!pt.isInstanceOf[FunProto])
assert(tree.tpe <:< pt,
- s"error at ${sourcePos(tree.pos)}\n" +
- err.typeMismatchStr(tree.tpe, pt) + "\ntree = " + tree)
+ i"""error at ${sourcePos(tree.pos)}
+ |${err.typeMismatchStr(tree.tpe, pt)}
+ |tree = $tree""")
tree
}
}
diff --git a/src/dotty/tools/dotc/transform/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala
index 05961508a..52a3ad94e 100644
--- a/src/dotty/tools/dotc/transform/TreeTransform.scala
+++ b/src/dotty/tools/dotc/transform/TreeTransform.scala
@@ -81,6 +81,7 @@ object TreeTransforms {
def prepareForReturn(tree: Return)(implicit ctx: Context) = this
def prepareForTry(tree: Try)(implicit ctx: Context) = this
def prepareForSeqLiteral(tree: SeqLiteral)(implicit ctx: Context) = this
+ def prepareForInlined(tree: Inlined)(implicit ctx: Context) = this
def prepareForTypeTree(tree: TypeTree)(implicit ctx: Context) = this
def prepareForBind(tree: Bind)(implicit ctx: Context) = this
def prepareForAlternative(tree: Alternative)(implicit ctx: Context) = this
@@ -112,6 +113,7 @@ object TreeTransforms {
def transformReturn(tree: Return)(implicit ctx: Context, info: TransformerInfo): Tree = tree
def transformTry(tree: Try)(implicit ctx: Context, info: TransformerInfo): Tree = tree
def transformSeqLiteral(tree: SeqLiteral)(implicit ctx: Context, info: TransformerInfo): Tree = tree
+ def transformInlined(tree: Inlined)(implicit ctx: Context, info: TransformerInfo): Tree = tree
def transformTypeTree(tree: TypeTree)(implicit ctx: Context, info: TransformerInfo): Tree = tree
def transformBind(tree: Bind)(implicit ctx: Context, info: TransformerInfo): Tree = tree
def transformAlternative(tree: Alternative)(implicit ctx: Context, info: TransformerInfo): Tree = tree
@@ -273,6 +275,7 @@ object TreeTransforms {
nxPrepReturn = index(transformations, "prepareForReturn")
nxPrepTry = index(transformations, "prepareForTry")
nxPrepSeqLiteral = index(transformations, "prepareForSeqLiteral")
+ nxPrepInlined = index(transformations, "prepareForInlined")
nxPrepTypeTree = index(transformations, "prepareForTypeTree")
nxPrepBind = index(transformations, "prepareForBind")
nxPrepAlternative = index(transformations, "prepareForAlternative")
@@ -303,6 +306,7 @@ object TreeTransforms {
nxTransReturn = index(transformations, "transformReturn")
nxTransTry = index(transformations, "transformTry")
nxTransSeqLiteral = index(transformations, "transformSeqLiteral")
+ nxTransInlined = index(transformations, "transformInlined")
nxTransTypeTree = index(transformations, "transformTypeTree")
nxTransBind = index(transformations, "transformBind")
nxTransAlternative = index(transformations, "transformAlternative")
@@ -343,6 +347,7 @@ object TreeTransforms {
nxPrepReturn = indexUpdate(prev.nxPrepReturn, changedTransformationClass, transformationIndex, "prepareForReturn", copy)
nxPrepTry = indexUpdate(prev.nxPrepTry, changedTransformationClass, transformationIndex, "prepareForTry", copy)
nxPrepSeqLiteral = indexUpdate(prev.nxPrepSeqLiteral, changedTransformationClass, transformationIndex, "prepareForSeqLiteral", copy)
+ nxPrepInlined = indexUpdate(prev.nxPrepInlined, changedTransformationClass, transformationIndex, "prepareForInlined", copy)
nxPrepTypeTree = indexUpdate(prev.nxPrepTypeTree, changedTransformationClass, transformationIndex, "prepareForTypeTree", copy)
nxPrepBind = indexUpdate(prev.nxPrepBind, changedTransformationClass, transformationIndex, "prepareForBind", copy)
nxPrepAlternative = indexUpdate(prev.nxPrepAlternative, changedTransformationClass, transformationIndex, "prepareForAlternative", copy)
@@ -372,6 +377,7 @@ object TreeTransforms {
nxTransReturn = indexUpdate(prev.nxTransReturn, changedTransformationClass, transformationIndex, "transformReturn", copy)
nxTransTry = indexUpdate(prev.nxTransTry, changedTransformationClass, transformationIndex, "transformTry", copy)
nxTransSeqLiteral = indexUpdate(prev.nxTransSeqLiteral, changedTransformationClass, transformationIndex, "transformSeqLiteral", copy)
+ nxTransInlined = indexUpdate(prev.nxTransInlined, changedTransformationClass, transformationIndex, "transformInlined", copy)
nxTransTypeTree = indexUpdate(prev.nxTransTypeTree, changedTransformationClass, transformationIndex, "transformTypeTree", copy)
nxTransBind = indexUpdate(prev.nxTransBind, changedTransformationClass, transformationIndex, "transformBind", copy)
nxTransAlternative = indexUpdate(prev.nxTransAlternative, changedTransformationClass, transformationIndex, "transformAlternative", copy)
@@ -407,6 +413,7 @@ object TreeTransforms {
var nxPrepReturn: Array[Int] = _
var nxPrepTry: Array[Int] = _
var nxPrepSeqLiteral: Array[Int] = _
+ var nxPrepInlined: Array[Int] = _
var nxPrepTypeTree: Array[Int] = _
var nxPrepBind: Array[Int] = _
var nxPrepAlternative: Array[Int] = _
@@ -437,6 +444,7 @@ object TreeTransforms {
var nxTransReturn: Array[Int] = _
var nxTransTry: Array[Int] = _
var nxTransSeqLiteral: Array[Int] = _
+ var nxTransInlined: Array[Int] = _
var nxTransTypeTree: Array[Int] = _
var nxTransBind: Array[Int] = _
var nxTransAlternative: Array[Int] = _
@@ -515,6 +523,7 @@ object TreeTransforms {
val prepForReturn: Mutator[Return] = (trans, tree, ctx) => trans.prepareForReturn(tree)(ctx)
val prepForTry: Mutator[Try] = (trans, tree, ctx) => trans.prepareForTry(tree)(ctx)
val prepForSeqLiteral: Mutator[SeqLiteral] = (trans, tree, ctx) => trans.prepareForSeqLiteral(tree)(ctx)
+ val prepForInlined: Mutator[Inlined] = (trans, tree, ctx) => trans.prepareForInlined(tree)(ctx)
val prepForTypeTree: Mutator[TypeTree] = (trans, tree, ctx) => trans.prepareForTypeTree(tree)(ctx)
val prepForBind: Mutator[Bind] = (trans, tree, ctx) => trans.prepareForBind(tree)(ctx)
val prepForAlternative: Mutator[Alternative] = (trans, tree, ctx) => trans.prepareForAlternative(tree)(ctx)
@@ -741,6 +750,17 @@ object TreeTransforms {
}
@tailrec
+ final private[TreeTransforms] def goInlined(tree: Inlined, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
+ if (cur < info.transformers.length) {
+ val trans = info.transformers(cur)
+ trans.transformInlined(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
+ case t: Inlined => goInlined(t, info.nx.nxTransInlined(cur + 1))
+ case t => transformSingle(t, cur + 1)
+ }
+ } else tree
+ }
+
+ @tailrec
final private[TreeTransforms] def goTypeTree(tree: TypeTree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
@@ -884,7 +904,8 @@ object TreeTransforms {
case tree: CaseDef => goCaseDef(tree, info.nx.nxTransCaseDef(cur))
case tree: Return => goReturn(tree, info.nx.nxTransReturn(cur))
case tree: Try => goTry(tree, info.nx.nxTransTry(cur))
- case tree: SeqLiteral => goSeqLiteral(tree, info.nx.nxTransLiteral(cur))
+ case tree: SeqLiteral => goSeqLiteral(tree, info.nx.nxTransSeqLiteral(cur))
+ case tree: Inlined => goInlined(tree, info.nx.nxTransInlined(cur))
case tree: TypeTree => goTypeTree(tree, info.nx.nxTransTypeTree(cur))
case tree: Alternative => goAlternative(tree, info.nx.nxTransAlternative(cur))
case tree: UnApply => goUnApply(tree, info.nx.nxTransUnApply(cur))
@@ -1090,6 +1111,14 @@ object TreeTransforms {
val elemtpt = transform(tree.elemtpt, mutatedInfo, cur)
goSeqLiteral(cpy.SeqLiteral(tree)(elems, elemtpt), mutatedInfo.nx.nxTransSeqLiteral(cur))
}
+ case tree: Inlined =>
+ implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForInlined, info.nx.nxPrepInlined, tree, cur)
+ if (mutatedInfo eq null) tree
+ else {
+ val bindings = transformSubTrees(tree.bindings, mutatedInfo, cur)
+ val expansion = transform(tree.expansion, mutatedInfo, cur)(inlineContext(tree))
+ goInlined(cpy.Inlined(tree)(tree.call, bindings, expansion), mutatedInfo.nx.nxTransInlined(cur))
+ }
case tree: TypeTree =>
implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTypeTree, info.nx.nxPrepTypeTree, tree, cur)
if (mutatedInfo eq null) tree
diff --git a/src/dotty/tools/dotc/transform/patmat/Space.scala b/src/dotty/tools/dotc/transform/patmat/Space.scala
index d942c6853..830d0f938 100644
--- a/src/dotty/tools/dotc/transform/patmat/Space.scala
+++ b/src/dotty/tools/dotc/transform/patmat/Space.scala
@@ -522,7 +522,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
}
val Match(sel, cases) = tree
- isCheckable(sel.tpe.widen.deAnonymize.dealias)
+ isCheckable(sel.tpe.widen.deAnonymize.dealiasKeepAnnots)
}
diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala
index 55d9fc990..2c9039db1 100644
--- a/src/dotty/tools/dotc/typer/Applications.scala
+++ b/src/dotty/tools/dotc/typer/Applications.scala
@@ -861,7 +861,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
/** A typed unapply hook, can be overridden by re any-typers between frontend
* and pattern matcher.
*/
- def typedUnApply(tree: untpd.UnApply, selType: Type)(implicit ctx: Context) =
+ def typedUnApply(tree: untpd.UnApply, selType: Type)(implicit ctx: Context): UnApply =
throw new UnsupportedOperationException("cannot type check an UnApply node")
/** Is given method reference applicable to type arguments `targs` and argument trees `args`?
diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala
index 415cd5d6a..b02b0ad21 100644
--- a/src/dotty/tools/dotc/typer/Checking.scala
+++ b/src/dotty/tools/dotc/typer/Checking.scala
@@ -344,6 +344,7 @@ object Checking {
fail(i"only classes can have declared but undefined members$varNote")
checkWithDeferred(Private)
checkWithDeferred(Final)
+ checkWithDeferred(Inline)
}
if (sym.isValueClass && sym.is(Trait) && !sym.isRefinementClass)
fail(i"$sym cannot extend AnyVal")
@@ -479,6 +480,14 @@ trait Checking {
tp
}
+ /** Check that `tree` is a pure expression of constant type */
+ def checkInlineConformant(tree: Tree, what: => String)(implicit ctx: Context): Unit =
+ tree.tpe.widenTermRefExpr match {
+ case tp: ConstantType if isPureExpr(tree) => // ok
+ case tp if defn.isFunctionType(tp) && isPureExpr(tree) => // ok
+ case _ => ctx.error(em"$what must be a constant expression or a function", tree.pos)
+ }
+
/** Check that class does not define same symbol twice */
def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = {
val seen = new mutable.HashMap[Name, List[Symbol]] {
@@ -543,6 +552,7 @@ trait NoChecking extends Checking {
override def checkClassType(tp: Type, pos: Position, traitReq: Boolean, stablePrefixReq: Boolean)(implicit ctx: Context): Type = tp
override def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = ()
override def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp
+ override def checkInlineConformant(tree: Tree, what: => String)(implicit ctx: Context) = ()
override def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = ()
override def checkParentCall(call: Tree, caller: ClassSymbol)(implicit ctx: Context) = ()
override def checkSimpleKinded(tpt: Tree)(implicit ctx: Context): Tree = tpt
diff --git a/src/dotty/tools/dotc/typer/EtaExpansion.scala b/src/dotty/tools/dotc/typer/EtaExpansion.scala
index 7cbe70b47..397b6d95b 100644
--- a/src/dotty/tools/dotc/typer/EtaExpansion.scala
+++ b/src/dotty/tools/dotc/typer/EtaExpansion.scala
@@ -148,7 +148,7 @@ object EtaExpansion {
case _ =>
}
val fn = untpd.Function(params, body)
- if (defs.nonEmpty) untpd.Block(defs.toList map untpd.TypedSplice, fn) else fn
+ if (defs.nonEmpty) untpd.Block(defs.toList map (untpd.TypedSplice(_)), fn) else fn
}
}
diff --git a/src/dotty/tools/dotc/typer/Inliner.scala b/src/dotty/tools/dotc/typer/Inliner.scala
new file mode 100644
index 000000000..55008c0c5
--- /dev/null
+++ b/src/dotty/tools/dotc/typer/Inliner.scala
@@ -0,0 +1,521 @@
+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 Constants._
+import StdNames.nme
+import Contexts.Context
+import Names.{Name, TermName}
+import NameOps._
+import SymDenotations.SymDenotation
+import Annotations._
+import transform.ExplicitOuter
+import Inferencing.fullyDefinedType
+import config.Printers.inlining
+import ErrorReporting.errorTree
+import collection.mutable
+import transform.TypeUtils._
+
+object Inliner {
+ import tpd._
+
+ /** Adds accessors accessors for all non-public term members accessed
+ * from `tree`. Non-public type members are currently left as they are.
+ * This means that references to a private type will lead to typing failures
+ * on the code when it is inlined. Less than ideal, but hard to do better (see below).
+ *
+ * @return If there are accessors generated, a thicket consisting of the rewritten `tree`
+ * and all accessors, otherwise the original tree.
+ */
+ private def makeInlineable(tree: Tree)(implicit ctx: Context) = {
+
+ /** A tree map which inserts accessors for all non-public term members accessed
+ * from inlined code. Accesors are collected in the `accessors` buffer.
+ */
+ object addAccessors extends TreeMap {
+ val inlineMethod = ctx.owner
+ val accessors = new mutable.ListBuffer[MemberDef]
+
+ /** A definition needs an accessor if it is private, protected, or qualified private */
+ def needsAccessor(sym: Symbol)(implicit ctx: Context) =
+ sym.is(AccessFlags) || sym.privateWithin.exists
+
+ /** The name of the next accessor to be generated */
+ def accessorName(implicit ctx: Context) =
+ ctx.freshNames.newName(inlineMethod.name.asTermName.inlineAccessorName.toString)
+
+ /** A fresh accessor symbol.
+ *
+ * @param tree The tree representing the original access to the non-public member
+ * @param accessorInfo The type of the accessor
+ */
+ 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
+
+ /** Add an accessor to a non-public method and replace the original access with a
+ * call to the accessor.
+ *
+ * @param tree The original access to the non-public symbol
+ * @param refPart The part that refers to the method or field of the original access
+ * @param targs All type arguments passed in the access, if any
+ * @param argss All value arguments passed in the access, if any
+ * @param accessedType The type of the accessed method or field, as seen from the access site.
+ * @param rhs A function that builds the right-hand side of the accessor,
+ * given a reference to the accessed symbol and any type and
+ * value arguments the need to be integrated.
+ * @return The call to the accessor method that replaces the original access.
+ */
+ def addAccessor(tree: Tree, refPart: Tree, targs: List[Tree], argss: List[List[Tree]],
+ accessedType: Type, rhs: (Tree, List[Type], List[List[Tree]]) => Tree)(implicit ctx: Context): Tree = {
+ val qual = qualifier(refPart)
+ def refIsLocal = qual match {
+ case qual: This => qual.symbol == refPart.symbol.owner
+ case _ => false
+ }
+ val (accessorDef, accessorRef) =
+ if (refPart.symbol.isStatic || refIsLocal) {
+ // Easy case: Reference to a static symbol or a symbol referenced via `this.`
+ val accessorType = accessedType.ensureMethodic
+ val accessor = accessorSymbol(tree, accessorType).asTerm
+ val accessorDef = polyDefDef(accessor, tps => argss =>
+ rhs(refPart, tps, argss))
+ val accessorRef = ref(accessor).appliedToTypeTrees(targs).appliedToArgss(argss)
+ (accessorDef, accessorRef)
+ } else {
+ // Hard case: Reference needs to go via a dynamic prefix
+ inlining.println(i"adding inline accessor for $tree -> (${qual.tpe}, $refPart: ${refPart.getClass}, [$targs%, %], ($argss%, %))")
+
+ // Need to dealias in order to catch all possible references to abstracted over types in
+ // substitutions
+ val dealiasMap = new TypeMap {
+ def apply(t: Type) = mapOver(t.dealias)
+ }
+
+ val qualType = dealiasMap(qual.tpe.widen)
+
+ // Add qualifier type as leading method argument to argument `tp`
+ 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)
+ }
+
+ // The types that are local to the inlined method, and that therefore have
+ // to be abstracted out in the accessor, which is external to the inlined method
+ val localRefs = qualType.namedPartsWith(_.symbol.isContainedIn(inlineMethod)).toList
+
+ // Abstract accessed type over local refs
+ def abstractQualType(mtpe: Type): Type =
+ if (localRefs.isEmpty) mtpe
+ else PolyType.fromSymbols(localRefs.map(_.symbol), mtpe).asInstanceOf[PolyType].flatten
+
+ val accessorType = abstractQualType(addQualType(dealiasMap(accessedType)))
+ val accessor = accessorSymbol(tree, accessorType).asTerm
+
+ val accessorDef = polyDefDef(accessor, tps => argss =>
+ rhs(argss.head.head.select(refPart.symbol), tps.drop(localRefs.length), argss.tail))
+
+ val accessorRef = ref(accessor)
+ .appliedToTypeTrees(localRefs.map(TypeTree(_)) ++ targs)
+ .appliedToArgss((qual :: Nil) :: argss)
+ (accessorDef, accessorRef)
+ }
+ accessors += accessorDef
+ inlining.println(i"added inline accessor: $accessorDef")
+ accessorRef
+ }
+
+ 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)
+ addAccessor(tree, methPart, targs, argss,
+ accessedType = methPart.tpe.widen,
+ rhs = (qual, tps, argss) => qual.appliedToTypes(tps).appliedToArgss(argss))
+ } 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 Assign(lhs: RefTree, rhs) if needsAccessor(lhs.symbol) =>
+ addAccessor(tree, lhs, Nil, (rhs :: Nil) :: Nil,
+ accessedType = MethodType(rhs.tpe.widen :: Nil, defn.UnitType),
+ rhs = (lhs, tps, argss) => lhs.becomes(argss.head.head))
+ case _ => tree
+ }
+ }
+ }
+
+ val tree1 = addAccessors.transform(tree)
+ flatTree(tree1 :: addAccessors.accessors.toList)
+ }
+
+ /** Register inline info for given inline method `sym`.
+ *
+ * @param sym The symbol denotatioon of the inline method for which info is registered
+ * @param treeExpr A function that computes the tree to be inlined, given a context
+ * This tree may still refer to non-public members.
+ * @param ctx The context to use for evaluating `treeExpr`. It needs
+ * to have the inlined method as owner.
+ */
+ def registerInlineInfo(
+ sym: SymDenotation, treeExpr: Context => Tree)(implicit ctx: Context): Unit = {
+ sym.unforcedAnnotation(defn.BodyAnnot) match {
+ case Some(ann: ConcreteBodyAnnotation) =>
+ case Some(ann: LazyBodyAnnotation) if ann.isEvaluated =>
+ case _ =>
+ if (!ctx.isAfterTyper) {
+ val inlineCtx = ctx
+ sym.updateAnnotation(LazyBodyAnnotation { _ =>
+ implicit val ctx: Context = inlineCtx
+ val tree1 = treeExpr(ctx)
+ makeInlineable(tree1)
+ })
+ }
+ }
+ }
+
+ /** `sym` has an inline method with a known body to inline (note: definitions coming
+ * from Scala2x class files might be `@inline`, but still lack that body.
+ */
+ def hasBodyToInline(sym: SymDenotation)(implicit ctx: Context): Boolean =
+ sym.isInlineMethod && sym.hasAnnotation(defn.BodyAnnot)
+
+ private def bodyAndAccessors(sym: SymDenotation)(implicit ctx: Context): (Tree, List[MemberDef]) =
+ sym.unforcedAnnotation(defn.BodyAnnot).get.tree match {
+ case Thicket(body :: accessors) => (body, accessors.asInstanceOf[List[MemberDef]])
+ case body => (body, Nil)
+ }
+
+ /** The body to inline for method `sym`.
+ * @pre hasBodyToInline(sym)
+ */
+ def bodyToInline(sym: SymDenotation)(implicit ctx: Context): Tree =
+ bodyAndAccessors(sym)._1
+
+ /** The accessors to non-public members needed by the inlinable body of `sym`.
+ * These accessors are dropped as a side effect of calling this method.
+ * @pre hasBodyToInline(sym)
+ */
+ def removeInlineAccessors(sym: SymDenotation)(implicit ctx: Context): List[MemberDef] = {
+ val (body, accessors) = bodyAndAccessors(sym)
+ if (accessors.nonEmpty) sym.updateAnnotation(ConcreteBodyAnnotation(body))
+ accessors
+ }
+
+ /** Try to inline a call to a `@inline` method. Fail with error if the maximal
+ * inline depth is exceeded.
+ *
+ * @param tree The call to inline
+ * @param pt The expected type of the call.
+ * @return An `Inlined` node that refers to the original call and the inlined bindings
+ * and body that replace it.
+ */
+ def inlineCall(tree: Tree, pt: Type)(implicit ctx: Context): Tree =
+ if (enclosingInlineds.length < ctx.settings.xmaxInlines.value)
+ new Inliner(tree, bodyToInline(tree.symbol)).inlined(pt)
+ 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.""")
+
+ /** Replace `Inlined` node by a block that contains its bindings and expansion */
+ def dropInlined(inlined: tpd.Inlined)(implicit ctx: Context): Tree = {
+ val reposition = new TreeMap {
+ override def transform(tree: Tree)(implicit ctx: Context): Tree =
+ tree.withPos(inlined.call.pos)
+ }
+ tpd.seq(inlined.bindings, reposition.transform(inlined.expansion))
+ }
+
+ /** The qualifier part of a Select or Ident.
+ * For an Ident, this is the `This` of the current class. (TODO: use elsewhere as well?)
+ */
+ private def qualifier(tree: Tree)(implicit ctx: Context) = tree match {
+ case Select(qual, _) => qual
+ case _ => This(ctx.owner.enclosingClass.asClass)
+ }
+}
+
+/** 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.
+ */
+class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
+ import tpd._
+ import Inliner._
+
+ private val (methPart, targs, argss) = decomposeCall(call)
+ private val meth = methPart.symbol
+ private val prefix = qualifier(methPart)
+
+ // Make sure all type arguments to the call are fully determined
+ for (targ <- targs) fullyDefinedType(targ.tpe, "inlined type argument", targ.pos)
+
+ /** A map from parameter names of the inline method to references of the actual arguments.
+ * For a type argument this is the full argument type.
+ * For a value argument, it is a reference to either the argument value
+ * (if the argument is a pure expression of singleton type), or to `val` or `def` acting
+ * as a proxy (if the argument is something else).
+ */
+ private val paramBinding = new mutable.HashMap[Name, Type]
+
+ /** A map from references to (type and value) parameters of the inline method
+ * to their corresponding argument or proxy references, as given by `paramBinding`.
+ */
+ private val paramProxy = new mutable.HashMap[Type, Type]
+
+ /** A map from (direct and outer) this references in `rhs` to references of their proxies */
+ private val thisProxy = new mutable.HashMap[Type, TermRef]
+
+ /** A buffer for bindings that define proxies for actual arguments */
+ val bindingsBuf = new mutable.ListBuffer[ValOrDefDef]
+
+ computeParamBindings(meth.info, targs, argss)
+
+ private def newSym(name: Name, flags: FlagSet, info: Type): Symbol =
+ ctx.newSymbol(ctx.owner, name, flags, info, coord = call.pos)
+
+ /** Populate `paramBinding` and `bindingsBuf` by matching parameters with
+ * corresponding arguments. `bindingbuf` will be further extended later by
+ * proxies to this-references.
+ */
+ private def computeParamBindings(tp: Type, targs: List[Tree], argss: List[List[Tree]]): Unit = tp match {
+ case tp: PolyType =>
+ (tp.paramNames, targs).zipped.foreach { (name, arg) =>
+ paramBinding(name) = arg.tpe.stripTypeVar
+ }
+ computeParamBindings(tp.resultType, Nil, argss)
+ case tp: MethodType =>
+ (tp.paramNames, tp.paramTypes, argss.head).zipped.foreach { (name, paramtp, arg) =>
+ def isByName = paramtp.dealias.isInstanceOf[ExprType]
+ paramBinding(name) = arg.tpe.stripAnnots.stripTypeVar match {
+ case argtpe: SingletonType if isByName || isIdempotentExpr(arg) => argtpe
+ case argtpe =>
+ val inlineFlag = if (paramtp.hasAnnotation(defn.InlineParamAnnot)) Inline else EmptyFlags
+ val (bindingFlags, bindingType) =
+ if (isByName) (inlineFlag | Method, ExprType(argtpe.widen))
+ else (inlineFlag, argtpe.widen)
+ val boundSym = newSym(name, bindingFlags, bindingType).asTerm
+ val binding =
+ if (isByName) DefDef(boundSym, arg.changeOwner(ctx.owner, boundSym))
+ else ValDef(boundSym, arg)
+ bindingsBuf += binding
+ boundSym.termRef
+ }
+ }
+ computeParamBindings(tp.resultType, targs, argss.tail)
+ case _ =>
+ assert(targs.isEmpty)
+ assert(argss.isEmpty)
+ }
+
+ /** Populate `thisProxy` and `paramProxy` as follows:
+ *
+ * 1a. If given type refers to a static this, thisProxy binds it to corresponding global reference,
+ * 1b. If given type refers to an instance this, create a proxy symbol and bind the thistype to
+ * refer to the proxy. The proxy is not yet entered in `bindingsBuf` that will come later.
+ * 2. If given type refers to a parameter, make `paramProxy` refer to the entry stored
+ * in `paramNames` under the parameter's name. This roundabout way to bind parameter
+ * references to proxies is done because we not known a priori what the parameter
+ * references of a method are (we only know the method's type, but that contains PolyParams
+ * and MethodParams, not TypeRefs or TermRefs.
+ */
+ private def registerType(tpe: Type): Unit = tpe match {
+ case tpe: ThisType
+ if !ctx.owner.isContainedIn(tpe.cls) && !tpe.cls.is(Package) &&
+ !thisProxy.contains(tpe) =>
+ if (tpe.cls.isStaticOwner)
+ thisProxy(tpe) = tpe.cls.sourceModule.termRef
+ else {
+ val proxyName = s"${tpe.cls.name}_this".toTermName
+ val proxyType = tpe.asSeenFrom(prefix.tpe, meth.owner)
+ thisProxy(tpe) = newSym(proxyName, EmptyFlags, proxyType).termRef
+ registerType(meth.owner.thisType) // make sure we have a base from which to outer-select
+ }
+ case tpe: NamedType
+ if tpe.symbol.is(Param) && tpe.symbol.owner == meth &&
+ !paramProxy.contains(tpe) =>
+ paramProxy(tpe) = paramBinding(tpe.name)
+ case _ =>
+ }
+
+ /** Register type of leaf node */
+ private def registerLeaf(tree: Tree): Unit = tree match {
+ case _: This | _: Ident | _: TypeTree =>
+ tree.tpe.foreachPart(registerType, stopAtStatic = true)
+ case _ =>
+ }
+
+ /** The Inlined node representing the inlined call */
+ def inlined(pt: Type) = {
+ // make sure prefix is executed if it is impure
+ 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)
+
+ // The class that the this-proxy `selfSym` represents
+ def classOf(selfSym: Symbol) = selfSym.info.widen.classSymbol
+
+ // The name of the outer selector that computes the rhs of `selfSym`
+ def outerSelector(selfSym: Symbol): TermName = classOf(selfSym).name.toTermName ++ nme.OUTER_SELECT
+
+ // The total nesting depth of the class represented by `selfSym`.
+ def outerLevel(selfSym: Symbol): Int = classOf(selfSym).ownersIterator.length
+
+ // All needed this-proxies, sorted by nesting depth of the classes they represent (innermost first)
+ val accessedSelfSyms = thisProxy.values.toList.map(_.symbol).sortBy(-outerLevel(_))
+
+ // Compute val-definitions for all this-proxies and append them to `bindingsBuf`
+ var lastSelf: Symbol = NoSymbol
+ for (selfSym <- accessedSelfSyms) {
+ val rhs =
+ if (!lastSelf.exists)
+ prefix
+ else
+ untpd.Select(ref(lastSelf), outerSelector(selfSym)).withType(selfSym.info)
+ bindingsBuf += ValDef(selfSym.asTerm, rhs)
+ lastSelf = selfSym
+ }
+
+ // The type map to apply to the inlined tree. This maps references to this-types
+ // and parameters to type references of their arguments or proxies.
+ val typeMap = new TypeMap {
+ def apply(t: Type) = t match {
+ case t: ThisType => thisProxy.getOrElse(t, t)
+ case t: TypeRef => paramProxy.getOrElse(t, mapOver(t))
+ case t: SingletonType => paramProxy.getOrElse(t, mapOver(t))
+ case t => mapOver(t)
+ }
+ }
+
+ // The tree map to apply to the inlined tree. This maps references to this-types
+ // and parameters to references of their arguments or their proxies.
+ def treeMap(tree: Tree) = {
+ tree match {
+ case _: This =>
+ thisProxy.get(tree.tpe) match {
+ case Some(t) => ref(t).withPos(tree.pos)
+ case None => tree
+ }
+ case _: Ident =>
+ paramProxy.get(tree.tpe) match {
+ case Some(t: SingletonType) if tree.isTerm => singleton(t).withPos(tree.pos)
+ case Some(t) if tree.isType => TypeTree(t).withPos(tree.pos)
+ case None => tree
+ }
+ case _ => tree
+ }}
+
+ // The complete translation maps referenves to this and parameters to
+ // corresponding arguments or proxies on the type and term level. It also changes
+ // the owner from the inlined method to the current owner.
+ val inliner = new TreeTypeMap(typeMap, treeMap, meth :: Nil, ctx.owner :: Nil)
+
+ val expansion = inliner(rhs.withPos(call.pos))
+ ctx.traceIndented(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.
+ val expansion1 = InlineTyper.typed(expansion, pt)(inlineContext(call))
+
+ /** Does given definition bind a closure that will be inlined? */
+ def bindsDeadClosure(defn: ValOrDefDef) = Ident(defn.symbol.termRef) match {
+ case InlineableClosure(_) => !InlineTyper.retainedClosures.contains(defn.symbol)
+ case _ => false
+ }
+
+ /** All bindings in `bindingsBuf` except bindings of inlineable closures */
+ val bindings = bindingsBuf.toList.filterNot(bindsDeadClosure).map(_.withPos(call.pos))
+
+ tpd.Inlined(call, bindings, expansion1)
+ }
+ }
+
+ /** An extractor for references to closure arguments that refer to `@inline` methods */
+ private object InlineableClosure {
+ lazy val paramProxies = paramProxy.values.toSet
+ def unapply(tree: Ident)(implicit ctx: Context): Option[Tree] =
+ if (paramProxies.contains(tree.tpe)) {
+ bindingsBuf.find(_.name == tree.name) match {
+ case Some(ddef: ValDef) if ddef.symbol.is(Inline) =>
+ ddef.rhs match {
+ case closure(_, meth, _) => Some(meth)
+ case _ => None
+ }
+ case _ => None
+ }
+ } else None
+ }
+
+ /** A typer for inlined code. Its purpose is:
+ * 1. Implement constant folding over inlined code
+ * 2. Selectively expand ifs with constant conditions
+ * 3. Inline arguments that are inlineable closures
+ * 4. Make sure inlined code is type-correct.
+ * 5. Make sure that the tree's typing is idempotent (so that future -Ycheck passes succeed)
+ */
+ private object InlineTyper extends ReTyper {
+
+ var retainedClosures = Set[Symbol]()
+
+ override def typedIdent(tree: untpd.Ident, pt: Type)(implicit ctx: Context) = {
+ val tree1 = super.typedIdent(tree, pt)
+ tree1 match {
+ case InlineableClosure(_) => retainedClosures += tree.symbol
+ case _ =>
+ }
+ tree1
+ }
+
+ override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = {
+ val res = super.typedSelect(tree, pt)
+ ensureAccessible(res.tpe, tree.qualifier.isInstanceOf[untpd.Super], tree.pos)
+ res
+ }
+
+ override def typedIf(tree: untpd.If, pt: Type)(implicit ctx: Context) = {
+ val cond1 = typed(tree.cond, defn.BooleanType)
+ cond1.tpe.widenTermRefExpr match {
+ case ConstantType(Constant(condVal: Boolean)) =>
+ val selected = typed(if (condVal) tree.thenp else tree.elsep, pt)
+ if (isIdempotentExpr(cond1)) selected
+ else Block(cond1 :: Nil, selected)
+ case _ =>
+ val if1 = untpd.cpy.If(tree)(cond = untpd.TypedSplice(cond1))
+ super.typedIf(if1, pt)
+ }
+ }
+
+ override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context) = tree.asInstanceOf[tpd.Tree] match {
+ case Apply(Select(InlineableClosure(fn), nme.apply), args) =>
+ inlining.println(i"reducing $tree with closure $fn")
+ typed(fn.appliedToArgs(args), pt)
+ case _ =>
+ super.typedApply(tree, pt)
+ }
+ }
+}
diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala
index cfd49fd87..2e714ab6d 100644
--- a/src/dotty/tools/dotc/typer/Namer.scala
+++ b/src/dotty/tools/dotc/typer/Namer.scala
@@ -9,7 +9,7 @@ import Contexts._, Symbols._, Types._, SymDenotations._, Names._, NameOps._, Fla
import ast.desugar, ast.desugar._
import ProtoTypes._
import util.Positions._
-import util.{Attachment, SourcePosition, DotClass}
+import util.{Property, SourcePosition, DotClass}
import collection.mutable
import annotation.tailrec
import ErrorReporting._
@@ -160,9 +160,9 @@ class Namer { typer: Typer =>
import untpd._
- val TypedAhead = new Attachment.Key[tpd.Tree]
- val ExpandedTree = new Attachment.Key[Tree]
- val SymOfTree = new Attachment.Key[Symbol]
+ val TypedAhead = new Property.Key[tpd.Tree]
+ val ExpandedTree = new Property.Key[Tree]
+ val SymOfTree = new Property.Key[Symbol]
/** A partial map from unexpanded member and pattern defs and to their expansions.
* Populated during enterSyms, emptied during typer.
@@ -403,37 +403,28 @@ class Namer { typer: Typer =>
/** Create top-level symbols for all statements in the expansion of this statement and
* enter them into symbol table
*/
- def indexExpanded(stat: Tree)(implicit ctx: Context): Context = expanded(stat) match {
- case pcl: PackageDef =>
- val pkg = createPackageSymbol(pcl.pid)
- index(pcl.stats)(ctx.fresh.setOwner(pkg.moduleClass))
- invalidateCompanions(pkg, Trees.flatten(pcl.stats map expanded))
- setDocstring(pkg, stat)
- ctx
- case imp: Import =>
- importContext(createSymbol(imp), imp.selectors)
- case mdef: DefTree =>
- val sym = enterSymbol(createSymbol(mdef))
- setDocstring(sym, stat)
-
- // add java enum constants
- mdef match {
- case vdef: ValDef if (isEnumConstant(vdef)) =>
- val enumClass = sym.owner.linkedClass
- if (!(enumClass is Flags.Sealed)) enumClass.setFlag(Flags.AbstractSealed)
- enumClass.addAnnotation(Annotation.makeChild(sym))
- case _ =>
- }
-
- ctx
- case stats: Thicket =>
- for (tree <- stats.toList) {
- val sym = enterSymbol(createSymbol(tree))
- setDocstring(sym, stat)
- }
- ctx
- case _ =>
- ctx
+ def indexExpanded(origStat: Tree)(implicit ctx: Context): Context = {
+ def recur(stat: Tree): Context = stat match {
+ case pcl: PackageDef =>
+ val pkg = createPackageSymbol(pcl.pid)
+ index(pcl.stats)(ctx.fresh.setOwner(pkg.moduleClass))
+ invalidateCompanions(pkg, Trees.flatten(pcl.stats map expanded))
+ setDocstring(pkg, stat)
+ ctx
+ case imp: Import =>
+ importContext(createSymbol(imp), imp.selectors)
+ case mdef: DefTree =>
+ val sym = enterSymbol(createSymbol(mdef))
+ setDocstring(sym, origStat)
+ addEnumConstants(mdef, sym)
+ ctx
+ case stats: Thicket =>
+ stats.toList.foreach(recur)
+ ctx
+ case _ =>
+ ctx
+ }
+ recur(expanded(origStat))
}
/** Determines whether this field holds an enum constant.
@@ -454,6 +445,16 @@ class Namer { typer: Typer =>
vd.mods.is(allOf(Enum, Stable, JavaStatic, JavaDefined)) // && ownerHasEnumFlag
}
+ /** Add java enum constants */
+ def addEnumConstants(mdef: DefTree, sym: Symbol)(implicit ctx: Context): Unit = mdef match {
+ case vdef: ValDef if (isEnumConstant(vdef)) =>
+ val enumClass = sym.owner.linkedClass
+ if (!(enumClass is Flags.Sealed)) enumClass.setFlag(Flags.AbstractSealed)
+ enumClass.addAnnotation(Annotation.makeChild(sym))
+ case _ =>
+ }
+
+
def setDocstring(sym: Symbol, tree: Tree)(implicit ctx: Context) = tree match {
case t: MemberDef => ctx.docbase.addDocstring(sym, t.rawComment)
case _ => ()
@@ -561,11 +562,34 @@ class Namer { typer: Typer =>
protected def addAnnotations(denot: SymDenotation): Unit = original match {
case original: untpd.MemberDef =>
+ var hasInlineAnnot = false
for (annotTree <- untpd.modsDeco(original).mods.annotations) {
val cls = typedAheadAnnotation(annotTree)
val ann = Annotation.deferred(cls, implicit ctx => typedAnnotation(annotTree))
denot.addAnnotation(ann)
+ if (cls == defn.InlineAnnot) {
+ hasInlineAnnot = true
+ addInlineInfo(denot, original)
+ }
}
+ if (!hasInlineAnnot && denot.is(InlineMethod)) {
+ // create a @inline annotation. Currently, the inlining trigger
+ // is really the annotation, not the flag. This is done so that
+ // we can still compile inline methods from Scala2x. Once we stop
+ // being compatible with Scala2 we should revise the logic to
+ // be based on the flag. Then creating a separate annotation becomes unnecessary.
+ denot.addAnnotation(Annotation(defn.InlineAnnot))
+ addInlineInfo(denot, original)
+ }
+ case _ =>
+ }
+
+ private def addInlineInfo(denot: SymDenotation, original: untpd.Tree) = original match {
+ case original: untpd.DefDef =>
+ Inliner.registerInlineInfo(
+ denot,
+ implicit ctx => typedAheadExpr(original).asInstanceOf[tpd.DefDef].rhs
+ )(localContext(denot.symbol))
case _ =>
}
@@ -867,7 +891,7 @@ class Namer { typer: Typer =>
// println(s"final inherited for $sym: ${inherited.toString}") !!!
// println(s"owner = ${sym.owner}, decls = ${sym.owner.info.decls.show}")
- def isInline = sym.is(Final, butNot = Method | Mutable)
+ def isInline = sym.is(FinalOrInline, butNot = Method | Mutable)
// Widen rhs type and approximate `|' but keep ConstantTypes if
// definition is inline (i.e. final in Scala2).
diff --git a/src/dotty/tools/dotc/typer/ProtoTypes.scala b/src/dotty/tools/dotc/typer/ProtoTypes.scala
index 80f0fd186..0e6697fb7 100644
--- a/src/dotty/tools/dotc/typer/ProtoTypes.scala
+++ b/src/dotty/tools/dotc/typer/ProtoTypes.scala
@@ -299,7 +299,7 @@ object ProtoTypes {
}
class UnapplyFunProto(argType: Type, typer: Typer)(implicit ctx: Context) extends FunProto(
- untpd.TypedSplice(dummyTreeOfType(argType)) :: Nil, WildcardType, typer)
+ untpd.TypedSplice(dummyTreeOfType(argType))(ctx) :: Nil, WildcardType, typer)
/** A prototype for expressions [] that are type-parameterized:
*
diff --git a/src/dotty/tools/dotc/typer/ReTyper.scala b/src/dotty/tools/dotc/typer/ReTyper.scala
index 9750957bf..2413c0c22 100644
--- a/src/dotty/tools/dotc/typer/ReTyper.scala
+++ b/src/dotty/tools/dotc/typer/ReTyper.scala
@@ -10,7 +10,8 @@ import typer.ProtoTypes._
import ast.{tpd, untpd}
import ast.Trees._
import scala.util.control.NonFatal
-import config.Printers
+import util.Positions.Position
+import config.Printers.typr
/** A version of Typer that keeps all symbols defined and referenced in a
* previously typed tree.
@@ -56,6 +57,13 @@ class ReTyper extends Typer {
untpd.cpy.Bind(tree)(tree.name, body1).withType(tree.typeOpt)
}
+ override def typedUnApply(tree: untpd.UnApply, selType: Type)(implicit ctx: Context): UnApply = {
+ val fun1 = typedExpr(tree.fun, AnyFunctionProto)
+ val implicits1 = tree.implicits.map(typedExpr(_))
+ val patterns1 = tree.patterns.mapconserve(pat => typed(pat, pat.tpe))
+ untpd.cpy.UnApply(tree)(fun1, implicits1, patterns1).withType(tree.tpe)
+ }
+
override def localDummy(cls: ClassSymbol, impl: untpd.Template)(implicit ctx: Context) = impl.symbol
override def retrieveSym(tree: untpd.Tree)(implicit ctx: Context): Symbol = tree.symbol
@@ -87,9 +95,14 @@ 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}")
+ if (ctx.isAfterTyper)
+ println(i"exception while typing $tree of class ${tree.getClass} # ${tree.uniqueId}")
throw ex
}
override def checkVariance(tree: Tree)(implicit ctx: Context) = ()
+ override def inferView(from: Tree, to: Type)(implicit ctx: Context): Implicits.SearchResult =
+ Implicits.NoImplicitMatches
+ override def checkCanEqual(ltp: Type, rtp: Type, pos: Position)(implicit ctx: Context): Unit = ()
+ override def inlineExpansion(mdef: DefDef)(implicit ctx: Context): List[Tree] = mdef :: Nil
}
diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala
index ba8f35cd8..0c55d977e 100644
--- a/src/dotty/tools/dotc/typer/TypeAssigner.scala
+++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala
@@ -127,6 +127,9 @@ trait TypeAssigner {
widenMap(tp)
}
+ def avoidingType(expr: Tree, bindings: List[Tree])(implicit ctx: Context): Type =
+ avoid(expr.tpe, localSyms(bindings).filter(_.isTerm))
+
def seqToRepeated(tree: Tree)(implicit ctx: Context): Tree =
Typed(tree, TypeTree(tree.tpe.widen.translateParameterized(defn.SeqClass, defn.RepeatedParamClass)))
@@ -383,7 +386,10 @@ trait TypeAssigner {
tree.withType(defn.UnitType)
def assignType(tree: untpd.Block, stats: List[Tree], expr: Tree)(implicit ctx: Context) =
- tree.withType(avoid(expr.tpe, localSyms(stats) filter (_.isTerm)))
+ tree.withType(avoidingType(expr, stats))
+
+ def assignType(tree: untpd.Inlined, bindings: List[Tree], expansion: Tree)(implicit ctx: Context) =
+ tree.withType(avoidingType(expansion, bindings))
def assignType(tree: untpd.If, thenp: Tree, elsep: Tree)(implicit ctx: Context) =
tree.withType(thenp.tpe | elsep.tpe)
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index 562af75f6..3aff69bdb 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -432,6 +432,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
typed(cpy.Block(tree)(clsDef :: Nil, New(Ident(x), Nil)), pt)
case _ =>
var tpt1 = typedType(tree.tpt)
+ tpt1 = tpt1.withType(ensureAccessible(tpt1.tpe, superAccess = false, tpt1.pos))
tpt1.tpe.dealias match {
case TypeApplications.EtaExpansion(tycon) => tpt1 = tpt1.withType(tycon)
case _ =>
@@ -512,8 +513,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
val rawUpdate: untpd.Tree = untpd.Select(untpd.TypedSplice(fn), nme.update)
val wrappedUpdate =
if (targs.isEmpty) rawUpdate
- else untpd.TypeApply(rawUpdate, targs map untpd.TypedSplice)
- val appliedUpdate = cpy.Apply(fn)(wrappedUpdate, (args map untpd.TypedSplice) :+ tree.rhs)
+ else untpd.TypeApply(rawUpdate, targs map (untpd.TypedSplice(_)))
+ val appliedUpdate = cpy.Apply(fn)(wrappedUpdate, (args map (untpd.TypedSplice(_))) :+ tree.rhs)
typed(appliedUpdate, pt)
case lhs =>
val lhsCore = typedUnadapted(lhs, AssignProto)
@@ -553,9 +554,11 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
}
}
+ def typedBlockStats(stats: List[untpd.Tree])(implicit ctx: Context): (Context, List[tpd.Tree]) =
+ (index(stats), typedStats(stats, ctx.owner))
+
def typedBlock(tree: untpd.Block, pt: Type)(implicit ctx: Context) = track("typedBlock") {
- val exprCtx = index(tree.stats)
- val stats1 = typedStats(tree.stats, ctx.owner)
+ val (exprCtx, stats1) = typedBlockStats(tree.stats)
val ept =
if (tree.isInstanceOf[untpd.InfixOpBlock])
// Right-binding infix operations are expanded to InfixBlocks, which may be followed by arguments.
@@ -607,7 +610,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
em"local definition of ${leaks.head.name} escapes as part of expression's type ${tree.tpe}"/*; full type: ${result.tpe.toString}"*/)
}
- def typedIf(tree: untpd.If, pt: Type)(implicit ctx: Context) = track("typedIf") {
+ def typedIf(tree: untpd.If, pt: Type)(implicit ctx: Context): Tree = track("typedIf") {
val cond1 = typed(tree.cond, defn.BooleanType)
val thenp1 = typed(tree.thenp, pt.notApplied)
val elsep1 = typed(tree.elsep orElse (untpd.unitLiteral withPos tree.pos), pt.notApplied)
@@ -742,7 +745,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
case WildcardType(_) => untpd.TypeTree()
case _ => untpd.TypeTree(protoResult)
}
- desugar.makeClosure(inferredParams, fnBody, resultTpt)
+ val inlineable = pt.hasAnnotation(defn.InlineParamAnnot)
+ desugar.makeClosure(inferredParams, fnBody, resultTpt, inlineable)
}
typed(desugared, pt)
}
@@ -885,12 +889,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)
}
@@ -940,6 +947,13 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
assignType(cpy.SeqLiteral(tree)(elems1, elemtpt1), elems1, elemtpt1)
}
+ def typedInlined(tree: untpd.Inlined, pt: Type)(implicit ctx: Context): Inlined = {
+ val (exprCtx, bindings1) = typedBlockStats(tree.bindings)
+ val expansion1 = typed(tree.expansion, pt)(inlineContext(tree.call)(exprCtx))
+ assignType(cpy.Inlined(tree)(tree.call, bindings1.asInstanceOf[List[MemberDef]], expansion1),
+ bindings1, expansion1)
+ }
+
def typedTypeTree(tree: untpd.TypeTree, pt: Type)(implicit ctx: Context): TypeTree = track("typedTypeTree") {
if (tree.original.isEmpty)
tree match {
@@ -1104,7 +1118,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
def completeAnnotations(mdef: untpd.MemberDef, sym: Symbol)(implicit ctx: Context): Unit = {
// necessary to force annotation trees to be computed.
- sym.annotations.foreach(_.tree)
+ sym.annotations.foreach(_.ensureCompleted)
val annotCtx = ctx.outersIterator.dropWhile(_.owner == sym).next
// necessary in order to mark the typed ahead annotations as definitely typed:
untpd.modsDeco(mdef).mods.annotations.foreach(typedAnnotation(_)(annotCtx))
@@ -1123,6 +1137,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
case rhs => typedExpr(rhs, tpt1.tpe)
}
val vdef1 = assignType(cpy.ValDef(vdef)(name, tpt1, rhs1), sym)
+ if (sym.is(Inline, butNot = DeferredOrParamAccessor))
+ checkInlineConformant(rhs1, "right-hand side of inline value")
patchIfLazy(vdef1)
vdef1
}
@@ -1154,6 +1170,11 @@ 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
+ if (sym.hasAnnotation(defn.InlineAnnot))
+ Inliner.registerInlineInfo(sym, _ => rhs1)
+
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
@@ -1333,6 +1354,16 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
}
}
+ def typedTypedSplice(tree: untpd.TypedSplice)(implicit ctx: Context): Tree =
+ tree.tree match {
+ case tree1: TypeTree => tree1 // no change owner necessary here ...
+ case tree1: Ident => tree1 // ... or here, since these trees cannot contain bindings
+ case tree1 =>
+ if (ctx.owner ne tree.owner) tree1.changeOwner(tree.owner, ctx.owner)
+ else tree1
+ }
+
+
def typedAsFunction(tree: untpd.PostfixOp, pt: Type)(implicit ctx: Context): Tree = {
val untpd.PostfixOp(qual, nme.WILDCARD) = tree
val pt1 = if (defn.isFunctionType(pt)) pt else AnyFunctionProto
@@ -1419,6 +1450,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
case tree: untpd.TypeApply => typedTypeApply(tree, pt)
case tree: untpd.Super => typedSuper(tree, pt)
case tree: untpd.SeqLiteral => typedSeqLiteral(tree, pt)
+ case tree: untpd.Inlined => typedInlined(tree, pt)
case tree: untpd.TypeTree => typedTypeTree(tree, pt)
case tree: untpd.SingletonTypeTree => typedSingletonTypeTree(tree)
case tree: untpd.AndTypeTree => typedAndTypeTree(tree)
@@ -1431,7 +1463,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
case tree: untpd.Alternative => typedAlternative(tree, pt)
case tree: untpd.PackageDef => typedPackageDef(tree)
case tree: untpd.Annotated => typedAnnotated(tree, pt)
- case tree: untpd.TypedSplice => tree.tree
+ case tree: untpd.TypedSplice => typedTypedSplice(tree)
case tree: untpd.UnApply => typedUnApply(tree, pt)
case tree @ untpd.PostfixOp(qual, nme.WILDCARD) => typedAsFunction(tree, pt)
case untpd.EmptyTree => tpd.EmptyTree
@@ -1473,7 +1505,12 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
case Some(xtree) =>
traverse(xtree :: rest)
case none =>
- buf += typed(mdef)
+ typed(mdef) match {
+ case mdef1: DefDef if Inliner.hasBodyToInline(mdef1.symbol) =>
+ buf ++= inlineExpansion(mdef1)
+ case mdef1 =>
+ buf += mdef1
+ }
traverse(rest)
}
case Thicket(stats) :: rest =>
@@ -1487,6 +1524,14 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
traverse(stats)
}
+ /** Given an inline method `mdef`, the method rewritten so that its body
+ * uses accessors to access non-public members, followed by the accessor definitions.
+ * Overwritten in Retyper to return `mdef` unchanged.
+ */
+ protected def inlineExpansion(mdef: DefDef)(implicit ctx: Context): List[Tree] =
+ tpd.cpy.DefDef(mdef)(rhs = Inliner.bodyToInline(mdef.symbol)) ::
+ Inliner.removeInlineAccessors(mdef.symbol)
+
def typedExpr(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree =
typed(tree, pt)(ctx retractMode Mode.PatternOrType)
def typedType(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = // todo: retract mode between Type and Pattern?
@@ -1550,7 +1595,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
}
}
- def adapt(tree: Tree, pt: Type, original: untpd.Tree = untpd.EmptyTree)(implicit ctx: Context) = /*>|>*/ track("adapt") /*<|<*/ {
+ def adapt(tree: Tree, pt: Type, original: untpd.Tree = untpd.EmptyTree)(implicit ctx: Context): Tree = /*>|>*/ track("adapt") /*<|<*/ {
/*>|>*/ ctx.traceIndented(i"adapting $tree of type ${tree.tpe} to $pt", typr, show = true) /*<|<*/ {
if (tree.isDef) interpolateUndetVars(tree, tree.symbol)
else if (!tree.tpe.widen.isInstanceOf[MethodOrPoly]) interpolateUndetVars(tree, NoSymbol)
@@ -1772,8 +1817,15 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
}
tree
}
- else if (tree.tpe <:< pt)
- if (ctx.typeComparer.GADTused && pt.isValueType)
+ else if (tree.tpe <:< pt) {
+ if (pt.hasAnnotation(defn.InlineParamAnnot))
+ checkInlineConformant(tree, "argument to inline parameter")
+ if (Inliner.hasBodyToInline(tree.symbol) &&
+ !ctx.owner.ownersIterator.exists(_.isInlineMethod) &&
+ !ctx.settings.YnoInline.value &&
+ !ctx.isAfterTyper)
+ 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
@@ -1781,6 +1833,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
tree.asInstance(pt)
else
tree
+ }
else if (wtp.isInstanceOf[MethodType]) missingArgs
else {
typr.println(i"adapt to subtype ${tree.tpe} !<:< $pt")
@@ -1806,7 +1859,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
if (folded ne tree) return adaptConstant(folded, folded.tpe.asInstanceOf[ConstantType])
// drop type if prototype is Unit
if (pt isRef defn.UnitClass)
- return tpd.Block(tree :: Nil, Literal(Constant(())))
+ // local adaptation makes sure every adapted tree conforms to its pt
+ // so will take the code path that decides on inlining
+ return tpd.Block(adapt(tree, WildcardType) :: Nil, Literal(Constant(())))
// convert function literal to SAM closure
tree match {
case Closure(Nil, id @ Ident(nme.ANON_FUN), _)
diff --git a/src/dotty/tools/dotc/util/Attachment.scala b/src/dotty/tools/dotc/util/Attachment.scala
index 8088b4cd0..20facfd97 100644
--- a/src/dotty/tools/dotc/util/Attachment.scala
+++ b/src/dotty/tools/dotc/util/Attachment.scala
@@ -4,9 +4,7 @@ package dotty.tools.dotc.util
* adding, removing and lookup of attachments. Attachments are typed key/value pairs.
*/
object Attachment {
-
- /** The class of keys for attachments yielding values of type V */
- class Key[+V]
+ import Property.Key
/** An implementation trait for attachments.
* Clients should inherit from Container instead.
diff --git a/src/dotty/tools/dotc/util/Property.scala b/src/dotty/tools/dotc/util/Property.scala
new file mode 100644
index 000000000..608fc88e6
--- /dev/null
+++ b/src/dotty/tools/dotc/util/Property.scala
@@ -0,0 +1,10 @@
+package dotty.tools.dotc.util
+
+/** Defines a key type with which to tag properties, such as attachments
+ * or context properties
+ */
+object Property {
+
+ /** The class of keys for properties of type V */
+ class Key[+V]
+} \ No newline at end of file
diff --git a/src/dotty/tools/dotc/util/SourceFile.scala b/src/dotty/tools/dotc/util/SourceFile.scala
index 344bc253a..8bd0ecfd6 100644
--- a/src/dotty/tools/dotc/util/SourceFile.scala
+++ b/src/dotty/tools/dotc/util/SourceFile.scala
@@ -140,5 +140,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
}
diff --git a/src/dotty/tools/dotc/util/SourcePosition.scala b/src/dotty/tools/dotc/util/SourcePosition.scala
index 0b2b2aa0b..68a9b6403 100644
--- a/src/dotty/tools/dotc/util/SourcePosition.scala
+++ b/src/dotty/tools/dotc/util/SourcePosition.scala
@@ -5,7 +5,8 @@ package util
import Positions.{Position, NoPosition}
/** A source position is comprised of a position in a source file */
-case class SourcePosition(source: SourceFile, pos: Position) extends interfaces.SourcePosition {
+case class SourcePosition(source: SourceFile, pos: Position, outer: SourcePosition = NoSourcePosition)
+extends interfaces.SourcePosition {
def exists = pos.exists
def lineContent: String = source.lineContent(point)
@@ -24,6 +25,8 @@ case class SourcePosition(source: SourceFile, pos: Position) extends interfaces.
def endLine: Int = source.offsetToLine(end)
def endColumn: Int = source.column(end)
+ def withOuter(outer: SourcePosition) = new SourcePosition(source, pos, outer)
+
override def toString =
if (source.exists) s"${source.file}:${line + 1}"
else s"(no source file, offset = ${pos.point})"
@@ -32,5 +35,6 @@ case class SourcePosition(source: SourceFile, pos: Position) extends interfaces.
/** A sentinel for a non-existing source position */
@sharable object NoSourcePosition extends SourcePosition(NoSource, NoPosition) {
override def toString = "?"
+ override def withOuter(outer: SourcePosition) = outer
}
diff --git a/src/dotty/tools/dotc/util/Stats.scala b/src/dotty/tools/dotc/util/Stats.scala
index fdd3602c9..b7e0996f5 100644
--- a/src/dotty/tools/dotc/util/Stats.scala
+++ b/src/dotty/tools/dotc/util/Stats.scala
@@ -7,27 +7,34 @@ import collection.mutable
@sharable object Stats {
- final val enabled = true
+ final val enabled = false
/** The period in ms in which stack snapshots are displayed */
final val HeartBeatPeriod = 250
+ var monitored = false
+
@volatile private var stack: List[String] = Nil
val hits = new mutable.HashMap[String, Int] {
override def default(key: String): Int = 0
}
- def record(fn: String, n: Int = 1) = {
+ @inline
+ def record(fn: String, n: Int = 1) =
+ if (enabled) doRecord(fn, n)
+
+ private def doRecord(fn: String, n: Int) =
if (monitored) {
val name = if (fn.startsWith("member-")) "member" else fn
hits(name) += n
}
- }
-
- var monitored = false
+ @inline
def track[T](fn: String)(op: => T) =
+ if (enabled) doTrack(fn)(op) else op
+
+ def doTrack[T](fn: String)(op: => T) =
if (monitored) {
stack = fn :: stack
record(fn)
diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala
index c4d8085f5..f161fefe3 100644
--- a/test/dotc/tests.scala
+++ b/test/dotc/tests.scala
@@ -160,7 +160,7 @@ class tests extends CompilerTest {
.filter(_.nonEmpty)
.toList
- @Test def compileStdLib = compileList("compileStdLib", stdlibFiles, "-migration" :: scala2mode)
+ @Test def compileStdLib = compileList("compileStdLib", stdlibFiles, "-migration" :: "-Yno-inline" :: scala2mode)
@Test def compileMixed = compileLine(
"""tests/pos/B.scala
|./scala-scala/src/library/scala/collection/immutable/Seq.scala
@@ -278,7 +278,7 @@ class tests extends CompilerTest {
"ClassOf.scala", "CollectEntryPoints.scala", "Constructors.scala", "CrossCastAnd.scala",
"CtxLazy.scala", "ElimByName.scala", "ElimErasedValueType.scala", "ElimRepeated.scala",
"ElimStaticThis.scala", "Erasure.scala", "ExpandPrivate.scala", "ExpandSAMs.scala",
- "ExplicitOuter.scala", "ExplicitSelf.scala", "ExtensionMethods.scala", "FirstTransform.scala",
+ "ExplicitOuter.scala", "ExtensionMethods.scala", "FirstTransform.scala",
"Flatten.scala", "FullParameterization.scala", "FunctionalInterfaces.scala", "GetClass.scala",
"Getters.scala", "InterceptedMethods.scala", "LambdaLift.scala", "LiftTry.scala", "LinkScala2ImplClasses.scala",
"MacroTransform.scala", "Memoize.scala", "Mixin.scala", "MixinOps.scala", "NonLocalReturns.scala",
diff --git a/test/test/DottyBytecodeTest.scala b/test/test/DottyBytecodeTest.scala
index f2218d4b6..dbf86bf8e 100644
--- a/test/test/DottyBytecodeTest.scala
+++ b/test/test/DottyBytecodeTest.scala
@@ -105,7 +105,7 @@ trait DottyBytecodeTest extends DottyTest {
val a = isaPadded(line-1)
val b = isbPadded(line-1)
- sb append (s"""$line${" " * (lineWidth-line.toString.length)} ${if (a==b) "==" else "<>"} $a${" " * (width-a.length)} | $b""")
+ sb append (s"""$line${" " * (lineWidth-line.toString.length)} ${if (a==b) "==" else "<>"} $a${" " * (width-a.length)} | $b\n""")
}
}
sb.toString
diff --git a/test/test/InlineBytecodeTests.scala b/test/test/InlineBytecodeTests.scala
new file mode 100644
index 000000000..f7dc35305
--- /dev/null
+++ b/test/test/InlineBytecodeTests.scala
@@ -0,0 +1,32 @@
+package test
+
+import org.junit.Assert._
+import org.junit.Test
+
+class InlineBytecodeTests extends DottyBytecodeTest {
+ import ASMConverters._
+ @Test def inlineUnit = {
+ val source = """
+ |class Foo {
+ | inline def foo: Int = 1
+ |
+ | def meth1: Unit = foo
+ | def meth2: Unit = 1
+ |}
+ """.stripMargin
+
+ checkBCode(source) { dir =>
+ val clsIn = dir.lookupName("Foo.class", directory = false).input
+ val clsNode = loadClassNode(clsIn)
+ val meth1 = getMethod(clsNode, "meth1")
+ val meth2 = getMethod(clsNode, "meth2")
+
+ val instructions1 = instructionsFromMethod(meth1)
+ val instructions2 = instructionsFromMethod(meth2)
+
+ assert(instructions1 == instructions2,
+ "`foo` was not properly inlined in `meth1`\n" +
+ diffInstructions(instructions1, instructions2))
+ }
+ }
+}
diff --git a/tests/neg/inlineAccess/C_1.scala b/tests/neg/inlineAccess/C_1.scala
new file mode 100644
index 000000000..9d34fa3f0
--- /dev/null
+++ b/tests/neg/inlineAccess/C_1.scala
@@ -0,0 +1,8 @@
+package p
+private class D
+class C {
+ inline def inl(): Unit = {
+ val d = new D() // error (when inlined): not accessible
+ }
+}
+
diff --git a/tests/neg/inlineAccess/Test_2.scala b/tests/neg/inlineAccess/Test_2.scala
new file mode 100644
index 000000000..98ea7693a
--- /dev/null
+++ b/tests/neg/inlineAccess/Test_2.scala
@@ -0,0 +1,7 @@
+
+object Test {
+ def main(args: Array[String]) = {
+ val c = new p.C()
+ c.inl()
+ }
+}
diff --git a/tests/neg/inlinevals.scala b/tests/neg/inlinevals.scala
new file mode 100644
index 000000000..184aa2168
--- /dev/null
+++ b/tests/neg/inlinevals.scala
@@ -0,0 +1,24 @@
+object Test {
+
+ def power(x: Double, inline n: Int): Double = ???
+
+ inline val N = 10
+ def X = 20
+
+ inline inline val twice = 30 // error: repeated modifier
+
+ class C(inline x: Int, private inline val y: Int) {
+ inline val foo: Int // error: abstract member may not be inline
+ inline def bar: Int // error: abstract member may not be inline
+ }
+
+ power(2.0, N) // ok, since it's a by-name parameter
+ power(2.0, X) // error: argument to inline parameter must be a constant expression
+
+ inline val M = X // error: rhs must be constant expression
+
+ def byname(inline f: => String): Int = ??? // ok
+
+ byname("hello" ++ " world")
+
+}
diff --git a/tests/neg/power.scala b/tests/neg/power.scala
new file mode 100644
index 000000000..6230b4e51
--- /dev/null
+++ b/tests/neg/power.scala
@@ -0,0 +1,15 @@
+object Test {
+
+ @inline
+ def power(x: Double, n: Int): Double =
+ if (n == 0) 1.0
+ else if (n == 1) x
+ else {
+ val y = power(x, n / 2) // error: maximal number of inlines exceeded
+ if (n % 2 == 0) y * y else y * y * x
+ }
+
+ def main(args: Array[String]): Unit = {
+ println(power(2.0, args.length))
+ }
+}
diff --git a/tests/pos/rbtree.scala b/tests/pos/rbtree.scala
index 1401a1231..04c084596 100644
--- a/tests/pos/rbtree.scala
+++ b/tests/pos/rbtree.scala
@@ -430,12 +430,12 @@ object RedBlackTree {
* An alternative is to implement the these classes using plain old Java code...
*/
sealed abstract class Tree[A, +B](
- @(inline @getter) final val key: A,
- @(inline @getter) final val value: B,
- @(inline @getter) final val left: Tree[A, B],
- @(inline @getter) final val right: Tree[A, B])
+ @(`inline` @getter) final val key: A,
+ @(`inline` @getter) final val value: B,
+ @(`inline` @getter) final val left: Tree[A, B],
+ @(`inline` @getter) final val right: Tree[A, B])
extends Serializable {
- @(inline @getter) final val count: Int = 1 + RedBlackTree.count(left) + RedBlackTree.count(right)
+ @(`inline` @getter) final val count: Int = 1 + RedBlackTree.count(left) + RedBlackTree.count(right)
def black: Tree[A, B]
def red: Tree[A, B]
}
diff --git a/tests/run/inline.check b/tests/run/inline.check
new file mode 100644
index 000000000..5f711274b
--- /dev/null
+++ b/tests/run/inline.check
@@ -0,0 +1,9 @@
+100
+10000
+
+ Inner
+Outer.f
+Outer.f Inner
+ Inner
+Outer.f
+Outer.f Inner
diff --git a/tests/run/inline/Test_2.scala b/tests/run/inline/Test_2.scala
new file mode 100644
index 000000000..605868c80
--- /dev/null
+++ b/tests/run/inline/Test_2.scala
@@ -0,0 +1,21 @@
+object Test {
+
+ import p.inlines._
+
+ def main(args: Array[String]): Unit = {
+ println(f(10))
+ println(f(f(10)))
+
+ track("hello") { println("") }
+
+ val o = new Outer
+ val i = new o.Inner
+ println(i.m)
+ println(i.g)
+ println(i.h)
+ println(o.inner.m)
+ println(o.inner.g)
+ println(o.inner.h)
+ }
+
+}
diff --git a/tests/run/inline/inlines_1.scala b/tests/run/inline/inlines_1.scala
new file mode 100644
index 000000000..24f1c78fe
--- /dev/null
+++ b/tests/run/inline/inlines_1.scala
@@ -0,0 +1,41 @@
+package p
+import collection.mutable
+
+object inlines {
+
+ final val monitored = false
+
+ inline def f(x: Int): Int = x * x
+
+ val hits = new mutable.HashMap[String, Int] {
+ override def default(key: String): Int = 0
+ }
+
+ def record(fn: String, n: Int = 1) = {
+ if (monitored) {
+ val name = if (fn.startsWith("member-")) "member" else fn
+ hits(name) += n
+ }
+ }
+
+ @volatile private var stack: List[String] = Nil
+
+ inline def track[T](fn: String)(op: => T) =
+ if (monitored) {
+ stack = fn :: stack
+ record(fn)
+ try op
+ finally stack = stack.tail
+ } else op
+
+ class Outer {
+ def f = "Outer.f"
+ class Inner {
+ val msg = " Inner"
+ inline def m = msg
+ inline def g = f
+ inline def h = f ++ m
+ }
+ val inner = new Inner
+ }
+}
diff --git a/tests/run/inlineAccess/C_1.scala b/tests/run/inlineAccess/C_1.scala
new file mode 100644
index 000000000..349f5b150
--- /dev/null
+++ b/tests/run/inlineAccess/C_1.scala
@@ -0,0 +1,7 @@
+package p {
+class C {
+ protected def f(): Unit = ()
+
+ inline def inl() = f() // error (when inlined): not accessible
+}
+}
diff --git a/tests/run/inlineAccess/Test_2.scala b/tests/run/inlineAccess/Test_2.scala
new file mode 100644
index 000000000..98ea7693a
--- /dev/null
+++ b/tests/run/inlineAccess/Test_2.scala
@@ -0,0 +1,7 @@
+
+object Test {
+ def main(args: Array[String]) = {
+ val c = new p.C()
+ c.inl()
+ }
+}
diff --git a/tests/run/inlineArrowAssoc.scala b/tests/run/inlineArrowAssoc.scala
new file mode 100644
index 000000000..c3625609a
--- /dev/null
+++ b/tests/run/inlineArrowAssoc.scala
@@ -0,0 +1,24 @@
+import scala.collection.immutable._
+
+import scala.collection.mutable.{ Builder, ListBuffer }
+
+object Test {
+
+ private val defaultOrdering = Map[Numeric[_], Ordering[_]](
+ Numeric.BigIntIsIntegral -> Ordering.BigInt,
+ Numeric.IntIsIntegral -> Ordering.Int
+ )
+
+ final implicit class ArrowAssoc[A](private val self: A) extends AnyVal {
+ @inline def -> [B](y: B): Tuple2[A, B] = Tuple2(self, y)
+ def →[B](y: B): Tuple2[A, B] = ->(y)
+ }
+
+ def main(args: Array[String]): Unit = {
+ assert((1 -> 2) == (1, 2))
+ assert((1 → 2) == (1, 2))
+ }
+
+
+}
+
diff --git a/tests/run/inlineForeach.check b/tests/run/inlineForeach.check
new file mode 100644
index 000000000..3fced2fad
--- /dev/null
+++ b/tests/run/inlineForeach.check
@@ -0,0 +1,137 @@
+1
+2
+3
+4
+5
+6
+7
+8
+9
+1
+2
+3
+4
+5
+6
+7
+8
+9
+1
+2
+3
+4
+5
+6
+7
+8
+9
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+2
+2
+2
+2
+2
+2
+2
+2
+2
+2
+3
+3
+3
+3
+3
+3
+3
+3
+3
+3
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+5
+5
+5
+5
+5
+5
+5
+5
+5
+5
+6
+6
+6
+6
+6
+6
+6
+6
+6
+6
+7
+7
+7
+7
+7
+7
+7
+7
+7
+7
+8
+8
+8
+8
+8
+8
+8
+8
+8
+8
+9
+9
+9
+9
+9
+9
+9
+9
+9
+9
+10
+10
+10
+10
+10
+10
+10
+10
+10
+10
diff --git a/tests/run/inlineForeach.scala b/tests/run/inlineForeach.scala
new file mode 100644
index 000000000..1389ad6c4
--- /dev/null
+++ b/tests/run/inlineForeach.scala
@@ -0,0 +1,48 @@
+object Test {
+
+ class Range(from: Int, end: Int) {
+
+ inline
+ def foreach(inline op: Int => Unit): Unit = {
+ var i = from
+ while (i < end) {
+ op(i)
+ i += 1
+ }
+ }
+
+ def filter(p: Int => Boolean): List[Int] = ???
+ }
+
+ implicit class intWrapper(private val start: Int) extends AnyVal {
+ def until(end: Int) = new Range(start, end)
+ def to(limit: Int) = new Range(start, limit + 1)
+ }
+
+ def matmul(xs: Array[Array[Double]], ys: Array[Array[Double]]): Array[Array[Double]] = {
+ def nrows = xs.length
+ def ncols = ys(0).length
+ def n = ys.length
+ assert(xs(0).length == n)
+ val zs = Array.ofDim[Double](nrows, ncols)
+ for (i <- intWrapper(0) until nrows)
+ for (j <- 0 until ncols) {
+ var x = 0.0
+ for (k <- 0 until n)
+ x += xs(i)(k) * ys(k)(j)
+ zs(i)(j) = x
+ }
+ zs
+ }
+
+ def main(args: Array[String]) = {
+ 1.until(10).foreach(i => println(i))
+ 1.until(10).foreach(println(_))
+ 1.until(10).foreach(println)
+ for (i <- 1 to 10) println(i)
+
+ for (k1 <- 1 to 10)
+ for (k2 <- 1 to 10)
+ println(s"$k1")
+ }
+}
diff --git a/tests/run/inlinePower.check b/tests/run/inlinePower.check
new file mode 100644
index 000000000..25e115634
--- /dev/null
+++ b/tests/run/inlinePower.check
@@ -0,0 +1,2 @@
+1024.0
+2048.0
diff --git a/tests/run/inlinePower/Test_2.scala b/tests/run/inlinePower/Test_2.scala
new file mode 100644
index 000000000..8e16587b5
--- /dev/null
+++ b/tests/run/inlinePower/Test_2.scala
@@ -0,0 +1,9 @@
+import p.pow.power
+object Test {
+
+ def main(args: Array[String]): Unit = {
+ println(power(2.0, 10))
+ def x = 2.0
+ println(power(x, 11))
+ }
+}
diff --git a/tests/run/inlinePower/power_1.scala b/tests/run/inlinePower/power_1.scala
new file mode 100644
index 000000000..4e96d7caa
--- /dev/null
+++ b/tests/run/inlinePower/power_1.scala
@@ -0,0 +1,12 @@
+package p
+
+object pow {
+
+ inline def power(x: Double, n: Int): Double =
+ if (n == 0) 1.0
+ else if (n == 1) x
+ else {
+ val y = power(x, n / 2)
+ if (n % 2 == 0) y * y else y * y * x
+ }
+}
diff --git a/tests/run/inlinePrivates.scala b/tests/run/inlinePrivates.scala
new file mode 100644
index 000000000..ce438ae8d
--- /dev/null
+++ b/tests/run/inlinePrivates.scala
@@ -0,0 +1,36 @@
+object Test {
+
+ class C[T](private val x: T) {
+
+ private def foo[Z](z: Z): T = x
+
+ private var y: T = _
+
+ inline def get1 = x
+ inline def get2[U](c: C[U]) = c.x
+
+ inline def foo1(x: Int) = foo(x)
+ inline def foo2[U](c: C[U]) = c.foo(x)
+
+ inline def set1(z: T) = { y = z; y }
+ inline def set2[U](c: C[U]) = { c.y = c.x; c.y }
+ }
+
+ object CC {
+ private val x = 3
+ inline def get1 = x
+ }
+
+ def main(args: Array[String]) = {
+ val cc = new C(2)
+ assert(cc.get1 == 2)
+ assert(cc.get2(cc) == 2)
+ assert(cc.foo1(1) == 2)
+ assert(cc.foo2(cc) == 2)
+ assert(cc.set1(3) == 3)
+ assert(cc.set2(cc) == 2)
+
+ assert(CC.get1 == 3)
+ }
+
+}
diff --git a/tests/run/inlinedAssign.scala b/tests/run/inlinedAssign.scala
new file mode 100644
index 000000000..1b524f92b
--- /dev/null
+++ b/tests/run/inlinedAssign.scala
@@ -0,0 +1,24 @@
+object Test {
+
+ inline def swap[T](x: T, inline x_= : T => Unit, y: T, inline y_= : T => Unit) = {
+ x_=(y)
+ y_=(x)
+ }
+
+ inline def f(x: Int => Unit) = x
+
+ def main(args: Array[String]) = {
+ var x = 1
+ var y = 2
+ inline def setX(z: Int) = x = z
+ inline def setY(z: Int) = y = z
+ swap(x, setX, y, setY)
+ assert(x == 2 && y == 1)
+
+ swap(x, x = _, y, y = _)
+ assert(x == 1 && y == 2)
+
+
+ val z = f(setX) // tests case where inline arg is not applied
+ }
+}
diff --git a/tests/run/outerPatternMatch/Outer_1.scala b/tests/run/outerPatternMatch/Outer_1.scala
new file mode 100644
index 000000000..c3b102323
--- /dev/null
+++ b/tests/run/outerPatternMatch/Outer_1.scala
@@ -0,0 +1,6 @@
+class Outer {
+
+ class Inner
+
+}
+
diff --git a/tests/run/outerPatternMatch/Test_2.scala b/tests/run/outerPatternMatch/Test_2.scala
new file mode 100644
index 000000000..e46f52f20
--- /dev/null
+++ b/tests/run/outerPatternMatch/Test_2.scala
@@ -0,0 +1,14 @@
+object Test {
+
+ def main(args: Array[String]): Unit = {
+ val x = new Outer
+ val y = new Outer
+ val i = new x.Inner
+ val j = new y.Inner
+ i match {
+ case _: y.Inner => assert(false)
+ case _: x.Inner => // OK
+ }
+ }
+
+}