Skip to content

Commit ca29cdc

Browse files
authored
Disallow naming the root package, except for selections (#18187)
References #18020, didn't actually fix that, because it addressed aspects of using `_root_` in definitions. Adding more details to that ticket. Fixes #17757 Fixes #18050 Includes the code in scala/bug#12508
2 parents 9667958 + ba1354d commit ca29cdc

File tree

3 files changed

+86
-17
lines changed

3 files changed

+86
-17
lines changed

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,7 +1073,10 @@ object Parsers {
10731073

10741074
/** Accept identifier and return Ident with its name as a term name. */
10751075
def termIdent(): Ident =
1076-
makeIdent(in.token, in.offset, ident())
1076+
val t = makeIdent(in.token, in.offset, ident())
1077+
if t.name == nme.ROOTPKG then
1078+
syntaxError(em"Illegal use of root package name.")
1079+
t
10771080

10781081
/** Accept identifier and return Ident with its name as a type name. */
10791082
def typeIdent(): Ident =
@@ -1128,19 +1131,21 @@ object Parsers {
11281131

11291132
if in.token == THIS then handleThis(EmptyTypeIdent)
11301133
else if in.token == SUPER then handleSuper(EmptyTypeIdent)
1131-
else
1132-
val t = termIdent()
1133-
if in.token == DOT then
1134-
def qual = cpy.Ident(t)(t.name.toTypeName)
1135-
in.lookahead.token match
1136-
case THIS =>
1137-
in.nextToken()
1138-
handleThis(qual)
1139-
case SUPER =>
1140-
in.nextToken()
1141-
handleSuper(qual)
1142-
case _ => t
1143-
else t
1134+
else if in.token != INTERPOLATIONID && in.lookahead.token == DOT then
1135+
val tok = in.token
1136+
val offset = in.offset
1137+
val name = ident()
1138+
def qual = makeIdent(tok, offset, name.toTypeName)
1139+
in.lookahead.token match
1140+
case THIS =>
1141+
in.nextToken()
1142+
handleThis(qual)
1143+
case SUPER =>
1144+
in.nextToken()
1145+
handleSuper(qual)
1146+
case _ =>
1147+
makeIdent(tok, offset, name)
1148+
else termIdent()
11441149
end simpleRef
11451150

11461151
/** MixinQualifier ::= `[' id `]'

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
569569
// optimization, it also avoids forcing imports thus potentially avoiding
570570
// cyclic references.
571571
if (name == nme.ROOTPKG)
572-
return tree.withType(defn.RootPackage.termRef)
572+
val tree2 = tree.withType(defn.RootPackage.termRef)
573+
checkLegalValue(tree2, pt)
574+
return tree2
573575

574576
val rawType =
575577
val saved1 = unimported
@@ -586,8 +588,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
586588
foundUnderScala2
587589
else found
588590
finally
589-
unimported = saved1
590-
foundUnderScala2 = saved2
591+
unimported = saved1
592+
foundUnderScala2 = saved2
591593

592594
/** Normally, returns `ownType` except if `ownType` is a constructor proxy,
593595
* and there is another shadowed type accessible with the same name that is not:

tests/neg/i18020.scala

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import _root_.scala.StringContext // ok
2+
3+
class Test :
4+
val Foo = 1
5+
def foo0: Unit =
6+
val x = new _root_.scala.StringContext() // ok
7+
val y: Option[_root_.scala.Serializable] = None // ok
8+
val z: _root_.scala.None.type = None
9+
val w = _root_.scala.None
10+
val (_root_, other) = (1, 2) // error
11+
val (Test.this.Foo, 1) = ???
12+
??? match
13+
case (Test.this.Foo, 1) => ()
14+
15+
def foo3 =
16+
val _root_ = "abc" // error
17+
18+
def foo1: Unit =
19+
val _root_: String = "abc" // error // error
20+
// _root_: is, technically, a legal name
21+
// so then it tries to construct the infix op pattern
22+
// "_root_ String .." and then throws in a null when it fails
23+
// to find an argument
24+
// then Typer rejects "String" as an infix extractor (like ::)
25+
// which is the second error
26+
27+
def foo2: Unit = // error
28+
val _root_ : String = "abc" // error
29+
30+
// i17757
31+
def fooVal: Unit =
32+
val _root_ = "abc" // error
33+
println(_root_.length) // error
34+
println(_root_) // error
35+
36+
def barVal: Unit =
37+
_root_ // error
38+
_root_.scala // error
39+
println(_root_) // error
40+
println(_root_.scala) // error
41+
42+
// i18050
43+
package p {
44+
package _root_ { // error
45+
object X // error
46+
}
47+
}
48+
49+
// scala/bug#12508
50+
package _root_ { // error
51+
class C {
52+
val _root_ = 42 // error
53+
}
54+
}
55+
package _root_.p { // error
56+
class C
57+
}
58+
59+
// from ScalaPB
60+
def fromScalaPb(x: Option[String]) = x match
61+
case _root_.scala.Some(s) => s
62+
case _ => ""

0 commit comments

Comments
 (0)