Skip to content

Commit e4bcbda

Browse files
authored
Merge pull request #2553 from dotty-staging/fix-#2473
Fix #2473: Check self for inner class redefinitions
2 parents 39acd47 + fd64da7 commit e4bcbda

File tree

5 files changed

+44
-5
lines changed

5 files changed

+44
-5
lines changed

compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1479,12 +1479,26 @@ object messages {
14791479
|"""
14801480
}
14811481

1482-
case class CannotHaveSameNameAs(sym: Symbol, cls: Symbol)(implicit ctx: Context)
1482+
case class CannotHaveSameNameAs(sym: Symbol, cls: Symbol, reason: CannotHaveSameNameAs.Reason)(implicit ctx: Context)
14831483
extends Message(CannotHaveSameNameAsID) {
1484-
val msg = hl"""$sym cannot have the same name as ${cls.showLocated} -- class definitions cannot be overridden"""
1484+
import CannotHaveSameNameAs._
1485+
def reasonMessage: String = reason match {
1486+
case CannotBeOverridden => "class definitions cannot be overridden"
1487+
case DefinedInSelf(self) =>
1488+
s"""cannot define ${sym.showKind} member with the same name as a ${cls.showKind} member in self reference ${self.name}.
1489+
|(Note: this can be resolved by using another name)
1490+
|""".stripMargin
1491+
}
1492+
1493+
val msg = hl"""$sym cannot have the same name as ${cls.showLocated} -- """ + reasonMessage
14851494
val kind = "Syntax"
14861495
val explanation = ""
14871496
}
1497+
object CannotHaveSameNameAs {
1498+
sealed trait Reason
1499+
case object CannotBeOverridden extends Reason
1500+
case class DefinedInSelf(self: tpd.ValDef) extends Reason
1501+
}
14881502

14891503
case class ValueClassesMayNotDefineInner(valueClass: Symbol, inner: Symbol)(implicit ctx: Context)
14901504
extends Message(ValueClassesMayNotDefineInnerID) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ object Checking {
347347
checkNoConflict(Abstract, Override)
348348
if (sym.isType && !sym.is(Deferred))
349349
for (cls <- sym.allOverriddenSymbols.filter(_.isClass)) {
350-
fail(CannotHaveSameNameAs(sym, cls))
350+
fail(CannotHaveSameNameAs(sym, cls, CannotHaveSameNameAs.CannotBeOverridden))
351351
sym.setFlag(Private) // break the overriding relationship by making sym Private
352352
}
353353
}

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1326,12 +1326,30 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
13261326
result
13271327
}
13281328

1329+
/** Checks if one of the decls is a type with the same name as class type member in selfType */
1330+
def classExistsOnSelf(decls: Scope, self: tpd.ValDef): Boolean = {
1331+
val selfType = self.tpt.tpe
1332+
if (!selfType.exists || (selfType.classSymbol eq cls)) false
1333+
else {
1334+
def memberInSelfButNotThis(decl: Symbol) =
1335+
selfType.member(decl.name).symbol.filter(other => other.isClass && other.owner != cls)
1336+
decls.iterator.filter(_.isType).foldLeft(false) { (foundRedef, decl) =>
1337+
val other = memberInSelfButNotThis(decl)
1338+
if (other.exists) {
1339+
val msg = CannotHaveSameNameAs(decl, other, CannotHaveSameNameAs.DefinedInSelf(self))
1340+
ctx.error(msg, decl.pos)
1341+
}
1342+
foundRedef || other.exists
1343+
}
1344+
}
1345+
}
1346+
13291347
completeAnnotations(cdef, cls)
13301348
val constr1 = typed(constr).asInstanceOf[DefDef]
13311349
val parentsWithClass = ensureFirstIsClass(parents mapconserve typedParent, cdef.namePos)
13321350
val parents1 = ensureConstrCall(cls, parentsWithClass)(superCtx)
13331351
val self1 = typed(self)(ctx.outer).asInstanceOf[ValDef] // outer context where class members are not visible
1334-
if (self1.tpt.tpe.isError) {
1352+
if (self1.tpt.tpe.isError || classExistsOnSelf(cls.unforcedDecls, self1)) {
13351353
// fail fast to avoid typing the body with an error type
13361354
cdef.withType(UnspecifiedErrorType)
13371355
} else {

compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -669,7 +669,7 @@ class ErrorMessagesTests extends ErrorMessagesTest {
669669
.expect { (ictx, messages) =>
670670
implicit val ctx: Context = ictx
671671
assertMessageCount(1, messages)
672-
val CannotHaveSameNameAs(symbol, cls) :: Nil = messages
672+
val CannotHaveSameNameAs(symbol, cls, _) :: Nil = messages
673673
assertEquals("class A", symbol.show)
674674
assertEquals("class A", cls.show)
675675
}

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
4+
}
5+
trait Baz { baz: Foo =>
6+
class Inner // error
7+
}

0 commit comments

Comments
 (0)