diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 81118831c8fa..d830505f8419 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1073,10 +1073,7 @@ object Parsers { /** Accept identifier and return Ident with its name as a term name. */ def termIdent(): Ident = - val t = makeIdent(in.token, in.offset, ident()) - if t.name == nme.ROOTPKG then - syntaxError(em"Illegal use of root package name.") - t + makeIdent(in.token, in.offset, ident()) /** Accept identifier and return Ident with its name as a type name. */ def typeIdent(): Ident = diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 83319bd90489..c17c5f25ab5f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -968,6 +968,11 @@ trait Checking { em"Implementation restriction: ${path.tpe.classSymbol} is not a valid prefix for a wildcard export, as it is a package", path.srcPos) + /** Check that the definition name isn't root. */ + def checkNonRootName(name: Name, nameSpan: Span)(using Context): Unit = + if name == nme.ROOTPKG then + report.error(em"Illegal use of root package name.", ctx.source.atSpan(nameSpan)) + /** Check that module `sym` does not clash with a class of the same name * that is concurrently compiled in another source file. */ diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 3aaf4fec59d6..0adb6d5a94b4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2472,6 +2472,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer def typedValDef(vdef: untpd.ValDef, sym: Symbol)(using Context): Tree = { val ValDef(name, tpt, _) = vdef + checkNonRootName(vdef.name, vdef.nameSpan) completeAnnotations(vdef, sym) if (sym.isOneOf(GivenOrImplicit)) checkImplicitConversionDefOK(sym) if sym.is(Module) then checkNoModuleClash(sym) @@ -2505,6 +2506,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // hence we special case it until `erased` is no longer experimental. sym.setFlag(Erased) val DefDef(name, paramss, tpt, _) = ddef + checkNonRootName(ddef.name, ddef.nameSpan) completeAnnotations(ddef, sym) val paramss1 = paramss.nestedMapConserve(typed(_)).asInstanceOf[List[ParamClause]] for case ValDefs(vparams) <- paramss1 do @@ -2853,6 +2855,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val pkg = pid1.symbol pid1 match case pid1: RefTree if pkg.is(Package) => + if ctx.owner != defn.RootClass // valid top-level "package _root_" + && ctx.owner != defn.EmptyPackageClass // valid "package _root_" after parser's "package " wrapper + then + checkNonRootName(pid1.name, pid1.span) inContext(ctx.packageContext(tree, pkg)) { // If it exists, complete the class containing the top-level definitions // before typing any statement in the package to avoid cycles as in i13669.scala diff --git a/tests/neg/i18020.scala b/tests/neg/i18020.scala index b924c136d863..2714574af5fc 100644 --- a/tests/neg/i18020.scala +++ b/tests/neg/i18020.scala @@ -47,12 +47,12 @@ package p { } // scala/bug#12508 -package _root_ { // error +package _root_ { // ok class C { val _root_ = 42 // error } } -package _root_.p { // error +package _root_.p { // ok class C } diff --git a/tests/pos/i18275.scala b/tests/pos/i18275.scala new file mode 100644 index 000000000000..2890cd88e49a --- /dev/null +++ b/tests/pos/i18275.scala @@ -0,0 +1,8 @@ +package foo + +enum MyEnum derives _root_.foo.Eq: + case One + +trait Eq[T] +object Eq: + inline def derived[T](using m: scala.deriving.Mirror.Of[T]): Eq[T] = ???