Skip to content

Commit 8a42819

Browse files
authored
Merge pull request #8608 from dotty-staging/fix-#8564
Fix #8564: Make concrete inline methods final
2 parents 9a69ea8 + 93dc03d commit 8a42819

File tree

7 files changed

+65
-18
lines changed

7 files changed

+65
-18
lines changed

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

+3-1
Original file line numberDiff line numberDiff line change
@@ -1096,7 +1096,9 @@ object SymDenotations {
10961096

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

11011103
/** A class is effectively sealed if has the `final` or `sealed` modifier, or it
11021104
* is defined in Scala 3 and is neither abstract nor open.

compiler/src/dotty/tools/dotc/typer/RefChecks.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ object RefChecks {
157157
* 1.8.3 M is of type ()S, O is of type []T and S <: T, or
158158
* 1.9.1 If M is erased, O is erased. If O is erased, M is erased or inline.
159159
* 1.9.2 If M or O are extension methods, they must both be extension methods.
160-
* 1.10 If O is inline, M must be inline
160+
* 1.10. If O is inline (and deferred, otherwise O would be final), M must be inline
161161
* 1.11. If O is a Scala-2 macro, M must be a Scala-2 macro.
162162
* 2. Check that only abstract classes have deferred members
163163
* 3. Check that concrete classes do not have deferred definitions
@@ -398,7 +398,7 @@ object RefChecks {
398398
else if (other.isAllOf(ExtensionMethod) && !member.isAllOf(ExtensionMethod)) // (1.9.2)
399399
overrideError("is a normal method, cannot override an extension method")
400400
else if other.isInlineMethod && !member.isInlineMethod then // (1.10)
401-
overrideError("is not inline, cannot override an inline method")
401+
overrideError("is not inline, cannot implement an inline method")
402402
else if (other.isScala2Macro && !member.isScala2Macro) // (1.11)
403403
overrideError("cannot be used here - only Scala-2 macros can override Scala-2 macros")
404404
else if (!compatibleTypes(memberTp(self), otherTp(self)) &&

docs/docs/reference/metaprogramming/inline.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ funkyAssertEquals(computeActual(), computeExpected(), computeDelta())
144144
```
145145
### Rules for Overriding
146146

147-
Inline methods can override other methods and can themselves be overridden by other inline methods. The rules are as follows:
147+
Inline methods can override other non-inline methods. The rules are as follows:
148148

149149
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:
150150
```scala
@@ -167,7 +167,7 @@ assert(a.g() == 33)
167167
```
168168
The inlined invocations and the dynamically dispatched invocations give the same results.
169169

170-
2. Inline methods can override or implement normal methods, as the previous example shows. Inline methods can be overridden only by other inline methods.
170+
2. Inline methods are effectively final.
171171

172172
3. Inline methods can also be abstract. An abstract inline method can be implemented only by other inline methods. It cannot be invoked directly:
173173
```scala

tests/neg/inline-override.scala

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
object Test {
2+
3+
abstract class A {
4+
def f1(): Int = 1
5+
def f2(): Int
6+
inline def f3(): Int
7+
inline def f4(): Int = 1
8+
}
9+
10+
class B extends A {
11+
override def f1(): Int = 1 // OK
12+
override def f2(): Int = 1 // OK
13+
override def f3(): Int = 1 // error
14+
override def f4(): Int = 1 // error
15+
}
16+
17+
class C extends A {
18+
inline override def f1(): Int = 1 // OK
19+
inline override def f2(): Int = 1 // OK retained
20+
inline override def f3(): Int = 1 // OK not retianed
21+
inline override def f4(): Int = 1 // error
22+
}
23+
24+
abstract class D extends A {
25+
override def f1(): Int // OK
26+
override def f2(): Int // OK
27+
override def f3(): Int // error
28+
override def f4(): Int // OK not retained
29+
}
30+
31+
abstract class E extends A { // error f1
32+
inline override def f1(): Int
33+
inline override def f2(): Int
34+
inline override def f3(): Int
35+
inline override def f4(): Int // OK not retained
36+
}
37+
38+
}

tests/neg/quote-MacroOverride.scala

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
object Test {
2+
3+
abstract class A {
4+
inline def f1(): String = "A.f1"
5+
inline def f2(): String = "A.f2"
6+
def f3(): String = "A.f3"
7+
}
8+
9+
object B extends A {
10+
override inline def f1(): String = "B.f1" // error
11+
override inline def f2(): String = "B.f2" // error
12+
override inline def f3(): String = "B.f3"
13+
}
14+
15+
}

tests/run/quote-MacroOverride.scala

+4-12
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,19 @@
11
object Test {
22

33
abstract class A {
4-
inline def f1(): String = "A.f1"
5-
inline def f2(): String = "A.f2"
6-
def f3(): String = "A.f3"
4+
def f(): String = "A.f"
75
}
86

97
object B extends A {
10-
override inline def f1(): String = "B.f1"
11-
override inline def f2(): String = "B.f2"
12-
override inline def f3(): String = "B.f3"
8+
override inline def f(): String = "B.f"
139
}
1410

1511
def main(args: Array[String]): Unit = {
1612
val a: A = B
17-
assert(a.f1() == "A.f1")
18-
assert(a.f2() == "A.f2")
19-
assert(a.f3() == "B.f3")
13+
assert(a.f() == "B.f")
2014

2115
val b: B.type = B
22-
assert(b.f1() == "B.f1")
23-
assert(b.f2() == "B.f2")
24-
assert(b.f3() == "B.f3")
16+
assert(b.f() == "B.f")
2517
}
2618

2719
}

0 commit comments

Comments
 (0)