Skip to content

Commit 31f9a1b

Browse files
authored
Merge pull request #4755 from dotty-staging/forceInline
Introduce `@forceInline` annotation
2 parents d761815 + 6d7dd2c commit 31f9a1b

40 files changed

+101
-74
lines changed

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1052,7 +1052,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
10521052
def enclosingInlineds(implicit ctx: Context): List[Tree] =
10531053
ctx.property(InlinedCalls).getOrElse(Nil)
10541054

1055-
/** The source file where the symbol of the `@inline` method referred to by `call`
1055+
/** The source file where the symbol of the `inline` method referred to by `call`
10561056
* is defined
10571057
*/
10581058
def sourceFile(call: Tree)(implicit ctx: Context) = {

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -707,8 +707,8 @@ class Definitions {
707707
def ImplicitAmbiguousAnnot(implicit ctx: Context) = ImplicitAmbiguousAnnotType.symbol.asClass
708708
lazy val ImplicitNotFoundAnnotType = ctx.requiredClassRef("scala.annotation.implicitNotFound")
709709
def ImplicitNotFoundAnnot(implicit ctx: Context) = ImplicitNotFoundAnnotType.symbol.asClass
710-
lazy val InlineAnnotType = ctx.requiredClassRef("scala.inline")
711-
def InlineAnnot(implicit ctx: Context) = InlineAnnotType.symbol.asClass
710+
lazy val ForceInlineAnnotType = ctx.requiredClassRef("scala.forceInline")
711+
def ForceInlineAnnot(implicit ctx: Context) = ForceInlineAnnotType.symbol.asClass
712712
lazy val InlineParamAnnotType = ctx.requiredClassRef("scala.annotation.internal.InlineParam")
713713
def InlineParamAnnot(implicit ctx: Context) = InlineParamAnnotType.symbol.asClass
714714
lazy val InvariantBetweenAnnotType = ctx.requiredClassRef("scala.annotation.internal.InvariantBetween")

compiler/src/dotty/tools/dotc/core/Symbols.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,6 @@ object Symbols {
803803
override def toString: String = value.asScala.toString()
804804
}
805805

806-
@inline def newMutableSymbolMap[T]: MutableSymbolMap[T] =
806+
@forceInline def newMutableSymbolMap[T]: MutableSymbolMap[T] =
807807
new MutableSymbolMap(new java.util.IdentityHashMap[Symbol, T]())
808808
}

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3935,7 +3935,7 @@ object Types {
39353935
abstract class VariantTraversal {
39363936
protected[core] var variance = 1
39373937

3938-
@inline protected def atVariance[T](v: Int)(op: => T): T = {
3938+
@forceInline protected def atVariance[T](v: Int)(op: => T): T = {
39393939
val saved = variance
39403940
variance = v
39413941
val res = op

compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ object MarkupParsers {
316316
}
317317

318318
/** Some try/catch/finally logic used by xLiteral and xLiteralPattern. */
319-
@inline private def xLiteralCommon(f: () => Tree, ifTruncated: String => Unit): Tree = {
319+
@forceInline private def xLiteralCommon(f: () => Tree, ifTruncated: String => Unit): Tree = {
320320
var output: Tree = null.asInstanceOf[Tree]
321321
try output = f()
322322
catch {

compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,12 @@ object SyntaxHighlighting {
5151
val newBuf = new StringBuilder
5252
var lastValDefToken = ""
5353

54-
@inline def keywordStart =
54+
@forceInline def keywordStart =
5555
prev == 0 || prev == ' ' || prev == '{' || prev == '(' ||
5656
prev == '\n' || prev == '[' || prev == ',' || prev == ':' ||
5757
prev == '|' || prev == '&'
5858

59-
@inline def numberStart(c: Char) =
59+
@forceInline def numberStart(c: Char) =
6060
c.isDigit && (!prev.isLetter || prev == '.' || prev == ' ' || prev == '(' || prev == '\u0000')
6161

6262
def takeChar(): Char = takeChars(1).head

compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1748,7 +1748,7 @@ object messages {
17481748
val kind = "Syntax"
17491749
val msg = hl"no explicit ${"return"} allowed from inline $owner"
17501750
val explanation =
1751-
hl"""Methods marked with ${"@inline"} may not use ${"return"} statements.
1751+
hl"""Methods marked with ${"inline"} modifier may not use ${"return"} statements.
17521752
|Instead, you should rely on the last expression's value being
17531753
|returned from a method.
17541754
|"""

compiler/src/dotty/tools/dotc/reporting/trace.scala

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,19 @@ import core.Mode
99

1010
object trace {
1111

12-
@inline
12+
@forceInline
1313
def onDebug[TD](question: => String)(op: => TD)(implicit ctx: Context): TD =
1414
conditionally(ctx.settings.YdebugTrace.value, question, false)(op)
1515

16-
@inline
16+
@forceInline
1717
def conditionally[TC](cond: Boolean, question: => String, show: Boolean)(op: => TC)(implicit ctx: Context): TC =
1818
if (Config.tracingEnabled) {
1919
def op1 = op
2020
if (cond) apply[TC](question, Printers.default, show)(op1)
2121
else op1
2222
} else op
2323

24-
@inline
24+
@forceInline
2525
def apply[T](question: => String, printer: Printers.Printer, showOp: Any => String)(op: => T)(implicit ctx: Context): T =
2626
if (Config.tracingEnabled) {
2727
def op1 = op
@@ -30,7 +30,7 @@ object trace {
3030
}
3131
else op
3232

33-
@inline
33+
@forceInline
3434
def apply[T](question: => String, printer: Printers.Printer, show: Boolean)(op: => T)(implicit ctx: Context): T =
3535
if (Config.tracingEnabled) {
3636
def op1 = op
@@ -39,15 +39,15 @@ object trace {
3939
}
4040
else op
4141

42-
@inline
42+
@forceInline
4343
def apply[T](question: => String, printer: Printers.Printer)(op: => T)(implicit ctx: Context): T =
4444
apply[T](question, printer, false)(op)
4545

46-
@inline
46+
@forceInline
4747
def apply[T](question: => String, show: Boolean)(op: => T)(implicit ctx: Context): T =
4848
apply[T](question, Printers.default, show)(op)
4949

50-
@inline
50+
@forceInline
5151
def apply[T](question: => String)(op: => T)(implicit ctx: Context): T =
5252
apply[T](question, Printers.default, false)(op)
5353

compiler/src/dotty/tools/dotc/typer/Inliner.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ object Inliner {
229229
}
230230

231231
/** `sym` has an inline method with a known body to inline (note: definitions coming
232-
* from Scala2x class files might be `@inline`, but still lack that body.
232+
* from Scala2x class files might be `@forceInline`, but still lack that body.
233233
*/
234234
def hasBodyToInline(sym: SymDenotation)(implicit ctx: Context): Boolean =
235235
sym.isInlinedMethod && sym.hasAnnotation(defn.BodyAnnot) // TODO: Open this up for transparent methods as well
@@ -240,7 +240,7 @@ object Inliner {
240240
def bodyToInline(sym: SymDenotation)(implicit ctx: Context): Tree =
241241
sym.unforcedAnnotation(defn.BodyAnnot).get.tree
242242

243-
/** Try to inline a call to a `@inline` method. Fail with error if the maximal
243+
/** Try to inline a call to a `inline` method. Fail with error if the maximal
244244
* inline depth is exceeded.
245245
*
246246
* @param tree The call to inline
@@ -281,7 +281,7 @@ object Inliner {
281281

282282
/** Produces an inlined version of `call` via its `inlined` method.
283283
*
284-
* @param call the original call to a `@inline` method
284+
* @param call the original call to an `inline` method
285285
* @param rhsToInline the body of the inline method that replaces the call.
286286
*/
287287
class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {

compiler/src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -751,7 +751,7 @@ class Namer { typer: Typer =>
751751
if (sym.unforcedAnnotation(cls).isEmpty) {
752752
val ann = Annotation.deferred(cls, implicit ctx => typedAheadAnnotation(annotTree))
753753
sym.addAnnotation(ann)
754-
if (cls == defn.InlineAnnot && sym.is(Method, butNot = Accessor))
754+
if (cls == defn.ForceInlineAnnot && sym.is(Method, butNot = Accessor))
755755
sym.setFlag(Inline)
756756
}
757757
}

compiler/src/dotty/tools/dotc/util/Chars.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ object Chars {
3838

3939
/** Convert a character to a backslash-u escape */
4040
def char2uescape(c: Char): String = {
41-
@inline def hexChar(ch: Int): Char =
41+
@forceInline def hexChar(ch: Int): Char =
4242
(( if (ch < 10) '0' else 'A' - 10 ) + ch).toChar
4343

4444
char2uescapeArray(2) = hexChar((c >> 12) )

compiler/src/dotty/tools/dotc/util/Stats.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import collection.mutable
2020
override def default(key: String): Int = 0
2121
}
2222

23-
@inline
23+
@forceInline
2424
def record(fn: => String, n: => Int = 1) =
2525
if (enabled) doRecord(fn, n)
2626

@@ -30,7 +30,7 @@ import collection.mutable
3030
hits(name) += n
3131
}
3232

33-
@inline
33+
@forceInline
3434
def track[T](fn: String)(op: => T) =
3535
if (enabled) doTrack(fn)(op) else op
3636

@@ -42,7 +42,7 @@ import collection.mutable
4242
finally stack = stack.tail
4343
} else op
4444

45-
@inline
45+
@forceInline
4646
def trackTime[T](fn: String)(op: => T) =
4747
if (enabled) doTrackTime(fn)(op) else op
4848

compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,32 @@ class InlineBytecodeTests extends DottyBytecodeTest {
99
val source = """
1010
|class Foo {
1111
| inline def foo: Int = 1
12+
| @forceInline def bar: Int = 1
1213
|
1314
| def meth1: Unit = foo
14-
| def meth2: Unit = 1
15+
| def meth2: Unit = bar
16+
| def meth3: Unit = 1
1517
|}
1618
""".stripMargin
1719

1820
checkBCode(source) { dir =>
1921
val clsIn = dir.lookupName("Foo.class", directory = false).input
2022
val clsNode = loadClassNode(clsIn)
21-
val meth1 = getMethod(clsNode, "meth1")
22-
val meth2 = getMethod(clsNode, "meth2")
23+
val meth1 = getMethod(clsNode, "meth1")
24+
val meth2 = getMethod(clsNode, "meth2")
25+
val meth3 = getMethod(clsNode, "meth3")
2326

2427
val instructions1 = instructionsFromMethod(meth1)
2528
val instructions2 = instructionsFromMethod(meth2)
29+
val instructions3 = instructionsFromMethod(meth3)
2630

27-
assert(instructions1 == instructions2,
31+
assert(instructions1 == instructions3,
2832
"`foo` was not properly inlined in `meth1`\n" +
29-
diffInstructions(instructions1, instructions2))
33+
diffInstructions(instructions1, instructions3))
34+
35+
assert(instructions2 == instructions3,
36+
"`bar` was not properly inlined in `meth2`\n" +
37+
diffInstructions(instructions2, instructions3))
3038
}
3139
}
3240
}

compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -935,7 +935,7 @@ class ErrorMessagesTests extends ErrorMessagesTest {
935935
@Test def noReturnInInline =
936936
checkMessagesAfter(FrontEnd.name) {
937937
"""class BadFunction {
938-
| @inline def usesReturn: Int = { return 42 }
938+
| inline def usesReturn: Int = { return 42 }
939939
|}
940940
""".stripMargin
941941
}.expect { (ictx, messages) =>

docs/docs/reference/inline.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@ it in backticks, i.e.
125125

126126
@`inline` def ...
127127

128+
The Dotty compiler ignores `@inline` annotated definitions. To cross
129+
compile between both Dotty and Scalac, we introduce a new `@forceInline`
130+
annotation which is equivalent to the new `inline` modifier. Note that
131+
Scala 2 ignores the `@forceInLine` annotation, and one must use both
132+
annotations to inline across the two compilers (i.e. `@forceInline @inline`).
133+
128134
### The definition of constant expression
129135

130136
Right-hand sides of inline values and of arguments for inline parameters

library/src/dotty/DottyPredef.scala

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package dotty
22

3+
import scala.forceInline
4+
35
object DottyPredef {
46

57
/** A class for implicit values that can serve as implicit conversions
@@ -22,18 +24,18 @@ object DottyPredef {
2224
*/
2325
abstract class ImplicitConverter[-T, +U] extends Function1[T, U]
2426

25-
@inline final def assert(assertion: Boolean, message: => Any): Unit = {
27+
@forceInline final def assert(assertion: Boolean, message: => Any): Unit = {
2628
if (!assertion)
2729
assertFail(message)
2830
}
2931

30-
@inline final def assert(assertion: Boolean): Unit = {
32+
@forceInline final def assert(assertion: Boolean): Unit = {
3133
if (!assertion)
3234
assertFail()
3335
}
3436

3537
final def assertFail(): Unit = throw new java.lang.AssertionError("assertion failed")
3638
final def assertFail(message: => Any): Unit = throw new java.lang.AssertionError("assertion failed: " + message)
3739

38-
@inline final def implicitly[T](implicit ev: T): T = ev
40+
@forceInline final def implicitly[T](implicit ev: T): T = ev
3941
}

library/src/dotty/runtime/LazyVals.scala

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dotty.runtime
22

33
import scala.annotation.tailrec
4+
import scala.forceInline
45

56
/**
67
* Helper methods used in thread-safe lazy vals.
@@ -24,20 +25,20 @@ object LazyVals {
2425
final val LAZY_VAL_MASK = 3L
2526
final val debug = false
2627

27-
@inline def STATE(cur: Long, ord: Int) = {
28+
@forceInline def STATE(cur: Long, ord: Int) = {
2829
val r = (cur >> (ord * BITS_PER_LAZY_VAL)) & LAZY_VAL_MASK
2930
if (debug)
3031
println(s"STATE($cur, $ord) = $r")
3132
r
3233
}
33-
@inline def CAS(t: Object, offset: Long, e: Long, v: Int, ord: Int) = {
34+
@forceInline def CAS(t: Object, offset: Long, e: Long, v: Int, ord: Int) = {
3435
if (debug)
3536
println(s"CAS($t, $offset, $e, $v, $ord)")
3637
val mask = ~(LAZY_VAL_MASK << ord * BITS_PER_LAZY_VAL)
3738
val n = (e & mask) | (v.toLong << (ord * BITS_PER_LAZY_VAL))
3839
compareAndSet(t, offset, e, n)
3940
}
40-
@inline def setFlag(t: Object, offset: Long, v: Int, ord: Int) = {
41+
@forceInline def setFlag(t: Object, offset: Long, v: Int, ord: Int) = {
4142
if (debug)
4243
println(s"setFlag($t, $offset, $v, $ord)")
4344
var retry = true
@@ -56,7 +57,7 @@ object LazyVals {
5657
}
5758
}
5859
}
59-
@inline def wait4Notification(t: Object, offset: Long, cur: Long, ord: Int) = {
60+
@forceInline def wait4Notification(t: Object, offset: Long, cur: Long, ord: Int) = {
6061
if (debug)
6162
println(s"wait4Notification($t, $offset, $cur, $ord)")
6263
var retry = true
@@ -74,8 +75,8 @@ object LazyVals {
7475
}
7576
}
7677

77-
@inline def compareAndSet(t: Object, off: Long, e: Long, v: Long) = unsafe.compareAndSwapLong(t, off, e, v)
78-
@inline def get(t: Object, off: Long) = {
78+
@forceInline def compareAndSet(t: Object, off: Long, e: Long, v: Long) = unsafe.compareAndSwapLong(t, off, e, v)
79+
@forceInline def get(t: Object, off: Long) = {
7980
if (debug)
8081
println(s"get($t, $off)")
8182
unsafe.getLongVolatile(t, off)
@@ -87,7 +88,7 @@ object LazyVals {
8788
x => new Object()
8889
}.toArray
8990

90-
@inline def getMonitor(obj: Object, fieldId: Int = 0) = {
91+
@forceInline def getMonitor(obj: Object, fieldId: Int = 0) = {
9192
var id = (
9293
/*java.lang.System.identityHashCode(obj) + */ // should be here, but #548
9394
fieldId) % base
@@ -96,7 +97,7 @@ object LazyVals {
9697
monitors(id)
9798
}
9899

99-
@inline def getOffset(clz: Class[_], name: String) = {
100+
@forceInline def getOffset(clz: Class[_], name: String) = {
100101
val r = unsafe.objectFieldOffset(clz.getDeclaredField(name))
101102
if (debug)
102103
println(s"getOffset($clz, $name) = $r")

library/src/scala/forceInline.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package scala
2+
3+
/** An annotation on methods that is equivalent to Dotty `inline` modifier.
4+
*
5+
* The annotation should be used instead of the `inline` modifier in code
6+
* that needs to cross compile between Scala 2 and Dotty.
7+
*
8+
* Note that Scala 2 ignores the `@forceInLine` annotation, and one must use
9+
* both the `@inline` and `@forceInline` annotation to inline across the
10+
* two compilers. E.g.
11+
*
12+
* ```scala
13+
* @inline @forceInline def foo = ...
14+
* ```
15+
*/
16+
class forceInline extends scala.annotation.StaticAnnotation

library/src/scala/tasty/util/ShowSourceCode.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -949,7 +949,7 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
949949
case Annotation(annot, _) =>
950950
annot.tpe match {
951951
case Type.TypeRef(_, Type.SymRef(PackageDef("internal", _), Type.ThisType(Type.SymRef(PackageDef("annotation", _), NoPrefix())))) => false
952-
case Type.TypeRef("inline", Types.ScalaPackage()) => false
952+
case Type.TypeRef("forceInline", Types.ScalaPackage()) => false
953953
case _ => true
954954
}
955955
case x => throw new MatchError(x.show)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
object B {
2-
@inline def getInline: Int =
2+
inline def getInline: Int =
33
A.get
44
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
object B {
2-
@inline def getInline: Double =
2+
inline def getInline: Double =
33
A.get
44
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
object B {
2-
@inline def getInline: Int =
2+
inline def getInline: Int =
33
sys.error("This is an expected failure when running C")
44
}

0 commit comments

Comments
 (0)