From 990d112582e94c4d06dafa597585bd1e6bc332e5 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 2 Aug 2023 17:23:53 +0100 Subject: [PATCH 1/4] Fix selecting terms using _root_ Switch to only reporting val and def names --- .../src/dotty/tools/dotc/parsing/Parsers.scala | 18 +++++++++++++----- tests/neg/i18020.scala | 6 +++--- tests/pos/i18275.scala | 8 ++++++++ 3 files changed, 24 insertions(+), 8 deletions(-) create mode 100644 tests/pos/i18275.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 81118831c8fa..6f43abc1a8f7 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1071,12 +1071,13 @@ object Parsers { nme.ERROR } + def checkNotRoot(name: Name): name.type = + if name == nme.ROOTPKG then syntaxError(em"Illegal use of root package name.") + name + /** 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 = @@ -3601,6 +3602,13 @@ object Parsers { case _ => first :: Nil } + + def checkForRoot(trees: List[Tree]): Unit = for tree <- trees do tree match + case IdPattern(id, _) => checkNotRoot(id.name) + case Tuple(trees) => checkForRoot(trees) + case _ => + checkForRoot(lhs) + val tpt = typedOpt() val rhs = if tpt.isEmpty || in.token == EQUALS then @@ -3681,7 +3689,7 @@ object Parsers { else { val mods1 = addFlag(mods, Method) val ident = termIdent() - var name = ident.name.asTermName + var name = checkNotRoot(ident.name).asTermName val paramss = if in.featureEnabled(Feature.clauseInterleaving) then // If you are making interleaving stable manually, please refer to the PR introducing it instead, section "How to make non-experimental" diff --git a/tests/neg/i18020.scala b/tests/neg/i18020.scala index b924c136d863..fdad48a8097a 100644 --- a/tests/neg/i18020.scala +++ b/tests/neg/i18020.scala @@ -41,18 +41,18 @@ def barVal: Unit = // i18050 package p { - package _root_ { // error + package _root_ { // not-reported object X // error } } // 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] = ??? From 052afd87e62d1d39ce6c61faff18969431ba752a Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 8 Aug 2023 11:11:55 +0100 Subject: [PATCH 2/4] Move root package name check to Typer --- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 13 +------------ compiler/src/dotty/tools/dotc/typer/Typer.scala | 2 ++ 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 6f43abc1a8f7..d830505f8419 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1071,10 +1071,6 @@ object Parsers { nme.ERROR } - def checkNotRoot(name: Name): name.type = - if name == nme.ROOTPKG then syntaxError(em"Illegal use of root package name.") - name - /** Accept identifier and return Ident with its name as a term name. */ def termIdent(): Ident = makeIdent(in.token, in.offset, ident()) @@ -3602,13 +3598,6 @@ object Parsers { case _ => first :: Nil } - - def checkForRoot(trees: List[Tree]): Unit = for tree <- trees do tree match - case IdPattern(id, _) => checkNotRoot(id.name) - case Tuple(trees) => checkForRoot(trees) - case _ => - checkForRoot(lhs) - val tpt = typedOpt() val rhs = if tpt.isEmpty || in.token == EQUALS then @@ -3689,7 +3678,7 @@ object Parsers { else { val mods1 = addFlag(mods, Method) val ident = termIdent() - var name = checkNotRoot(ident.name).asTermName + var name = ident.name.asTermName val paramss = if in.featureEnabled(Feature.clauseInterleaving) then // If you are making interleaving stable manually, please refer to the PR introducing it instead, section "How to make non-experimental" diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 3aaf4fec59d6..fe20584d8800 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 + if name == nme.ROOTPKG then report.error(em"Illegal use of root package name.", vdef) 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 + if name == nme.ROOTPKG then report.error(em"Illegal use of root package name.", ddef) completeAnnotations(ddef, sym) val paramss1 = paramss.nestedMapConserve(typed(_)).asInstanceOf[List[ParamClause]] for case ValDefs(vparams) <- paramss1 do From 68b7b7593448e3f8ecf1ce017fb94bba73d316eb Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 8 Aug 2023 17:40:26 +0100 Subject: [PATCH 3/4] Check package names too --- compiler/src/dotty/tools/dotc/typer/Checking.scala | 5 +++++ compiler/src/dotty/tools/dotc/typer/Typer.scala | 6 ++++-- tests/neg/i18020.scala | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) 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 fe20584d8800..31d2e5d81a38 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2472,7 +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 - if name == nme.ROOTPKG then report.error(em"Illegal use of root package name.", vdef) + checkNonRootName(vdef.name, vdef.nameSpan) completeAnnotations(vdef, sym) if (sym.isOneOf(GivenOrImplicit)) checkImplicitConversionDefOK(sym) if sym.is(Module) then checkNoModuleClash(sym) @@ -2506,7 +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 - if name == nme.ROOTPKG then report.error(em"Illegal use of root package name.", ddef) + checkNonRootName(ddef.name, ddef.nameSpan) completeAnnotations(ddef, sym) val paramss1 = paramss.nestedMapConserve(typed(_)).asInstanceOf[List[ParamClause]] for case ValDefs(vparams) <- paramss1 do @@ -2855,6 +2855,8 @@ 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.EmptyPackageClass 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 fdad48a8097a..2714574af5fc 100644 --- a/tests/neg/i18020.scala +++ b/tests/neg/i18020.scala @@ -41,7 +41,7 @@ def barVal: Unit = // i18050 package p { - package _root_ { // not-reported + package _root_ { // error object X // error } } From 9a4171690a7856278f3cd7dd8d84996aa26d3175 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 9 Aug 2023 10:24:37 +0100 Subject: [PATCH 4/4] Tweak package name exeptions --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 31d2e5d81a38..0adb6d5a94b4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2855,7 +2855,9 @@ 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.EmptyPackageClass then + 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