Skip to content

Commit 490b937

Browse files
committed
Update "good bounds" checks
These need to be special-cased for base types now, since conflicting base types do not need any longer to public type members with conflicting bounds. A Test case where this shows up is points.scala. Another is Iter3.scala which got moved from pos to neg. These test cases passed in the old scheme, probably because comflicting aliases were not coalesced into bad type bounds. points.scala failed at ElimErasedValueTypes because it reported a double definition, but I think that one was not correct either and came from the same root cause of conflicting aliases.
1 parent 477dfe1 commit 490b937

File tree

5 files changed

+53
-19
lines changed

5 files changed

+53
-19
lines changed

compiler/src/dotty/tools/dotc/core/CheckRealizable.scala

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ object CheckRealizable {
3333
class HasProblemBounds(typ: SingleDenotation)(implicit ctx: Context)
3434
extends Realizability(i" has a member $typ with possibly conflicting bounds ${typ.info.bounds.lo} <: ... <: ${typ.info.bounds.hi}")
3535

36+
class HasProblemBaseArg(typ: Type, argBounds: TypeBounds)(implicit ctx: Context)
37+
extends Realizability(i" has a base type $typ with possibly conflicting parameter bounds ${argBounds.lo} <: ... <: ${argBounds.hi}")
38+
39+
class HasProblemBase(base1: Type, base2: Type)(implicit ctx: Context)
40+
extends Realizability(i" has conflicting base types $base1 and $base2")
41+
3642
class HasProblemField(fld: SingleDenotation, problem: Realizability)(implicit ctx: Context)
3743
extends Realizability(i" has a member $fld which is not a legal path\n since ${fld.symbol.name}: ${fld.info}${problem.msg}")
3844

@@ -89,18 +95,36 @@ class CheckRealizable(implicit ctx: Context) {
8995
else boundsRealizability(tp).andAlso(memberRealizability(tp))
9096
}
9197

92-
/** `Realizable` if `tp` has good bounds, a `HasProblemBounds` instance
93-
* pointing to a bad bounds member otherwise.
98+
/** `Realizable` if `tp` has good bounds, a `HasProblem...` instance
99+
* pointing to a bad bounds member otherwise. "Has good bounds" means:
100+
*
101+
* - all type members have good bounds
102+
* - all base types are class types, and if their arguments are wildcards
103+
* they have good bounds.
94104
*/
95105
private def boundsRealizability(tp: Type) = {
96-
def hasBadBounds(mbr: SingleDenotation) = {
97-
val bounds = mbr.info.bounds
98-
!(bounds.lo <:< bounds.hi)
99-
}
100-
tp.nonClassTypeMembers.find(hasBadBounds) match {
101-
case Some(mbr) => new HasProblemBounds(mbr)
102-
case _ => Realizable
106+
val mbrProblems =
107+
for {
108+
mbr <- tp.nonClassTypeMembers
109+
if !(mbr.info.loBound <:< mbr.info.hiBound)
110+
}
111+
yield new HasProblemBounds(mbr)
112+
113+
def baseTypeProblems(base: Type) = base match {
114+
case AndType(base1, base2) =>
115+
new HasProblemBase(base1, base2) :: Nil
116+
case base =>
117+
base.argInfos.collect {
118+
case bounds @ TypeBounds(lo, hi) if !(lo <:< hi) =>
119+
new HasProblemBaseArg(base, bounds)
120+
}
103121
}
122+
val baseProblems =
123+
tp.baseClasses.map(_.baseTypeOf(tp)).flatMap(baseTypeProblems)
124+
125+
(((Realizable: Realizability)
126+
/: mbrProblems)(_ andAlso _)
127+
/: baseProblems)(_ andAlso _)
104128
}
105129

106130
/** `Realizable` if all of `tp`'s non-struct fields have realizable types,

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -547,10 +547,10 @@ trait Checking {
547547
if (!tp.isStable) ctx.error(ex"$tp is not stable", pos)
548548

549549
/** Check that all type members of `tp` have realizable bounds */
550-
def checkRealizableBounds(tp: Type, pos: Position)(implicit ctx: Context): Unit = {
551-
val rstatus = boundsRealizability(tp)
550+
def checkRealizableBounds(cls: Symbol, pos: Position)(implicit ctx: Context): Unit = {
551+
val rstatus = boundsRealizability(cls.thisType)
552552
if (rstatus ne Realizable)
553-
ctx.error(ex"$tp cannot be instantiated since it${rstatus.msg}", pos)
553+
ctx.error(ex"$cls cannot be instantiated since it${rstatus.msg}", pos)
554554
}
555555

556556
/** Check that `tp` is a class type.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1413,7 +1413,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
14131413
.withType(dummy.nonMemberTermRef)
14141414
checkVariance(impl1)
14151415
if (!cls.is(AbstractOrTrait) && !ctx.isAfterTyper)
1416-
checkRealizableBounds(cls.thisType, cdef.namePos) // !@@@ adapt
1416+
checkRealizableBounds(cls, cdef.namePos) // !@@@ adapt
14171417
val cdef1 = assignType(cpy.TypeDef(cdef)(name, impl1), cls)
14181418
if (ctx.phase.isTyper && cdef1.tpe.derivesFrom(defn.DynamicClass) && !ctx.dynamicsEnabled) {
14191419
val isRequired = parents1.exists(_.tpe.isRef(defn.DynamicClass))

tests/pos/Iter3.scala renamed to tests/neg/Iter3.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,19 +59,19 @@ object Iter2 {
5959
if (isEmpty) 0 else 1 + tail.length
6060
}
6161

62-
case class Cons[A](x: A, xs: List[A]) extends List[A] {
62+
case class Cons[A](x: A, xs: List[A]) extends List[A] { // error: cannot be instantiated
6363
def isEmpty = false
6464
def head = x
6565
def tail = xs
6666
}
6767

68-
case object Nil extends List[Nothing] {
68+
case object Nil extends List[Nothing] { // error: cannot be instantiated
6969
def isEmpty = true
7070
def head = ???
7171
def tail = ???
7272
}
7373

74-
class ArrayBuffer[A] private (initElems: Array[AnyRef], initLen: Int) extends Seq[A] with FromIterator[ArrayBuffer] {
74+
class ArrayBuffer[A] private (initElems: Array[AnyRef], initLen: Int) extends Seq[A] with FromIterator[ArrayBuffer] { // error: cannot be instantiated
7575
def this() = this(new Array[AnyRef](16), 0)
7676
def this(it: ArrayIterator[A]) = this(it.elems, it.len)
7777
private var elems: Array[AnyRef] = initElems

tests/neg/points.scala

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
1+
trait Comparable[T] {
2+
def compareTo(other: T): Int
3+
}
4+
15
class Point extends Comparable[Point] {
2-
override def compareTo(other: Point): Int = ???
6+
override def compareTo(other: Point): Int = 1
37
}
48

5-
class ColoredPoint extends Point with Comparable[ColoredPoint] {
6-
override def compareTo(other: ColoredPoint): Int = ??? // error: overridden method has different signature
9+
class ColoredPoint extends Point with Comparable[ColoredPoint] { // error: cannot be instantiated
10+
override def compareTo(other: ColoredPoint): Int = -1
711
}
812

13+
object Test extends App {
14+
val c: Point = new ColoredPoint
15+
def cmp[T <: Comparable[T]](p1: Comparable[T], p2: T) =
16+
p1.compareTo(p2)
17+
println(cmp(c, c))
18+
}

0 commit comments

Comments
 (0)