Skip to content

Commit

Permalink
Fix override containing type param
Browse files Browse the repository at this point in the history
  • Loading branch information
noti0na1 committed Nov 19, 2021
1 parent 2b37257 commit 1c56c9d
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 10 deletions.
23 changes: 23 additions & 0 deletions compiler/src/dotty/tools/dotc/core/NullOpsDecorator.scala
Expand Up @@ -49,6 +49,29 @@ object NullOpsDecorator:
val stripped = self.stripNull
stripped ne self
}

/** Strips nulls from this type deeply.
* Compaired to `stripNull`, `stripNullsDeep` will apply `stripNull` to
* each member of function types as well.
*/
def stripNullsDeep(using Context): Type =
object DeepStripNullsMap extends TypeMap:
override def apply(tp: Type): Type = tp match
case appTp @ AppliedType(tycon, targs) =>
derivedAppliedType(appTp, tycon, targs.map(this))
case ptp: PolyType =>
derivedLambdaType(ptp)(ptp.paramInfos, this(ptp.resType))
case mtp: MethodType =>
mapOver(mtp)
case tp: TypeAlias =>
mapOver(tp)
case _ =>
val tp1 = tp.stripNull
if tp1 eq tp then tp else this(tp1)
end DeepStripNullsMap

if ctx.explicitNulls then DeepStripNullsMap(self) else self

end extension

import ast.tpd._
Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/core/Types.scala
Expand Up @@ -1112,8 +1112,10 @@ object Types {
*/
def matches(that: Type)(using Context): Boolean = {
record("matches")
val thisTp1 = this.stripNullsDeep
val thatTp1 = that.stripNullsDeep
withoutMode(Mode.SafeNulls)(
TypeComparer.matchesType(this, that, relaxed = !ctx.phase.erasedTypes))
TypeComparer.matchesType(thisTp1, thatTp1, relaxed = !ctx.phase.erasedTypes))
}

/** This is the same as `matches` except that it also matches => T with T and
Expand Down
24 changes: 15 additions & 9 deletions compiler/src/dotty/tools/dotc/transform/OverridingPairs.scala
Expand Up @@ -5,6 +5,7 @@ package transform
import core._
import Flags._, Symbols._, Contexts._, Scopes._, Decorators._, Types.Type
import NameKinds.DefaultGetterName
import NullOpsDecorator._
import collection.mutable
import collection.immutable.BitSet
import scala.annotation.tailrec
Expand Down Expand Up @@ -215,15 +216,20 @@ object OverridingPairs:
}
)
else
// releaxed override check for explicit nulls if one of the symbols is Java defined,
// force `Null` being a subtype of reference types during override checking
val relaxedCtxForNulls =
val matchNullaryLoosely = member.matchNullaryLoosely || other.matchNullaryLoosely || fallBack
// default getters are not checked for compatibility
member.name.is(DefaultGetterName) || {
if ctx.explicitNulls && (member.is(JavaDefined) || other.is(JavaDefined)) then
ctx.retractMode(Mode.SafeNulls)
else ctx
member.name.is(DefaultGetterName) // default getters are not checked for compatibility
|| memberTp.overrides(otherTp,
member.matchNullaryLoosely || other.matchNullaryLoosely || fallBack
)(using relaxedCtxForNulls)
// releaxed override check for explicit nulls if one of the symbols is Java defined,
// force `Null` being a subtype of reference types during override checking.
// `stripNullsDeep` is used here because we may encounter type parameters
// (`T | Null` is not a subtype of `T` even if we retract Mode.SafeNulls).
val memberTp1 = memberTp.stripNullsDeep
val otherTp1 = otherTp.stripNullsDeep
withoutMode(Mode.SafeNulls)(
memberTp1.overrides(otherTp1, matchNullaryLoosely))
else
memberTp.overrides(otherTp, matchNullaryLoosely)
}

end OverridingPairs
18 changes: 18 additions & 0 deletions tests/explicit-nulls/pos/override-type-params.scala
@@ -0,0 +1,18 @@
// Testing relaxed overriding check for explicit nulls.
// The relaxed check is only enabled if one of the members is Java defined.

import java.util.Comparator

class C1[T <: AnyRef] extends Ordering[T]:
override def compare(o1: T, o2: T): Int = 0

// The following overriding is not allowed, because `compare`
// has already been declared in Scala class `Ordering`.
// class C2[T <: AnyRef] extends Ordering[T]:
// override def compare(o1: T | Null, o2: T | Null): Int = 0

class D1[T <: AnyRef] extends Comparator[T]:
override def compare(o1: T, o2: T): Int = 0

class D2[T <: AnyRef] extends Comparator[T]:
override def compare(o1: T | Null, o2: T | Null): Int = 0

0 comments on commit 1c56c9d

Please sign in to comment.