Skip to content

Commit dd4c05c

Browse files
committed
Fix #2473: Check self for inner class redefinitions
1 parent aff93b1 commit dd4c05c

File tree

2 files changed

+59
-32
lines changed

2 files changed

+59
-32
lines changed

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

Lines changed: 52 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1316,49 +1316,69 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
13161316
result
13171317
}
13181318

1319+
/** Checks if one of the decls is a type with the same name as class type member in selfType */
1320+
def checkSelfDecls(decls: Scope, selfType: Type): Boolean = {
1321+
if (!selfType.exists || (selfType.classSymbol eq cls)) false
1322+
else {
1323+
def memberInSelfButNotThis(decl: Symbol) =
1324+
selfType.member(decl.name).symbol.filter(other => other.isClass && other.owner != cls)
1325+
decls.iterator.filter(_.isType).foldLeft(false) { (foundRedef, decl) =>
1326+
val other = memberInSelfButNotThis(decl)
1327+
if (other.exists)
1328+
ctx.error(CannotHaveSameNameAs(decl, other), decl.pos)
1329+
foundRedef || other.exists
1330+
}
1331+
}
1332+
}
1333+
13191334
completeAnnotations(cdef, cls)
13201335
val constr1 = typed(constr).asInstanceOf[DefDef]
13211336
val parentsWithClass = ensureFirstIsClass(parents mapconserve typedParent, cdef.namePos)
13221337
val parents1 = ensureConstrCall(cls, parentsWithClass)(superCtx)
13231338
val self1 = typed(self)(ctx.outer).asInstanceOf[ValDef] // outer context where class members are not visible
13241339
val dummy = localDummy(cls, impl)
1325-
val body1 = typedStats(impl.body, dummy)(inClassContext(self1.symbol))
1326-
cls.setNoInitsFlags((NoInitsInterface /: body1)((fs, stat) => fs & defKind(stat)))
1327-
1328-
// Expand comments and type usecases
1329-
cookComments(body1.map(_.symbol), self1.symbol)(localContext(cdef, cls).setNewScope)
1330-
1331-
checkNoDoubleDefs(cls)
1332-
val impl1 = cpy.Template(impl)(constr1, parents1, self1, body1)
1333-
.withType(dummy.nonMemberTermRef)
1334-
checkVariance(impl1)
1335-
if (!cls.is(AbstractOrTrait) && !ctx.isAfterTyper) checkRealizableBounds(cls.typeRef, cdef.namePos)
1336-
val cdef1 = assignType(cpy.TypeDef(cdef)(name, impl1), cls)
1337-
if (ctx.phase.isTyper && cdef1.tpe.derivesFrom(defn.DynamicClass) && !ctx.dynamicsEnabled) {
1338-
val isRequired = parents1.exists(_.tpe.isRef(defn.DynamicClass))
1339-
ctx.featureWarning(nme.dynamics.toString, "extension of type scala.Dynamic", isScala2Feature = true,
1340+
1341+
if (checkSelfDecls(cls.unforcedDecls, self1.tpt.tpe)) {
1342+
cdef.withType(UnspecifiedErrorType)
1343+
} else {
1344+
val body1 = typedStats(impl.body, dummy)(inClassContext(self1.symbol))
1345+
cls.setNoInitsFlags((NoInitsInterface /: body1) ((fs, stat) => fs & defKind(stat)))
1346+
1347+
// Expand comments and type usecases
1348+
cookComments(body1.map(_.symbol), self1.symbol)(localContext(cdef, cls).setNewScope)
1349+
1350+
checkNoDoubleDefs(cls)
1351+
val impl1 = cpy.Template(impl)(constr1, parents1, self1, body1)
1352+
.withType(dummy.nonMemberTermRef)
1353+
checkVariance(impl1)
1354+
if (!cls.is(AbstractOrTrait) && !ctx.isAfterTyper) checkRealizableBounds(cls.typeRef, cdef.namePos)
1355+
val cdef1 = assignType(cpy.TypeDef(cdef)(name, impl1), cls)
1356+
if (ctx.phase.isTyper && cdef1.tpe.derivesFrom(defn.DynamicClass) && !ctx.dynamicsEnabled) {
1357+
val isRequired = parents1.exists(_.tpe.isRef(defn.DynamicClass))
1358+
ctx.featureWarning(nme.dynamics.toString, "extension of type scala.Dynamic", isScala2Feature = true,
13401359
cls, isRequired, cdef.pos)
1341-
}
1360+
}
13421361

1343-
// Check that phantom lattices are defined in a static object
1344-
if (cls.classParents.exists(_.classSymbol eq defn.PhantomClass) && !cls.isStaticOwner)
1345-
ctx.error("only static objects can extend scala.Phantom", cdef.pos)
1362+
// Check that phantom lattices are defined in a static object
1363+
if (cls.classParents.exists(_.classSymbol eq defn.PhantomClass) && !cls.isStaticOwner)
1364+
ctx.error("only static objects can extend scala.Phantom", cdef.pos)
13461365

1347-
// check value class constraints
1348-
checkDerivedValueClass(cls, body1)
1366+
// check value class constraints
1367+
checkDerivedValueClass(cls, body1)
13491368

1350-
if (ctx.settings.YretainTrees.value) {
1351-
cls.myTree = cdef1
1369+
if (ctx.settings.YretainTrees.value) {
1370+
cls.myTree = cdef1
1371+
}
1372+
cdef1
1373+
1374+
// todo later: check that
1375+
// 1. If class is non-abstract, it is instantiatable:
1376+
// - self type is s supertype of own type
1377+
// - all type members have consistent bounds
1378+
// 2. all private type members have consistent bounds
1379+
// 3. Types do not override classes.
1380+
// 4. Polymorphic type defs override nothing.
13521381
}
1353-
cdef1
1354-
1355-
// todo later: check that
1356-
// 1. If class is non-abstract, it is instantiatable:
1357-
// - self type is s supertype of own type
1358-
// - all type members have consistent bounds
1359-
// 2. all private type members have consistent bounds
1360-
// 3. Types do not override classes.
1361-
// 4. Polymorphic type defs override nothing.
13621382
}
13631383

13641384
/** Ensure that the first type in a list of parent types Ps points to a non-trait class.

tests/neg/i2473.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
trait Foo { trait Inner }
2+
trait Bar { foo: Foo =>
3+
type Inner <: foo.Inner // error: type Inner cannot have the same name as trait Inner in trait Foo -- class definitions cannot be overridden
4+
}
5+
trait Baz { baz: Foo =>
6+
class Inner // error: class Inner cannot have the same name as trait Inner in trait Foo -- class definitions cannot be overridden
7+
}

0 commit comments

Comments
 (0)