diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 0775b3caaf0c..f044c4d69796 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -624,4 +624,6 @@ object Flags { val SyntheticParam: FlagSet = Synthetic | Param val SyntheticTermParam: FlagSet = Synthetic | TermParam val SyntheticTypeParam: FlagSet = Synthetic | TypeParam + + val NonAllowedLocalModifier: FlagSet = Private | Abstract //| Final | Sealed | Implicit | Lazy } diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index cfb132dd95ce..232888b8e6bc 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -4709,7 +4709,7 @@ object Parsers { } def localDef(start: Int, implicitMods: Modifiers = EmptyModifiers): Tree = { - var mods = defAnnotsMods(localModifierTokens) + var mods = defAnnotsMods(modifierTokens) for (imod <- implicitMods.mods) mods = addMod(mods, imod) if (mods.is(Final)) // A final modifier means the local definition is "class-like". // FIXME: Deal with modifiers separately @@ -4726,7 +4726,7 @@ object Parsers { /** BlockStatSeq ::= { BlockStat semi } [Expr] * BlockStat ::= Import - * | Annotations [implicit] [lazy] Def + * | Annotations [implicit] [lazy] ValOrDef * | Annotations LocalModifiers TmplDef * | Extension * | Expr1 @@ -4744,7 +4744,7 @@ object Parsers { stats += closure(in.offset, Location.InBlock, modifiers(BitSet(IMPLICIT))) else if isIdent(nme.extension) && followingIsExtension() then stats += extension() - else if isDefIntro(localModifierTokens, + else if isDefIntro(modifierTokens, excludedSoftModifiers = // Allow opaque definitions at outermost level in REPL. if outermost && ctx.mode.is(Mode.Interactive) diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index ec07fefc64ab..b08afceea3fc 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -591,6 +591,10 @@ object Checking { checkWithDeferred(Private) checkWithDeferred(Final) } + + if !sym.owner.isClass && sym.is(Method) && sym.isOneOf(NonAllowedLocalModifier) then + report.error("not allowed", sym.srcPos) + if (sym.isValueClass && sym.is(Trait) && !sym.isRefinementClass) fail(CannotExtendAnyVal(sym)) if (sym.isConstructor && !sym.isPrimaryConstructor && sym.owner.is(Trait, butNot = JavaDefined)) diff --git a/tests/neg/i22631.check b/tests/neg/i22631.check new file mode 100644 index 000000000000..3f61fd3a51b3 --- /dev/null +++ b/tests/neg/i22631.check @@ -0,0 +1,16 @@ +-- Error: tests/neg/i22631.scala:3:16 ---------------------------------------------------------------------------------- +3 | private def bar: Int = 0 // error + | ^ + | not allowed +-- Error: tests/neg/i22631.scala:8:16 ---------------------------------------------------------------------------------- +8 | private def bar: Int = 0 // error + | ^ + | not allowed +-- Error: tests/neg/i22631.scala:13:16 --------------------------------------------------------------------------------- +13 | private def bar: Int = 0 // error + | ^ + | not allowed +-- Error: tests/neg/i22631.scala:18:16 --------------------------------------------------------------------------------- +18 | private def bar: Int = 0 // error + | ^ + | not allowed diff --git a/tests/neg/i22631.scala b/tests/neg/i22631.scala new file mode 100644 index 000000000000..c9918e28c86e --- /dev/null +++ b/tests/neg/i22631.scala @@ -0,0 +1,20 @@ +object Foo: + def foo1: Int = { + private def bar: Int = 0 // error + bar + } + + def foo2: Int = { + private def bar: Int = 0 // error + bar + } + + def foo3: Int = { + private def bar: Int = 0 // error + bar + } + + def foo4: Int = { + private def bar: Int = 0 // error + bar + }