Skip to content

Commit 9efa940

Browse files
committed
Fix overload check
1 parent a7a9230 commit 9efa940

File tree

6 files changed

+55
-51
lines changed

6 files changed

+55
-51
lines changed

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,14 @@ object NullOpsDecorator {
4545
def stripAllNulls(using Context): Type = {
4646
object RemoveNulls extends TypeMap {
4747
override def apply(tp: Type): Type =
48-
mapOver(tp.widenTermRefExpr.stripNull)
48+
val mapped = mapOver(tp.widenTermRefExpr.stripNull)
49+
tp match {
50+
case tr: TermRef =>
51+
if tp eq mapped then tp
52+
else AndType(tr, mapped)
53+
case _ =>
54+
mapped
55+
}
4956
}
5057
val rem = RemoveNulls(self)
5158
if rem ne self then rem else self

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

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,23 +37,33 @@ object ProtoTypes {
3737
* If `pt` is a by-name type, we compare against the underlying type instead.
3838
*/
3939
def isCompatible(tp: Type, pt: Type)(using Context): Boolean =
40-
(tp.widenExpr relaxed_<:< pt.widenExpr) ||
41-
viewExists(tp, pt) ||
42-
(ctx.explicitNulls &&
40+
val tpw = tp.widenExpr
41+
val ptw = pt.widenExpr
42+
(tpw relaxed_<:< ptw) ||
43+
ctx.explicitNulls &&
4344
// If unsafeNulls is enabled, we relax the condition by striping all nulls from the types
4445
// before subtype check. We use Feature to check language feature. However, when we search implicits,
4546
// the context is from ContextualImplicits; hence, we don't know whether unsafeNulls is enabled.
4647
// We have to add Mode.UnsafeNullConversion before implicit search.
4748
(config.Feature.enabled(nme.unsafeNulls) || ctx.mode.is(Mode.UnsafeNullConversion)) &&
48-
(tp.widenExpr.stripAllNulls relaxed_<:< pt.widenExpr.stripAllNulls))
49+
(tpw.stripAllNulls relaxed_<:< ptw.stripAllNulls) ||
50+
viewExists(tp, pt)
4951

5052
/** Like isCompatibe, but using a subtype comparison with necessary eithers
5153
* that don't unnecessarily truncate the constraint space, returning false instead.
5254
*/
5355
def necessarilyCompatible(tp: Type, pt: Type)(using Context): Boolean =
5456
val tpw = tp.widenExpr
5557
val ptw = pt.widenExpr
56-
necessarySubType(tpw, ptw) || tpw.isValueSubType(ptw) || viewExists(tp, pt)
58+
necessarySubType(tpw, ptw) || tpw.isValueSubType(ptw) ||
59+
ctx.explicitNulls && {
60+
val tpwsn = tpw.stripAllNulls
61+
val ptwsn = ptw.stripAllNulls
62+
// See comments in `isCompatible`
63+
(config.Feature.enabled(nme.unsafeNulls) || ctx.mode.is(Mode.UnsafeNullConversion)) &&
64+
(necessarySubType(tpwsn, ptwsn) || tpwsn.isValueSubType(ptwsn))
65+
} ||
66+
viewExists(tp, pt)
5767

5868
/** Test compatibility after normalization.
5969
* Do this in a fresh typerstate unless `keepConstraint` is true.
@@ -141,7 +151,7 @@ object ProtoTypes {
141151

142152
// equals comes from case class; no need to redefine
143153
end IgnoredProto
144-
154+
145155
final class CachedIgnoredProto(ignored: Type) extends IgnoredProto(ignored)
146156

147157
object IgnoredProto:

tests/explicit-nulls/neg/override-java-object-arg.scala

Lines changed: 0 additions & 26 deletions
This file was deleted.

tests/explicit-nulls/neg/override-java-object-arg2.scala

Lines changed: 0 additions & 13 deletions
This file was deleted.

tests/explicit-nulls/pos/override-java-object-arg-src/S.scala

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
// This test is like tests/pos/override-java-object-arg.scala, except that
32
// here we load the Java code from source, as opposed to a class file.
43
// In this case, the Java 'Object' type is turned into 'AnyRef', not 'Any'.
@@ -15,6 +14,20 @@ class S {
1514
override def handleNotification(n: Notification|Null, emitter: AnyRef|Null): Unit = {
1615
}
1716
}
18-
}
1917

18+
val listener3 = new NotificationListener() {
19+
override def handleNotification(n: Notification|Null, emitter: Object): Unit = {
20+
}
21+
}
22+
23+
val listener4 = new NotificationListener() {
24+
override def handleNotification(n: Notification, emitter: Object|Null): Unit = {
25+
}
26+
}
27+
28+
val listener5 = new NotificationListener() {
29+
override def handleNotification(n: Notification, emitter: Object): Unit = {
30+
}
31+
}
32+
}
2033
}

tests/explicit-nulls/pos/override-java-object-arg.scala

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
21
// When we load a Java class file, if a java method has an argument with type
32
// 'Object', it (the method argument) gets loaded by Dotty as 'Any' (as opposed to 'AnyRef').
43
// This is pre-explicit-nulls behaviour.
54
// There is special logic in the type comparer that allows that method to be overridden
6-
// with a corresponding argument with type 'AnyRef'.
5+
// with a corresponding argument with type 'AnyRef | Null' (or `Object | Null`).
76
// This test verifies that we can continue to override such methods, except that in
87
// the explicit nulls world we override with 'AnyRef|Null'.
98

@@ -25,6 +24,20 @@ class Foo {
2524
override def handleNotification(n: Notification|Null, emitter: AnyRef|Null): Unit = {
2625
}
2726
}
28-
}
2927

28+
val listener3 = new NotificationListener() {
29+
override def handleNotification(n: Notification|Null, emitter: Object): Unit = {
30+
}
31+
}
32+
33+
val listener4 = new NotificationListener() {
34+
override def handleNotification(n: Notification, emitter: Object|Null): Unit = {
35+
}
36+
}
37+
38+
val listener5 = new NotificationListener() {
39+
override def handleNotification(n: Notification, emitter: Object): Unit = {
40+
}
41+
}
42+
}
3043
}

0 commit comments

Comments
 (0)