Skip to content

Commit

Permalink
Fix #8564: Make concrete inline methods final
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolasstucki committed Mar 25, 2020
1 parent 68e2023 commit c484461
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 16 deletions.
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Expand Up @@ -251,6 +251,8 @@ object desugar {
cpy.DefDef(meth)(name = methName, tparams = tparams1), epbuf.toList)

if (meth1.mods.is(Inline))
if !meth1.rhs.isEmpty then
meth1 = meth1.withMods(meth1.mods | Final)
meth1.tpt match {
case TypeBoundsTree(_, tpt1, _) =>
meth1 = cpy.DefDef(meth1)(tpt = tpt1)
Expand Down
7 changes: 5 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/RefChecks.scala
Expand Up @@ -157,7 +157,8 @@ object RefChecks {
* 1.8.3 M is of type ()S, O is of type []T and S <: T, or
* 1.9.1 If M is erased, O is erased. If O is erased, M is erased or inline.
* 1.9.2 If M or O are extension methods, they must both be extension methods.
* 1.10 If O is inline, M must be inline
* 1.10.1. If O is inline, M must be inline
* 1.10.2. If M is inline, M must not be deferred
* 1.11. If O is a Scala-2 macro, M must be a Scala-2 macro.
* 2. Check that only abstract classes have deferred members
* 3. Check that concrete classes do not have deferred definitions
Expand Down Expand Up @@ -397,8 +398,10 @@ object RefChecks {
overrideError("is an extension method, cannot override a normal method")
else if (other.isAllOf(ExtensionMethod) && !member.isAllOf(ExtensionMethod)) // (1.9.2)
overrideError("is a normal method, cannot override an extension method")
else if other.isInlineMethod && !member.isInlineMethod then // (1.10)
else if other.isInlineMethod && !member.isInlineMethod then // (1.10.1)
overrideError("is not inline, cannot override an inline method")
else if member.isInlineMethod && member.is(Deferred) then // (1.10.2)
overrideError("is inline method, cannot be an abstract override")
else if (other.isScala2Macro && !member.isScala2Macro) // (1.11)
overrideError("cannot be used here - only Scala-2 macros can override Scala-2 macros")
else if (!compatibleTypes(memberTp(self), otherTp(self)) &&
Expand Down
4 changes: 2 additions & 2 deletions docs/docs/reference/metaprogramming/inline.md
Expand Up @@ -144,7 +144,7 @@ funkyAssertEquals(computeActual(), computeExpected(), computeDelta())
```
### Rules for Overriding

Inline methods can override other methods and can themselves be overridden by other inline methods. The rules are as follows:
Inline methods can override other non-inline methods. The rules are as follows:

1. If an inline method `f` implements or overrides another, non-inline method, the inline method can also be invoked at runtime. For instance, consider the scenario:
```scala
Expand All @@ -167,7 +167,7 @@ assert(a.g() == 33)
```
The inlined invocations and the dynamically dispatched invocations give the same results.

2. Inline methods can override or implement normal methods, as the previous example shows. Inline methods can be overridden only by other inline methods.
2. Inline methods are final.

3. Inline methods can also be abstract. An abstract inline method can be implemented only by other inline methods. It cannot be invoked directly:
```scala
Expand Down
38 changes: 38 additions & 0 deletions tests/neg/inline-override.scala
@@ -0,0 +1,38 @@
object Test {

abstract class A {
def f1(): Int = 1
def f2(): Int
inline def f3(): Int
inline def f4(): Int = 1
}

class B extends A {
override def f1(): Int = 1 // OK
override def f2(): Int = 1 // OK
override def f3(): Int = 1 // error
override def f4(): Int = 1 // error
}

class C extends A {
inline override def f1(): Int = 1 // OK
inline override def f2(): Int = 1 // OK retained
inline override def f3(): Int = 1 // OK not retianed
inline override def f4(): Int = 1 // error
}

abstract class D extends A {
override def f1(): Int // OK
override def f2(): Int // OK
override def f3(): Int // error
override def f4(): Int // FIXME (1.10.2): error
}

abstract class E extends A { // error f1
inline override def f1(): Int
inline override def f2(): Int // error
inline override def f3(): Int // error
inline override def f4(): Int // FIXME (1.10.1): error
}

}
15 changes: 15 additions & 0 deletions tests/neg/quote-MacroOverride.scala
@@ -0,0 +1,15 @@
object Test {

abstract class A {
inline def f1(): String = "A.f1"
inline def f2(): String = "A.f2"
def f3(): String = "A.f3"
}

object B extends A {
override inline def f1(): String = "B.f1" // error
override inline def f2(): String = "B.f2" // error
override inline def f3(): String = "B.f3"
}

}
16 changes: 4 additions & 12 deletions tests/run/quote-MacroOverride.scala
@@ -1,27 +1,19 @@
object Test {

abstract class A {
inline def f1(): String = "A.f1"
inline def f2(): String = "A.f2"
def f3(): String = "A.f3"
def f(): String = "A.f"
}

object B extends A {
override inline def f1(): String = "B.f1"
override inline def f2(): String = "B.f2"
override inline def f3(): String = "B.f3"
override inline def f(): String = "B.f"
}

def main(args: Array[String]): Unit = {
val a: A = B
assert(a.f1() == "A.f1")
assert(a.f2() == "A.f2")
assert(a.f3() == "B.f3")
assert(a.f() == "B.f")

val b: B.type = B
assert(b.f1() == "B.f1")
assert(b.f2() == "B.f2")
assert(b.f3() == "B.f3")
assert(b.f() == "B.f")
}

}

0 comments on commit c484461

Please sign in to comment.