diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index efbe5878828c..27362bff418f 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -7,7 +7,7 @@ import util.Spans._, Types._, Contexts._, Constants._, Names._, NameOps._, Flags import Symbols._, StdNames._, Trees._, Phases._, ContextOps._ import Decorators._, transform.SymUtils._ import NameKinds.{UniqueName, EvidenceParamName, DefaultGetterName} -import typer.{FrontEnd, Namer} +import typer.{FrontEnd, Namer, Checking} import util.{Property, SourceFile, SourcePosition} import config.Feature.{sourceVersion, migrateTo3, enabled} import config.SourceVersion._ @@ -859,16 +859,7 @@ object desugar { val mods = mdef.mods val moduleName = normalizeName(mdef, impl).asTermName def isEnumCase = mods.isEnumCase - - def flagSourcePos(flag: FlagSet) = mods.mods.find(_.flags == flag).fold(mdef.sourcePos)(_.sourcePos) - - if (mods.is(Abstract)) - report.error(AbstractCannotBeUsedForObjects(mdef), flagSourcePos(Abstract)) - if (mods.is(Sealed)) - report.error(ModifierRedundantForObjects(mdef, "sealed"), flagSourcePos(Sealed)) - // Maybe this should be an error; see https://github.com/scala/bug/issues/11094. - if (mods.is(Final) && !mods.is(Synthetic)) - report.warning(ModifierRedundantForObjects(mdef, "final"), flagSourcePos(Final)) + Checking.checkWellFormedModule(mdef) if (mods.is(Package)) packageModuleDef(mdef) diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index 518be160763e..1cc0f2afc1a7 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -154,8 +154,8 @@ enum ErrorMessageID extends java.lang.Enum[ErrorMessageID] { ErasedTypesCanOnlyBeFunctionTypesID, CaseClassMissingNonImplicitParamListID, EnumerationsShouldNotBeEmptyID, - AbstractCannotBeUsedForObjectsID, - ModifierRedundantForObjectsID, + UNUSED_1, + RedundantModifierID, TypedCaseDoesNotExplicitlyExtendTypedEnumID, IllegalRedefinitionOfStandardKindID, NoExtensionMethodAllowedID, diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index eb3e2b22781c..3ef9d8d076a2 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2364,32 +2364,6 @@ import transform.SymUtils._ |""".stripMargin } - class AbstractCannotBeUsedForObjects(mdef: untpd.ModuleDef)(using Context) - extends SyntaxMsg(AbstractCannotBeUsedForObjectsID) { - def msg = em"${hl("abstract")} modifier cannot be used for objects" - - def explain = - em"""|Objects are final and cannot be extended, thus cannot have the ${hl("abstract")} modifier - | - |You may want to define an abstract class: - | ${hl("abstract")} ${hl("class")} Abstract${mdef.name} { } - | - |And extend it in an object: - | ${hl("object")} ${mdef.name} ${hl("extends")} Abstract${mdef.name} { } - |""".stripMargin - } - - class ModifierRedundantForObjects(mdef: untpd.ModuleDef, modifier: String)(using Context) - extends SyntaxMsg(ModifierRedundantForObjectsID) { - def msg = em"${hl(modifier)} modifier is redundant for objects" - - def explain = - em"""|Objects cannot be extended making the ${hl(modifier)} modifier redundant. - |You may want to define the object without it: - | ${hl("object")} ${mdef.name} { } - |""".stripMargin - } - class TypedCaseDoesNotExplicitlyExtendTypedEnum(enumDef: Symbol, caseDef: untpd.TypeDef)(using Context) extends SyntaxMsg(TypedCaseDoesNotExplicitlyExtendTypedEnumID) { def msg = i"explicit extends clause needed because both enum case and enum class have type parameters" @@ -2481,9 +2455,15 @@ import transform.SymUtils._ |""".stripMargin } - class ModifierNotAllowedForDefinition(flag: FlagSet)(using Context) + class ModifierNotAllowedForDefinition(flag: Flag)(using Context) extends SyntaxMsg(ModifierNotAllowedForDefinitionID) { - def msg = s"Modifier `${flag.flagsString}` is not allowed for this definition" + def msg = em"Modifier ${hl(flag.flagsString)} is not allowed for this definition" + def explain = "" + } + + class RedundantModifier(flag: Flag)(using Context) + extends SyntaxMsg(RedundantModifierID) { + def msg = em"Modifier ${hl(flag.flagsString)} is redundant for this definition" def explain = "" } diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index cd0effedafcd..9f48913d1f67 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -433,9 +433,10 @@ object Checking { if (sym.isAllOf(flag1 | flag2)) fail(msg) def checkCombination(flag1: FlagSet, flag2: FlagSet) = if sym.isAllOf(flag1 | flag2) then fail(i"illegal combination of modifiers: `${flag1.flagsString}` and `${flag2.flagsString}` for: $sym") - def checkApplicable(flag: FlagSet, ok: Boolean) = - if (!ok && !sym.is(Synthetic)) + def checkApplicable(flag: Flag, ok: Boolean) = + if sym.is(flag, butNot = Synthetic) && !ok then fail(ModifierNotAllowedForDefinition(flag)) + sym.resetFlag(flag) if (sym.is(Inline) && ( sym.is(ParamAccessor) && sym.owner.isClass @@ -485,6 +486,15 @@ object Checking { if (sym.isConstructor && !sym.isPrimaryConstructor && sym.owner.is(Trait, butNot = JavaDefined)) val addendum = if ctx.settings.Ydebug.value then s" ${sym.owner.flagsString}" else "" fail("Traits cannot have secondary constructors" + addendum) + checkApplicable(Inline, sym.isTerm && !sym.isOneOf(Mutable | Module)) + checkApplicable(Lazy, !sym.isOneOf(Method | Mutable)) + if (sym.isType && !sym.is(Deferred)) + for (cls <- sym.allOverriddenSymbols.filter(_.isClass)) { + fail(CannotHaveSameNameAs(sym, cls, CannotHaveSameNameAs.CannotBeOverridden)) + sym.setFlag(Private) // break the overriding relationship by making sym Private + } + checkApplicable(Erased, + !sym.isOneOf(MutableOrLazy, butNot = Given) && !sym.isType || sym.isClass) checkCombination(Final, Open) checkCombination(Sealed, Open) checkCombination(Final, Sealed) @@ -493,18 +503,22 @@ object Checking { checkCombination(Private, Override) checkCombination(Lazy, Inline) checkNoConflict(Lazy, ParamAccessor, s"parameter may not be `lazy`") - if (sym.is(Inline)) checkApplicable(Inline, sym.isTerm && !sym.isOneOf(Mutable | Module)) - if (sym.is(Lazy)) checkApplicable(Lazy, !sym.isOneOf(Method | Mutable)) - if (sym.isType && !sym.is(Deferred)) - for (cls <- sym.allOverriddenSymbols.filter(_.isClass)) { - fail(CannotHaveSameNameAs(sym, cls, CannotHaveSameNameAs.CannotBeOverridden)) - sym.setFlag(Private) // break the overriding relationship by making sym Private - } - if (sym.is(Erased)) - checkApplicable(Erased, - !sym.isOneOf(MutableOrLazy, butNot = Given) && !sym.isType || sym.isClass) } + /** Check for illegal or redundant modifiers on modules. This is done separately + * from checkWellformed, since the original module modifiers don't surivive desugaring + */ + def checkWellFormedModule(mdef: untpd.ModuleDef)(using Context) = + val mods = mdef.mods + def flagSourcePos(flag: FlagSet) = + mods.mods.find(_.flags == flag).getOrElse(mdef).srcPos + if mods.is(Abstract) then + report.error(ModifierNotAllowedForDefinition(Abstract), flagSourcePos(Abstract)) + if mods.is(Sealed) then + report.error(ModifierNotAllowedForDefinition(Sealed), flagSourcePos(Sealed)) + if mods.is(Final, butNot = Synthetic) then + report.warning(RedundantModifier(Final), flagSourcePos(Final)) + /** Check the type signature of the symbol `M` defined by `tree` does not refer * to a private type or value which is invisible at a point where `M` is still * visible. diff --git a/tests/neg/AbstractObject.check b/tests/neg/AbstractObject.check deleted file mode 100644 index 4f3200ba2439..000000000000 --- a/tests/neg/AbstractObject.check +++ /dev/null @@ -1,6 +0,0 @@ --- [E146] Syntax Error: tests/neg/AbstractObject.scala:1:0 ------------------------------------------------------------- -1 |abstract object A {} // error - |^^^^^^^^ - |abstract modifier cannot be used for objects - -longer explanation available when compiling with `-explain` diff --git a/tests/neg/AbstractObject.scala b/tests/neg/AbstractObject.scala deleted file mode 100644 index 0abd82d2a7b5..000000000000 --- a/tests/neg/AbstractObject.scala +++ /dev/null @@ -1 +0,0 @@ -abstract object A {} // error diff --git a/tests/neg/SealedObject.check b/tests/neg/SealedObject.check deleted file mode 100644 index d63079f07a24..000000000000 --- a/tests/neg/SealedObject.check +++ /dev/null @@ -1,6 +0,0 @@ --- [E147] Syntax Error: tests/neg/SealedObject.scala:1:0 --------------------------------------------------------------- -1 |sealed object A {} // error - |^^^^^^ - |sealed modifier is redundant for objects - -longer explanation available when compiling with `-explain` diff --git a/tests/neg/SealedObject.scala b/tests/neg/SealedObject.scala deleted file mode 100644 index ecee2ea4857a..000000000000 --- a/tests/neg/SealedObject.scala +++ /dev/null @@ -1 +0,0 @@ -sealed object A {} // error \ No newline at end of file diff --git a/tests/neg/modifier-not-allowed.check b/tests/neg/modifier-not-allowed.check index f80ffb85495b..75d0dc8b616e 100644 --- a/tests/neg/modifier-not-allowed.check +++ b/tests/neg/modifier-not-allowed.check @@ -1,4 +1,12 @@ -- [E156] Syntax Error: tests/neg/modifier-not-allowed.scala:2:13 ------------------------------------------------------ 2 | opaque def o: Int = 3 // error | ^^^^^^^^^^^^^^^^^^^^^ - | Modifier `opaque` is not allowed for this definition + | Modifier opaque is not allowed for this definition +-- [E156] Syntax Error: tests/neg/modifier-not-allowed.scala:3:2 ------------------------------------------------------- +3 | abstract object A {} // error + | ^^^^^^^^ + | Modifier abstract is not allowed for this definition +-- [E156] Syntax Error: tests/neg/modifier-not-allowed.scala:4:2 ------------------------------------------------------- +4 | sealed object A {} // error + | ^^^^^^ + | Modifier sealed is not allowed for this definition diff --git a/tests/neg/modifier-not-allowed.scala b/tests/neg/modifier-not-allowed.scala index 2838dbb8f3eb..a8d4522eac60 100644 --- a/tests/neg/modifier-not-allowed.scala +++ b/tests/neg/modifier-not-allowed.scala @@ -1,3 +1,5 @@ object Test { opaque def o: Int = 3 // error + abstract object A {} // error + sealed object A {} // error } \ No newline at end of file