Skip to content

Commit

Permalink
Make concrete inline methods effectively final
Browse files Browse the repository at this point in the history
  • Loading branch information
odersky committed Mar 17, 2020
1 parent e6f5df6 commit 051039e
Show file tree
Hide file tree
Showing 5 changed files with 14 additions and 10 deletions.
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Expand Up @@ -1094,7 +1094,9 @@ object SymDenotations {

/** A symbol is effectively final if it cannot be overridden in a subclass */
final def isEffectivelyFinal(implicit ctx: Context): Boolean =
isOneOf(EffectivelyFinalFlags) || !owner.isExtensibleClass
isOneOf(EffectivelyFinalFlags)
|| is(Inline, butNot = Deferred)
|| !owner.isExtensibleClass

/** A class is effectively sealed if has the `final` or `sealed` modifier, or it
* is defined in Scala 3 and is neither abstract nor open.
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/RefChecks.scala
Expand Up @@ -157,7 +157,7 @@ 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 If O is inline (and deferred, otherwise O would be final), M must be inline
* 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 @@ -398,7 +398,7 @@ object RefChecks {
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)
overrideError("is not inline, cannot override an inline method")
overrideError("is not inline, cannot implement an inline method")
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
5 changes: 3 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 methods. Abstract inline methods can themselves be implemented by other 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,8 @@ 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 can override or implement normal methods, as the previous example shows. Concrete inline methods are effectively final; they cannot be
overriden.

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
4 changes: 3 additions & 1 deletion tests/neg-macros/quote-MacroOverride.scala
Expand Up @@ -3,11 +3,13 @@ object Test {
abstract class A {
def f(): Unit
inline def g(): Unit = ()
inline def h(): Unit
}

object B extends A {
override inline def f() = ()
override def g() = () // error: is not inline, cannot override an inline method
override def g() = () // error: cannot override final member
def h() = () // error: is not inline, cannot implement an inline method
}

}
7 changes: 3 additions & 4 deletions tests/run/quote-MacroOverride.scala
@@ -1,8 +1,8 @@
object Test {

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

Expand All @@ -14,8 +14,7 @@ object Test {

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

val b: B.type = B
Expand Down

0 comments on commit 051039e

Please sign in to comment.