From 4eee851a667c9b3e10b381a10e21b26b3df51421 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 24 Mar 2020 15:55:47 +0100 Subject: [PATCH 1/4] Remove uses of inline overriding inline --- community-build/community-projects/scalatest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 483567c75b82e1fff1ebc6ee6acecc20e95f1ef6 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 25 Mar 2020 15:21:00 +0100 Subject: [PATCH 2/4] Fix #8564: Make concrete inline methods final --- .../src/dotty/tools/dotc/ast/Desugar.scala | 2 + .../dotty/tools/dotc/typer/RefChecks.scala | 7 +++- docs/docs/reference/metaprogramming/inline.md | 4 +- tests/neg/inline-override.scala | 38 +++++++++++++++++++ tests/neg/quote-MacroOverride.scala | 15 ++++++++ tests/run/quote-MacroOverride.scala | 16 ++------ 6 files changed, 66 insertions(+), 16 deletions(-) create mode 100644 tests/neg/inline-override.scala create mode 100644 tests/neg/quote-MacroOverride.scala diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 91fe8f06daad..6cf7e5a70d50 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -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) diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index c5a476df1328..610132d879bd 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -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 @@ -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)) && diff --git a/docs/docs/reference/metaprogramming/inline.md b/docs/docs/reference/metaprogramming/inline.md index b47d21bbe0cb..15589c64e005 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 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..f7c8fba92405 --- /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 // error + inline override def f3(): Int // error + 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") } } From d9b9f011d677b4d0b56bf7837d0ec15bb30658e4 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 30 Mar 2020 11:44:49 +0200 Subject: [PATCH 3/4] Use effectivelyFinal for inline methods --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 2 -- compiler/src/dotty/tools/dotc/core/SymDenotations.scala | 4 +++- compiler/src/dotty/tools/dotc/typer/RefChecks.scala | 4 ++-- docs/docs/reference/metaprogramming/inline.md | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 6cf7e5a70d50..91fe8f06daad 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -251,8 +251,6 @@ 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) 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 610132d879bd..9cde98d08545 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.1. If O is inline, M must be inline + * 1.10.1. If O is inline (and deferred, otherwise O would be final), 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 @@ -399,7 +399,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.1) - overrideError("is not inline, cannot override an inline method") + overrideError("is not inline, cannot implement 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) diff --git a/docs/docs/reference/metaprogramming/inline.md b/docs/docs/reference/metaprogramming/inline.md index 15589c64e005..7b5bde245b5c 100644 --- a/docs/docs/reference/metaprogramming/inline.md +++ b/docs/docs/reference/metaprogramming/inline.md @@ -167,7 +167,7 @@ assert(a.g() == 33) ``` The inlined invocations and the dynamically dispatched invocations give the same results. -2. Inline methods are final. +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 From 93dc03dc58c3e97873f17ea175d5236fe20034fd Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 30 Mar 2020 11:56:13 +0200 Subject: [PATCH 4/4] Allow abstract inline overrites --- compiler/src/dotty/tools/dotc/typer/RefChecks.scala | 7 ++----- tests/neg/inline-override.scala | 4 ++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 9cde98d08545..365b0aabd7c5 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -157,8 +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.1. If O is inline (and deferred, otherwise O would be final), M must be inline - * 1.10.2. If M is inline, M must not be deferred + * 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,10 +397,8 @@ 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.1) + else if other.isInlineMethod && !member.isInlineMethod then // (1.10) overrideError("is not inline, cannot implement 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)) && diff --git a/tests/neg/inline-override.scala b/tests/neg/inline-override.scala index f7c8fba92405..8a27627bfe58 100644 --- a/tests/neg/inline-override.scala +++ b/tests/neg/inline-override.scala @@ -30,8 +30,8 @@ object Test { 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 f2(): Int + inline override def f3(): Int inline override def f4(): Int // OK not retained }