Skip to content

Fix #8564: Make concrete inline methods final #8608

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1097,7 +1097,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
Original file line number Diff line number Diff line change
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
4 changes: 2 additions & 2 deletions docs/docs/reference/metaprogramming/inline.md
Original file line number Diff line number Diff line change
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 effectively 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
Original file line number Diff line number Diff line change
@@ -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 // OK not retained
}

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

}
15 changes: 15 additions & 0 deletions tests/neg/quote-MacroOverride.scala
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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")
}

}