Skip to content

Commit 66f9d8d

Browse files
committed
Fix 4815: Illegal modifier on local def
During typechecking, disallow the use of "final" in local "defs". e.g. def foo = { final def bar = 42 // error: final in local def is disallowed }
1 parent 36c2e34 commit 66f9d8d

File tree

4 files changed

+39
-0
lines changed

4 files changed

+39
-0
lines changed

compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ public enum ErrorMessageID {
131131
MatchCaseOnlyNullWarningID,
132132
ImportRenamedTwiceID,
133133
TypeTestAlwaysSucceedsID,
134+
LocalDefMayNotBeFinalID,
134135
;
135136

136137
public int errorNumber() {

compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1560,6 +1560,15 @@ object messages {
15601560
"A trait can never be final since it is abstract and must be extended to be useful."
15611561
}
15621562

1563+
case class LocalDefMayNotBeFinal(sym: Symbol)(
1564+
implicit ctx: Context)
1565+
extends Message(LocalDefMayNotBeFinalID) {
1566+
val msg = hl"""$sym may not be ${"final"}."""
1567+
val kind = "Syntax"
1568+
val explanation =
1569+
hl"Only ${"def"}s that appear as fields may be marked as ${"final"}, but $sym is a local definition."
1570+
}
1571+
15631572
case class NativeMembersMayNotHaveImplementation(sym: Symbol)(
15641573
implicit ctx: Context)
15651574
extends Message(NativeMembersMayNotHaveImplementationID) {

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,8 @@ object Checking {
369369
if (!ok && !sym.is(Synthetic))
370370
fail(i"modifier `$flag` is not allowed for this definition")
371371

372+
def isClassLike(sym: Symbol) = sym.isClass || sym.is(Module) || sym.isAnonymousClass
373+
372374
if (sym.is(Transparent) &&
373375
( sym.is(ParamAccessor) && sym.owner.isClass
374376
|| sym.is(TermParam) && !sym.owner.isTransparentMethod
@@ -387,6 +389,9 @@ object Checking {
387389
fail(AbstractOverrideOnlyInTraits(sym))
388390
if (sym.is(Trait) && sym.is(Final))
389391
fail(TraitsMayNotBeFinal(sym))
392+
if (sym.is(Method) && sym.is(Final) && !isClassLike(sym.owner)) {
393+
fail(LocalDefMayNotBeFinal(sym))
394+
}
390395
// Skip ModuleVal since the annotation will also be on the ModuleClass
391396
if (sym.hasAnnotation(defn.TailrecAnnot) && !sym.is(Method | ModuleVal))
392397
fail(TailrecNotApplicable(sym))

tests/neg/t4815.scala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
class Test {
2+
def foo = {
3+
final def bar = 1 // error: local def may not be final
4+
final val v = 42 // ok: this is a constant expression
5+
}
6+
7+
{
8+
final def foo(x: Int) = x // error: local def may not be final
9+
}
10+
11+
final def foo2(x: Int) = x // ok: final allowed in class field
12+
13+
object Foo {
14+
final def foo(x: Int) = x // ok, but redundant
15+
}
16+
17+
abstract class Bar {
18+
def foo: Int
19+
}
20+
21+
val x = new Bar {
22+
override final def foo = 42 // ok: def is a field
23+
}
24+
}

0 commit comments

Comments
 (0)