Skip to content

Commit 1c56c9d

Browse files
committed
Fix override containing type param
1 parent 2b37257 commit 1c56c9d

File tree

4 files changed

+59
-10
lines changed

4 files changed

+59
-10
lines changed

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,29 @@ object NullOpsDecorator:
4949
val stripped = self.stripNull
5050
stripped ne self
5151
}
52+
53+
/** Strips nulls from this type deeply.
54+
* Compaired to `stripNull`, `stripNullsDeep` will apply `stripNull` to
55+
* each member of function types as well.
56+
*/
57+
def stripNullsDeep(using Context): Type =
58+
object DeepStripNullsMap extends TypeMap:
59+
override def apply(tp: Type): Type = tp match
60+
case appTp @ AppliedType(tycon, targs) =>
61+
derivedAppliedType(appTp, tycon, targs.map(this))
62+
case ptp: PolyType =>
63+
derivedLambdaType(ptp)(ptp.paramInfos, this(ptp.resType))
64+
case mtp: MethodType =>
65+
mapOver(mtp)
66+
case tp: TypeAlias =>
67+
mapOver(tp)
68+
case _ =>
69+
val tp1 = tp.stripNull
70+
if tp1 eq tp then tp else this(tp1)
71+
end DeepStripNullsMap
72+
73+
if ctx.explicitNulls then DeepStripNullsMap(self) else self
74+
5275
end extension
5376

5477
import ast.tpd._

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1112,8 +1112,10 @@ object Types {
11121112
*/
11131113
def matches(that: Type)(using Context): Boolean = {
11141114
record("matches")
1115+
val thisTp1 = this.stripNullsDeep
1116+
val thatTp1 = that.stripNullsDeep
11151117
withoutMode(Mode.SafeNulls)(
1116-
TypeComparer.matchesType(this, that, relaxed = !ctx.phase.erasedTypes))
1118+
TypeComparer.matchesType(thisTp1, thatTp1, relaxed = !ctx.phase.erasedTypes))
11171119
}
11181120

11191121
/** This is the same as `matches` except that it also matches => T with T and

compiler/src/dotty/tools/dotc/transform/OverridingPairs.scala

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package transform
55
import core._
66
import Flags._, Symbols._, Contexts._, Scopes._, Decorators._, Types.Type
77
import NameKinds.DefaultGetterName
8+
import NullOpsDecorator._
89
import collection.mutable
910
import collection.immutable.BitSet
1011
import scala.annotation.tailrec
@@ -215,15 +216,20 @@ object OverridingPairs:
215216
}
216217
)
217218
else
218-
// releaxed override check for explicit nulls if one of the symbols is Java defined,
219-
// force `Null` being a subtype of reference types during override checking
220-
val relaxedCtxForNulls =
219+
val matchNullaryLoosely = member.matchNullaryLoosely || other.matchNullaryLoosely || fallBack
220+
// default getters are not checked for compatibility
221+
member.name.is(DefaultGetterName) || {
221222
if ctx.explicitNulls && (member.is(JavaDefined) || other.is(JavaDefined)) then
222-
ctx.retractMode(Mode.SafeNulls)
223-
else ctx
224-
member.name.is(DefaultGetterName) // default getters are not checked for compatibility
225-
|| memberTp.overrides(otherTp,
226-
member.matchNullaryLoosely || other.matchNullaryLoosely || fallBack
227-
)(using relaxedCtxForNulls)
223+
// releaxed override check for explicit nulls if one of the symbols is Java defined,
224+
// force `Null` being a subtype of reference types during override checking.
225+
// `stripNullsDeep` is used here because we may encounter type parameters
226+
// (`T | Null` is not a subtype of `T` even if we retract Mode.SafeNulls).
227+
val memberTp1 = memberTp.stripNullsDeep
228+
val otherTp1 = otherTp.stripNullsDeep
229+
withoutMode(Mode.SafeNulls)(
230+
memberTp1.overrides(otherTp1, matchNullaryLoosely))
231+
else
232+
memberTp.overrides(otherTp, matchNullaryLoosely)
233+
}
228234

229235
end OverridingPairs
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Testing relaxed overriding check for explicit nulls.
2+
// The relaxed check is only enabled if one of the members is Java defined.
3+
4+
import java.util.Comparator
5+
6+
class C1[T <: AnyRef] extends Ordering[T]:
7+
override def compare(o1: T, o2: T): Int = 0
8+
9+
// The following overriding is not allowed, because `compare`
10+
// has already been declared in Scala class `Ordering`.
11+
// class C2[T <: AnyRef] extends Ordering[T]:
12+
// override def compare(o1: T | Null, o2: T | Null): Int = 0
13+
14+
class D1[T <: AnyRef] extends Comparator[T]:
15+
override def compare(o1: T, o2: T): Int = 0
16+
17+
class D2[T <: AnyRef] extends Comparator[T]:
18+
override def compare(o1: T | Null, o2: T | Null): Int = 0

0 commit comments

Comments
 (0)