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 2 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
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
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 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 // error
inline override def f3(): Int // error
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")
}

}