Skip to content

Commit 0397af7

Browse files
authored
Merge pull request #1717 from dotty-staging/fix-#1688
Don't allow redefinition of core classes
2 parents adb37ee + fb59174 commit 0397af7

File tree

4 files changed

+44
-16
lines changed

4 files changed

+44
-16
lines changed

src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,8 @@ object desugar {
250250

251251
/** The expansion of a class definition. See inline comments for what is involved */
252252
def classDef(cdef: TypeDef)(implicit ctx: Context): Tree = {
253-
val TypeDef(name, impl @ Template(constr0, parents, self, _)) = cdef
253+
val className = checkNotReservedName(cdef).asTypeName
254+
val impl @ Template(constr0, parents, self, _) = cdef.rhs
254255
val mods = cdef.mods
255256
val companionMods = mods.withFlags((mods.flags & AccessFlags).toCommonFlags)
256257

@@ -384,7 +385,7 @@ object desugar {
384385
def companionDefs(parentTpt: Tree, defs: List[Tree]) =
385386
moduleDef(
386387
ModuleDef(
387-
name.toTermName, Template(emptyConstructor, parentTpt :: Nil, EmptyValDef, defs))
388+
className.toTermName, Template(emptyConstructor, parentTpt :: Nil, EmptyValDef, defs))
388389
.withMods(companionMods | Synthetic))
389390
.withPos(cdef.pos).toList
390391

@@ -443,7 +444,7 @@ object desugar {
443444
else
444445
// implicit wrapper is typechecked in same scope as constructor, so
445446
// we can reuse the constructor parameters; no derived params are needed.
446-
DefDef(name.toTermName, constrTparams, constrVparamss, classTypeRef, creatorExpr)
447+
DefDef(className.toTermName, constrTparams, constrVparamss, classTypeRef, creatorExpr)
447448
.withMods(companionMods | Synthetic | Implicit)
448449
.withPos(cdef.pos) :: Nil
449450

@@ -460,6 +461,7 @@ object desugar {
460461
val caseAccessor = if (isCaseClass) CaseAccessor else EmptyFlags
461462
val vparamAccessors = derivedVparamss.flatten.map(_.withMods(originalVparams.next.mods | caseAccessor))
462463
cpy.TypeDef(cdef)(
464+
name = className,
463465
rhs = cpy.Template(impl)(constr, parents1, self1,
464466
tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths),
465467
tparams = Nil)
@@ -485,20 +487,21 @@ object desugar {
485487
* <module> final class name$ extends parents { self: name.type => body }
486488
*/
487489
def moduleDef(mdef: ModuleDef)(implicit ctx: Context): Tree = {
488-
val ModuleDef(name, tmpl) = mdef
490+
val moduleName = checkNotReservedName(mdef).asTermName
491+
val tmpl = mdef.impl
489492
val mods = mdef.mods
490493
if (mods is Package)
491-
PackageDef(Ident(name), cpy.ModuleDef(mdef)(nme.PACKAGE, tmpl).withMods(mods &~ Package) :: Nil)
494+
PackageDef(Ident(moduleName), cpy.ModuleDef(mdef)(nme.PACKAGE, tmpl).withMods(mods &~ Package) :: Nil)
492495
else {
493-
val clsName = name.moduleClassName
496+
val clsName = moduleName.moduleClassName
494497
val clsRef = Ident(clsName)
495-
val modul = ValDef(name, clsRef, New(clsRef, Nil))
498+
val modul = ValDef(moduleName, clsRef, New(clsRef, Nil))
496499
.withMods(mods | ModuleCreationFlags | mods.flags & AccessFlags)
497500
.withPos(mdef.pos)
498501
val ValDef(selfName, selfTpt, _) = tmpl.self
499502
val selfMods = tmpl.self.mods
500503
if (!selfTpt.isEmpty) ctx.error(ObjectMayNotHaveSelfType(mdef), tmpl.self.pos)
501-
val clsSelf = ValDef(selfName, SingletonTypeTree(Ident(name)), tmpl.self.rhs)
504+
val clsSelf = ValDef(selfName, SingletonTypeTree(Ident(moduleName)), tmpl.self.rhs)
502505
.withMods(selfMods)
503506
.withPos(tmpl.self.pos orElse tmpl.pos.startPos)
504507
val clsTmpl = cpy.Template(tmpl)(self = clsSelf, body = tmpl.body)
@@ -508,6 +511,19 @@ object desugar {
508511
}
509512
}
510513

514+
/** The name of `mdef`, after checking that it does not redefine a Scala core class.
515+
* If it does redefine, issue an error and return a mangled name instead of the original one.
516+
*/
517+
def checkNotReservedName(mdef: MemberDef)(implicit ctx: Context): Name = {
518+
val name = mdef.name
519+
if (ctx.owner == defn.ScalaPackageClass && defn.reservedScalaClassNames.contains(name.toTypeName)) {
520+
def kind = if (name.isTypeName) "class" else "object"
521+
ctx.error(em"illegal redefinition of standard $kind $name", mdef.pos)
522+
name.errorName
523+
}
524+
else name
525+
}
526+
511527
/** val p1, ..., pN: T = E
512528
* ==>
513529
* makePatDef[[val p1: T1 = E]]; ...; makePatDef[[val pN: TN = E]]

src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -633,9 +633,9 @@ class Definitions {
633633
name.startsWith(prefix) && name.drop(prefix.length).forall(_.isDigit)
634634
}
635635

636-
def isBottomClass(cls: Symbol) =
636+
def isBottomClass(cls: Symbol) =
637637
cls == NothingClass || cls == NullClass
638-
def isBottomType(tp: Type) =
638+
def isBottomType(tp: Type) =
639639
tp.derivesFrom(NothingClass) || tp.derivesFrom(NullClass)
640640

641641
def isFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.Function)
@@ -761,7 +761,7 @@ class Definitions {
761761
// ----- Initialization ---------------------------------------------------
762762

763763
/** Lists core classes that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */
764-
lazy val syntheticCoreClasses = List(
764+
lazy val syntheticScalaClasses = List(
765765
AnyClass,
766766
AnyRefAlias,
767767
RepeatedParamClass,
@@ -770,12 +770,16 @@ class Definitions {
770770
NullClass,
771771
NothingClass,
772772
SingletonClass,
773-
EqualsPatternClass,
773+
EqualsPatternClass)
774+
775+
lazy val syntheticCoreClasses = syntheticScalaClasses ++ List(
774776
EmptyPackageVal,
775777
OpsPackageClass)
776778

777-
/** Lists core methods that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */
778-
lazy val syntheticCoreMethods = AnyMethods ++ ObjectMethods ++ List(String_+, throwMethod)
779+
/** Lists core methods that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */
780+
lazy val syntheticCoreMethods = AnyMethods ++ ObjectMethods ++ List(String_+, throwMethod)
781+
782+
lazy val reservedScalaClassNames: Set[Name] = syntheticScalaClasses.map(_.name).toSet
779783

780784
private[this] var _isInitialized = false
781785
private def isInitialized = _isInitialized
@@ -785,8 +789,8 @@ class Definitions {
785789
if (!_isInitialized) {
786790
// force initialization of every symbol that is synthesized or hijacked by the compiler
787791
val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses()
788-
789-
// Enter all symbols from the scalaShadowing package in the scala package
792+
793+
// Enter all symbols from the scalaShadowing package in the scala package
790794
for (m <- ScalaShadowingPackageClass.info.decls)
791795
ScalaPackageClass.enter(m)
792796

src/dotty/tools/dotc/core/NameOps.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ object NameOps {
186186

187187
def implClassName: N = likeTyped(name ++ tpnme.IMPL_CLASS_SUFFIX)
188188

189+
def errorName: N = likeTyped(name ++ nme.ERROR)
190+
189191
def freshened(implicit ctx: Context): N =
190192
likeTyped(
191193
if (name.isModuleClassName) name.stripModuleClassSuffix.freshened.moduleClassName

tests/neg/i1688.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package scala
2+
sealed trait Null // error
3+
4+
object Null // error
5+
6+
class AnyVal // error

0 commit comments

Comments
 (0)