Skip to content

Commit 87a7757

Browse files
authored
Merge pull request #1492 from dotty-staging/add-inline
Implement inline
2 parents a306462 + e0a14e7 commit 87a7757

File tree

100 files changed

+1903
-376
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

100 files changed

+1903
-376
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object A {
2+
def get: Int = 1
3+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
object C {
2+
def main(args: Array[String]): Unit = {
3+
val i: Int = B.getInline
4+
}
5+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
object B {
2+
@inline def getInline: Int =
3+
A.get
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
object B {
2+
@inline def getInline: Double =
3+
A.get
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
object B {
2+
@inline def getInline: Int =
3+
sys.error("This is an expected failure when running C")
4+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import sbt._
2+
import Keys._
3+
4+
object DottyInjectedPlugin extends AutoPlugin {
5+
override def requires = plugins.JvmPlugin
6+
override def trigger = allRequirements
7+
8+
override val projectSettings = Seq(
9+
scalaVersion := "0.1-SNAPSHOT",
10+
scalaOrganization := "ch.epfl.lamp",
11+
scalacOptions += "-language:Scala2",
12+
scalaBinaryVersion := "2.11",
13+
autoScalaLibrary := false,
14+
libraryDependencies ++= Seq("org.scala-lang" % "scala-library" % "2.11.5"),
15+
scalaCompilerBridgeSource := ("ch.epfl.lamp" % "dotty-bridge" % "0.1.1-SNAPSHOT" % "component").sources()
16+
)
17+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
$ copy-file changes/B1.scala B.scala
2+
> compile
3+
4+
$ copy-file changes/B2.scala B.scala
5+
# Compilation of C.scala should fail because B.getInline now has type Double instead of Int
6+
-> compile
7+
8+
$ copy-file changes/B1.scala B.scala
9+
> run
10+
11+
$ copy-file changes/B3.scala B.scala
12+
# The body of B.getInline was changed so C.scala should be recompiled
13+
# If it was recompiled, run should fail since B.getInline now throws an exception
14+
-> run

docs/SyntaxSummary.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,14 +231,15 @@ grammar.
231231
ClsParamClauses ::= {ClsParamClause} [[nl] `(' `implicit' ClsParams `)']
232232
ClsParamClause ::= [nl] `(' [ClsParams] ')'
233233
ClsParams ::= ClsParam {`' ClsParam}
234-
ClsParam ::= {Annotation} [{Modifier} (`val' | `var')] Param ValDef(mods, id, tpe, expr) -- point of mods on val/var
234+
ClsParam ::= {Annotation}
235+
[{Modifier} (`val' | `var') | `inline'] Param ValDef(mods, id, tpe, expr) -- point of mods on val/var
235236
Param ::= id `:' ParamType [`=' Expr]
236237
| INT
237238

238239
DefParamClauses ::= {DefParamClause} [[nl] `(' `implicit' DefParams `)']
239240
DefParamClause ::= [nl] `(' [DefParams] ')'
240241
DefParams ::= DefParam {`,' DefParam}
241-
DefParam ::= {Annotation} Param ValDef(mods, id, tpe, expr) -- point of mods at id.
242+
DefParam ::= {Annotation} [`inline'] Param ValDef(mods, id, tpe, expr) -- point of mods at id.
242243

243244
Bindings ::= `(' Binding {`,' Binding `)' bindings
244245
Binding ::= (id | `_') [`:' Type] ValDef(_, id, tpe, EmptyTree)

dottydoc/test/BaseTest.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ trait DottyTest {
1818
val ctx = base.initialCtx.fresh
1919
ctx.setSetting(ctx.settings.language, List("Scala2"))
2020
ctx.setSetting(ctx.settings.YkeepComments, true)
21+
ctx.setSetting(ctx.settings.YnoInline, true)
2122
base.initialize()(ctx)
2223
ctx
2324
}

project/Build.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ object DottyBuild extends Build {
6464
lazy val dotty = project.in(file(".")).
6565
dependsOn(`dotty-interfaces`).
6666
settings(
67+
// Disable scaladoc generation, makes publishLocal much faster
68+
publishArtifact in packageDoc := false,
69+
6770
overrideScalaVersionSetting,
6871

6972
// set sources to src/, tests to test/ and resources to resources/
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package dotty.annotation.internal
2+
3+
import scala.annotation.Annotation
4+
5+
/** The class associated with a `BodyAnnotation`, which indicates
6+
* an inline method's right hand side
7+
*/
8+
final class Body() extends Annotation
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package dotty.annotation.internal
2+
3+
import scala.annotation.Annotation
4+
5+
/** An annotation produced by Namer to indicate an inline parameter */
6+
final class InlineParam() extends Annotation

src/dotty/tools/dotc/Compiler.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ class Compiler {
9494
new SelectStatic, // get rid of selects that would be compiled into GetStatic
9595
new CollectEntryPoints, // Find classes with main methods
9696
new CollectSuperCalls, // Find classes that are called with super
97+
new DropInlined, // Drop Inlined nodes, since backend has no use for them
9798
new MoveStatics, // Move static methods to companion classes
9899
new LabelDefs), // Converts calls to labels to jumps
99100
List(new GenSJSIR), // Generate .js code

src/dotty/tools/dotc/FromTasty.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ object FromTasty extends Driver {
8686
case info: ClassfileLoader =>
8787
info.load(clsd) match {
8888
case Some(unpickler: DottyUnpickler) =>
89-
val List(unpickled) = unpickler.body(readPositions = true)
89+
val List(unpickled) = unpickler.body(ctx.addMode(Mode.ReadPositions))
9090
val unit1 = new CompilationUnit(new SourceFile(clsd.symbol.sourceFile, Seq()))
9191
unit1.tpdTree = unpickled
9292
unit1.unpicklers += (clsd.classSymbol -> unpickler.unpickler)

src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._
88
import Decorators._
99
import language.higherKinds
1010
import collection.mutable.ListBuffer
11-
import util.Attachment
11+
import util.Property
1212

1313
object desugar {
1414
import untpd._
1515

1616
/** Tags a .withFilter call generated by desugaring a for expression.
1717
* Such calls can alternatively be rewritten to use filter.
1818
*/
19-
val MaybeFilter = new Attachment.Key[Unit]
19+
val MaybeFilter = new Property.Key[Unit]
2020

2121
/** Info of a variable in a pattern: The named tree and its type */
2222
private type VarInfo = (NameTree, Tree)
@@ -607,11 +607,17 @@ object desugar {
607607
* ==>
608608
* def $anonfun(params) = body
609609
* Closure($anonfun)
610+
*
611+
* If `inlineable` is true, tag $anonfun with an @inline annotation.
610612
*/
611-
def makeClosure(params: List[ValDef], body: Tree, tpt: Tree = TypeTree()) =
613+
def makeClosure(params: List[ValDef], body: Tree, tpt: Tree = TypeTree(), inlineable: Boolean)(implicit ctx: Context) = {
614+
var mods = synthetic
615+
if (inlineable)
616+
mods = mods.withAddedAnnotation(New(ref(defn.InlineAnnotType), Nil).withPos(body.pos))
612617
Block(
613-
DefDef(nme.ANON_FUN, Nil, params :: Nil, tpt, body).withMods(synthetic),
618+
DefDef(nme.ANON_FUN, Nil, params :: Nil, tpt, body).withMods(mods),
614619
Closure(Nil, Ident(nme.ANON_FUN), EmptyTree))
620+
}
615621

616622
/** If `nparams` == 1, expand partial function
617623
*

src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,6 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
8888
case mp => mp
8989
}
9090

91-
/** If tree is a closure, it's body, otherwise tree itself */
92-
def closureBody(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match {
93-
case Block((meth @ DefDef(nme.ANON_FUN, _, _, _, _)) :: Nil, Closure(_, _, _)) => meth.rhs
94-
case _ => tree
95-
}
96-
9791
/** If this is an application, its function part, stripping all
9892
* Apply nodes (but leaving TypeApply nodes in). Otherwise the tree itself.
9993
*/
@@ -311,6 +305,8 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
311305
if (vdef.symbol.flags is Mutable) Impure else exprPurity(vdef.rhs)
312306
case _ =>
313307
Impure
308+
// TODO: It seem like this should be exprPurity(tree)
309+
// But if we do that the repl/vars test break. Need to figure out why that's the case.
314310
}
315311

316312
/** The purity level of this expression.
@@ -327,13 +323,13 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
327323
case EmptyTree
328324
| This(_)
329325
| Super(_, _)
330-
| Literal(_) =>
326+
| Literal(_)
327+
| Closure(_, _, _) =>
331328
Pure
332329
case Ident(_) =>
333330
refPurity(tree)
334331
case Select(qual, _) =>
335-
refPurity(tree).min(
336-
if (tree.symbol.is(Inline)) Pure else exprPurity(qual))
332+
refPurity(tree).min(exprPurity(qual))
337333
case TypeApply(fn, _) =>
338334
exprPurity(fn)
339335
/*
@@ -435,6 +431,36 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
435431
}
436432
}
437433

434+
/** Decompose a call fn[targs](vargs_1)...(vargs_n)
435+
* into its constituents (where targs, vargss may be empty)
436+
*/
437+
def decomposeCall(tree: Tree): (Tree, List[Tree], List[List[Tree]]) = tree match {
438+
case Apply(fn, args) =>
439+
val (meth, targs, argss) = decomposeCall(fn)
440+
(meth, targs, argss :+ args)
441+
case TypeApply(fn, targs) =>
442+
val (meth, Nil, Nil) = decomposeCall(fn)
443+
(meth, targs, Nil)
444+
case _ =>
445+
(tree, Nil, Nil)
446+
}
447+
448+
/** An extractor for closures, either contained in a block or standalone.
449+
*/
450+
object closure {
451+
def unapply(tree: Tree): Option[(List[Tree], Tree, Tree)] = tree match {
452+
case Block(_, Closure(env, meth, tpt)) => Some(env, meth, tpt)
453+
case Closure(env, meth, tpt) => Some(env, meth, tpt)
454+
case _ => None
455+
}
456+
}
457+
458+
/** If tree is a closure, its body, otherwise tree itself */
459+
def closureBody(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match {
460+
case Block((meth @ DefDef(nme.ANON_FUN, _, _, _, _)) :: Nil, Closure(_, _, _)) => meth.rhs
461+
case _ => tree
462+
}
463+
438464
/** The variables defined by a pattern, in reverse order of their appearance. */
439465
def patVars(tree: Tree)(implicit ctx: Context): List[Symbol] = {
440466
val acc = new TreeAccumulator[List[Symbol]] {

src/dotty/tools/dotc/ast/TreeTypeMap.scala

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,20 @@ final class TreeTypeMap(
9292
case ddef @ DefDef(name, tparams, vparamss, tpt, _) =>
9393
val (tmap1, tparams1) = transformDefs(ddef.tparams)
9494
val (tmap2, vparamss1) = tmap1.transformVParamss(vparamss)
95-
cpy.DefDef(ddef)(name, tparams1, vparamss1, tmap2.transform(tpt), tmap2.transform(ddef.rhs))
95+
val res = cpy.DefDef(ddef)(name, tparams1, vparamss1, tmap2.transform(tpt), tmap2.transform(ddef.rhs))
96+
res.symbol.transformAnnotations {
97+
case ann: BodyAnnotation => ann.derivedAnnotation(res.rhs)
98+
case ann => ann
99+
}
100+
res
96101
case blk @ Block(stats, expr) =>
97102
val (tmap1, stats1) = transformDefs(stats)
98103
val expr1 = tmap1.transform(expr)
99104
cpy.Block(blk)(stats1, expr1)
105+
case inlined @ Inlined(call, bindings, expanded) =>
106+
val (tmap1, bindings1) = transformDefs(bindings)
107+
val expanded1 = tmap1.transform(expanded)
108+
cpy.Inlined(inlined)(call, bindings1, expanded1)
100109
case cdef @ CaseDef(pat, guard, rhs) =>
101110
val tmap = withMappedSyms(patVars(pat))
102111
val pat1 = tmap.transform(pat)
@@ -127,10 +136,7 @@ final class TreeTypeMap(
127136

128137
def apply[ThisTree <: tpd.Tree](tree: ThisTree): ThisTree = transform(tree).asInstanceOf[ThisTree]
129138

130-
def apply(annot: Annotation): Annotation = {
131-
val tree1 = apply(annot.tree)
132-
if (tree1 eq annot.tree) annot else ConcreteAnnotation(tree1)
133-
}
139+
def apply(annot: Annotation): Annotation = annot.derivedAnnotation(apply(annot.tree))
134140

135141
/** The current tree map composed with a substitution [from -> to] */
136142
def withSubstitution(from: List[Symbol], to: List[Symbol]): TreeTypeMap =

src/dotty/tools/dotc/ast/Trees.scala

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import collection.immutable.IndexedSeq
1212
import collection.mutable.ListBuffer
1313
import parsing.Tokens.Token
1414
import printing.Printer
15-
import util.{Stats, Attachment, DotClass}
15+
import util.{Stats, Attachment, Property, DotClass}
1616
import annotation.unchecked.uncheckedVariance
1717
import language.implicitConversions
1818
import parsing.Scanners.Comment
@@ -30,8 +30,8 @@ object Trees {
3030
/** The total number of created tree nodes, maintained if Stats.enabled */
3131
@sharable var ntrees = 0
3232

33-
/** Attachment key for trees with documentation strings attached */
34-
val DocComment = new Attachment.Key[Comment]
33+
/** Property key for trees with documentation strings attached */
34+
val DocComment = new Property.Key[Comment]
3535

3636
@sharable private var nextId = 0 // for debugging
3737

@@ -503,6 +503,25 @@ object Trees {
503503
override def toString = s"JavaSeqLiteral($elems, $elemtpt)"
504504
}
505505

506+
/** A tree representing inlined code.
507+
*
508+
* @param call The original call that was inlined
509+
* @param bindings Bindings for proxies to be used in the inlined code
510+
* @param expansion The inlined tree, minus bindings.
511+
*
512+
* The full inlined code is equivalent to
513+
*
514+
* { bindings; expansion }
515+
*
516+
* The reason to keep `bindings` separate is because they are typed in a
517+
* different context: `bindings` represent the arguments to the inlined
518+
* call, whereas `expansion` represents the body of the inlined function.
519+
*/
520+
case class Inlined[-T >: Untyped] private[ast] (call: tpd.Tree, bindings: List[MemberDef[T]], expansion: Tree[T])
521+
extends Tree[T] {
522+
type ThisTree[-T >: Untyped] = Inlined[T]
523+
}
524+
506525
/** A type tree that represents an existing or inferred type */
507526
case class TypeTree[-T >: Untyped] private[ast] (original: Tree[T])
508527
extends DenotingTree[T] with TypTree[T] {
@@ -797,6 +816,7 @@ object Trees {
797816
type Try = Trees.Try[T]
798817
type SeqLiteral = Trees.SeqLiteral[T]
799818
type JavaSeqLiteral = Trees.JavaSeqLiteral[T]
819+
type Inlined = Trees.Inlined[T]
800820
type TypeTree = Trees.TypeTree[T]
801821
type SingletonTypeTree = Trees.SingletonTypeTree[T]
802822
type AndTypeTree = Trees.AndTypeTree[T]
@@ -939,6 +959,10 @@ object Trees {
939959
case tree: SeqLiteral if (elems eq tree.elems) && (elemtpt eq tree.elemtpt) => tree
940960
case _ => finalize(tree, untpd.SeqLiteral(elems, elemtpt))
941961
}
962+
def Inlined(tree: Tree)(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(implicit ctx: Context): Inlined = tree match {
963+
case tree: Inlined if (call eq tree.call) && (bindings eq tree.bindings) && (expansion eq tree.expansion) => tree
964+
case _ => finalize(tree, untpd.Inlined(call, bindings, expansion))
965+
}
942966
def TypeTree(tree: Tree)(original: Tree): TypeTree = tree match {
943967
case tree: TypeTree if original eq tree.original => tree
944968
case _ => finalize(tree, untpd.TypeTree(original))
@@ -1083,6 +1107,8 @@ object Trees {
10831107
cpy.Try(tree)(transform(block), transformSub(cases), transform(finalizer))
10841108
case SeqLiteral(elems, elemtpt) =>
10851109
cpy.SeqLiteral(tree)(transform(elems), transform(elemtpt))
1110+
case Inlined(call, bindings, expansion) =>
1111+
cpy.Inlined(tree)(call, transformSub(bindings), transform(expansion))
10861112
case TypeTree(original) =>
10871113
tree
10881114
case SingletonTypeTree(ref) =>
@@ -1185,6 +1211,8 @@ object Trees {
11851211
this(this(this(x, block), handler), finalizer)
11861212
case SeqLiteral(elems, elemtpt) =>
11871213
this(this(x, elems), elemtpt)
1214+
case Inlined(call, bindings, expansion) =>
1215+
this(this(x, bindings), expansion)
11881216
case TypeTree(original) =>
11891217
x
11901218
case SingletonTypeTree(ref) =>

0 commit comments

Comments
 (0)