Skip to content

Commit 198a6c7

Browse files
committed
Fix #2770: Tighten type compatibility checks for overrides
- We now require strict subtypes if owning classes are in a subclass relationship. Nonempty bounds good enough only if classes are unrelated. - We treat class types correctly by eta-expanding their types. - We do kind checking on types. Previously, some illegal things slipped through because of the way Any works. In fact a hk type like `[X] => Any` is a subtype of `Any` according to TypeComparer. But kind checking should prevent us from making that comparison in the first place.
1 parent 775e3db commit 198a6c7

File tree

5 files changed

+52
-37
lines changed

5 files changed

+52
-37
lines changed

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,19 @@ class TypeApplications(val self: Type) extends AnyVal {
268268
case _ => NoType
269269
}
270270

271+
/** Do self and other have the same kinds (not counting bounds and variances) */
272+
def hasSameKindAs(other: Type)(implicit ctx: Context): Boolean = {
273+
// println(i"check kind $self $other") // DEBUG
274+
val selfResult = self.hkResult
275+
val otherResult = other.hkResult
276+
if (selfResult.exists)
277+
otherResult.exists &&
278+
selfResult.hasSameKindAs(otherResult) &&
279+
self.typeParams.corresponds(other.typeParams)((sparam, oparam) =>
280+
sparam.paramInfo.hasSameKindAs(oparam.paramInfo))
281+
else !otherResult.exists
282+
}
283+
271284
/** Dealias type if it can be done without forcing the TypeRef's info */
272285
def safeDealias(implicit ctx: Context): Type = self match {
273286
case self: TypeRef if self.denot.exists && self.symbol.isAliasType =>

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

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -109,26 +109,9 @@ object Checking {
109109
* types. Self application needs to be avoided since it can lead to stack overflows.
110110
* Test cases are neg/i2771.scala and neg/i2771b.scala.
111111
*/
112-
def preCheckKind(arg: Tree, paramBounds: TypeBounds)(implicit ctx: Context): Tree = {
113-
def result(tp: Type): Type = tp match {
114-
case tp: HKTypeLambda => tp.resultType
115-
case tp: TypeProxy => result(tp.superType)
116-
case _ => defn.AnyType
117-
}
118-
def kindOK(argType: Type, boundType: Type): Boolean = {
119-
// println(i"check kind rank2$arg $argType $boundType") // DEBUG
120-
val argResult = argType.hkResult
121-
val boundResult = argType.hkResult
122-
if (argResult.exists)
123-
boundResult.exists &&
124-
kindOK(boundResult, argResult) &&
125-
argType.typeParams.corresponds(boundType.typeParams)((ap, bp) =>
126-
kindOK(ap.paramInfo, bp.paramInfo))
127-
else !boundResult.exists
128-
}
129-
if (kindOK(arg.tpe, paramBounds.hi)) arg
130-
else errorTree(arg, em"${arg.tpe} has wrong kind")
131-
}
112+
def preCheckKind(arg: Tree, paramBounds: TypeBounds)(implicit ctx: Context): Tree =
113+
if (arg.tpe.hasSameKindAs(paramBounds.hi)) arg
114+
else errorTree(arg, em"Type argument ${arg.tpe} has not the same kind as its bound $paramBounds")
132115

133116
def preCheckKinds(args: List[Tree], paramBoundss: List[TypeBounds])(implicit ctx: Context): List[Tree] = {
134117
val args1 = args.zipWithConserve(paramBoundss)(preCheckKind)

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

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -183,25 +183,27 @@ object RefChecks {
183183
def infoString0(sym: Symbol, showLocation: Boolean) = {
184184
val sym1 = sym.underlyingSymbol
185185
def info = self.memberInfo(sym1)
186-
i"${if (showLocation) sym1.showLocated else sym1}${
186+
val infoStr =
187187
if (sym1.isAliasType) i", which equals ${info.bounds.hi}"
188188
else if (sym1.isAbstractType) i" with bounds$info"
189189
else if (sym1.is(Module)) ""
190190
else if (sym1.isTerm) i" of type $info"
191191
else ""
192-
}"
192+
i"${if (showLocation) sym1.showLocated else sym1}$infoStr"
193193
}
194194

195195
/* Check that all conditions for overriding `other` by `member`
196196
* of class `clazz` are met.
197197
*/
198198
def checkOverride(member: Symbol, other: Symbol): Unit = {
199-
def memberTp = self.memberInfo(member)
200-
def otherTp = self.memberInfo(other)
199+
def memberTp(self: Type) =
200+
if (member.isClass) TypeAlias(member.typeRef.EtaExpand(member.typeParams))
201+
else self.memberInfo(member)
202+
def otherTp(self: Type) = self.memberInfo(other)
201203

202204
ctx.debuglog("Checking validity of %s overriding %s".format(member.showLocated, other.showLocated))
203205

204-
def noErrorType = !memberTp.isErroneous && !otherTp.isErroneous
206+
def noErrorType = !memberTp(self).isErroneous && !otherTp(self).isErroneous
205207

206208
def overrideErrorMsg(msg: String): String = {
207209
val isConcreteOverAbstract =
@@ -212,10 +214,10 @@ object RefChecks {
212214
infoStringWithLocation(other),
213215
infoStringWithLocation(member))
214216
else if (ctx.settings.debug.value)
215-
err.typeMismatchMsg(memberTp, otherTp)
217+
err.typeMismatchMsg(memberTp(self), otherTp(self))
216218
else ""
217219

218-
"overriding %s;\n %s %s%s".format(
220+
"error overriding %s;\n %s %s%s".format(
219221
infoStringWithLocation(other), infoString(member), msg, addendum)
220222
}
221223

@@ -248,13 +250,16 @@ object RefChecks {
248250

249251
def compatibleTypes(memberTp: Type, otherTp: Type): Boolean =
250252
try
251-
if (member.isType) { // intersection of bounds to refined types must be nonempty
253+
if (member.isType) // intersection of bounds to refined types must be nonempty
252254
member.is(BaseTypeArg) ||
253-
(memberTp frozen_<:< otherTp) || {
254-
val jointBounds = (memberTp.bounds & otherTp.bounds).bounds
255-
jointBounds.lo frozen_<:< jointBounds.hi
256-
}
257-
}
255+
memberTp.bounds.hi.hasSameKindAs(otherTp.bounds.hi) &&
256+
((memberTp frozen_<:< otherTp) ||
257+
!member.owner.derivesFrom(other.owner) && {
258+
// if member and other come from independent classes or traits, their
259+
// bounds must have non-empty-intersection
260+
val jointBounds = (memberTp.bounds & otherTp.bounds).bounds
261+
jointBounds.lo frozen_<:< jointBounds.hi
262+
})
258263
else
259264
member.name.is(DefaultGetterName) || // default getters are not checked for compatibility
260265
memberTp.overrides(otherTp,
@@ -367,9 +372,9 @@ object RefChecks {
367372
overrideError("cannot be used here - term macros cannot override abstract methods")
368373
} else if (other.is(Macro) && !member.is(Macro)) { // (1.10)
369374
overrideError("cannot be used here - only term macros can override term macros")
370-
} else if (!compatibleTypes(memberTp, otherTp) &&
371-
!compatibleTypes(upwardsSelf.memberInfo(member), upwardsSelf.memberInfo(other))) {
372-
overrideError("has incompatible type" + err.whyNoMatchStr(memberTp, otherTp))
375+
} else if (!compatibleTypes(memberTp(self), otherTp(self)) &&
376+
!compatibleTypes(memberTp(upwardsSelf), otherTp(upwardsSelf))) {
377+
overrideError("has incompatible type" + err.whyNoMatchStr(memberTp(self), otherTp(self)))
373378
} else {
374379
checkOverrideDeprecated()
375380
}

tests/neg/i2770.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
trait A { type L }
2+
trait B extends A { type L[X] } // error: illegal override
3+
4+
trait A2 { type L <: String }
5+
trait B2 extends A2 { type L[X] <: String } // error: illegal override
6+
trait C2 extends B2 { type L[X, Y] <: String } // error: illegal override
7+
8+
trait D { type I }
9+
trait E extends D { type I <: String }
10+
trait F extends D { type I >: String }
11+
trait G extends E with F // ok
12+
13+
trait H extends D { type I >: Int }
14+
trait H2 extends E with H // error: illegal override

tests/neg/i2771.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ object Test {
1414
type LL[F[_]] <: LB[F] // ok
1515

1616
def foo[X[_] <: Any]() = ()
17-
foo[Int]() // an error would be raised later, during PostTyper.
17+
foo[Int]() // error: Int has wrong kind
1818

1919
def bar[X, Y]() = ()
2020
bar[List, Int]() // error: List has wrong kind

0 commit comments

Comments
 (0)