diff --git a/community-build/community-projects/scalatest b/community-build/community-projects/scalatest index 3e84922cd152..4c41a8bb3016 160000 --- a/community-build/community-projects/scalatest +++ b/community-build/community-projects/scalatest @@ -1 +1 @@ -Subproject commit 3e84922cd152b361e01f3fad56cdbe6e8445bd0b +Subproject commit 4c41a8bb3016d222150edf27e784e76750741980 diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index a6828bc88c6e..d138e40f5574 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -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. diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index c5a476df1328..365b0aabd7c5 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -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 @@ -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)) && diff --git a/docs/docs/reference/metaprogramming/inline.md b/docs/docs/reference/metaprogramming/inline.md index b47d21bbe0cb..7b5bde245b5c 100644 --- a/docs/docs/reference/metaprogramming/inline.md +++ b/docs/docs/reference/metaprogramming/inline.md @@ -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 @@ -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 diff --git a/tests/neg/inline-override.scala b/tests/neg/inline-override.scala new file mode 100644 index 000000000000..8a27627bfe58 --- /dev/null +++ b/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 // 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 + } + +} diff --git a/tests/neg/quote-MacroOverride.scala b/tests/neg/quote-MacroOverride.scala new file mode 100644 index 000000000000..7760048d4c54 --- /dev/null +++ b/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" + } + +} diff --git a/tests/run/quote-MacroOverride.scala b/tests/run/quote-MacroOverride.scala index 70449a91343a..831fd9d74ac8 100644 --- a/tests/run/quote-MacroOverride.scala +++ b/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") } }