Skip to content

Commit

Permalink
Testing new approach
Browse files Browse the repository at this point in the history
  • Loading branch information
noti0na1 committed Feb 4, 2022
1 parent 799ccb6 commit 10f13ea
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 67 deletions.
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Mode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,6 @@ object Mode {
* This mode forces expansion of inline calls in those positions even during typing.
*/
val ForceInline: Mode = newMode(29, "ForceInline")

val RelaxedOverriding: Mode = newMode(30, "ForceInline")
}
71 changes: 25 additions & 46 deletions compiler/src/dotty/tools/dotc/core/NullOpsDecorator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,43 +9,6 @@ import Types._
/** Defines operations on nullable types and tree. */
object NullOpsDecorator:

private class StripNullsMap(isDeep: Boolean)(using Context) extends TypeMap:
def strip(tp: Type): Type = tp match
case tp @ OrType(lhs, rhs) =>
val llhs = this(lhs)
val rrhs = this(rhs)
if rrhs.isNullType then llhs
else if llhs.isNullType then rrhs
else derivedOrType(tp, llhs, rrhs)
case tp @ AndType(tp1, tp2) =>
// We cannot `tp.derivedAndType(strip(tp1), strip(tp2))` directly,
// since `stripNull((A | Null) & B)` would produce the wrong
// result `(A & B) | Null`.
val tp1s = this(tp1)
val tp2s = this(tp2)
if isDeep || (tp1s ne tp1) && (tp2s ne tp2) then
derivedAndType(tp, tp1s, tp2s)
else tp
case tp: TypeBounds =>
mapOver(tp)
case _ => tp

def stripOver(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 _ => strip(tp)

override def apply(tp: Type): Type =
val tpw = tp.widenDealias
val tpws = if isDeep then stripOver(tpw) else strip(tpw)
if tpws ne tpw then tpws else tp

end StripNullsMap

extension (self: Type)
/** Syntactically strips the nullability from this type.
* If the type is `T1 | ... | Tn`, and `Ti` references to `Null`,
Expand All @@ -54,22 +17,38 @@ object NullOpsDecorator:
* The type will not be changed if explicit-nulls is not enabled.
*/
def stripNull(using Context): Type = {
if ctx.explicitNulls then new StripNullsMap(false)(self) else self
def strip(tp: Type): Type =
val tpWiden = tp.widenDealias
val tpStripped = tpWiden match {
case tp @ OrType(lhs, rhs) =>
val llhs = strip(lhs)
val rrhs = strip(rhs)
if rrhs.isNullType then llhs
else if llhs.isNullType then rrhs
else tp.derivedOrType(llhs, rrhs)
case tp @ AndType(tp1, tp2) =>
// We cannot `tp.derivedAndType(strip(tp1), strip(tp2))` directly,
// since `stripNull((A | Null) & B)` would produce the wrong
// result `(A & B) | Null`.
val tp1s = strip(tp1)
val tp2s = strip(tp2)
if (tp1s ne tp1) && (tp2s ne tp2) then
tp.derivedAndType(tp1s, tp2s)
else tp
case tp @ TypeBounds(lo, hi) =>
tp.derivedTypeBounds(strip(lo), strip(hi))
case tp => tp
}
if tpStripped ne tpWiden then tpStripped else tp

if ctx.explicitNulls then strip(self) else self
}

/** Is self (after widening and dealiasing) a type of the form `T | Null`? */
def isNullableUnion(using Context): Boolean = {
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 =
if ctx.explicitNulls then new StripNullsMap(true)(self) else self

end extension

import ast.tpd._
Expand Down
16 changes: 9 additions & 7 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -766,13 +766,15 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling

isSubType(hi1, tp2, approx.addLow) || compareGADT || tryLiftedToThis1
case _ =>
def isNullable(tp: Type): Boolean = tp.widenDealias match {
case tp: TypeRef => tp.symbol.isNullableClass
case tp: RefinedOrRecType => isNullable(tp.parent)
case tp: AppliedType => isNullable(tp.tycon)
case AndType(tp1, tp2) => isNullable(tp1) && isNullable(tp2)
case OrType(tp1, tp2) => isNullable(tp1) || isNullable(tp2)
case _ => false
def isNullable(tp: Type): Boolean = ctx.mode.is(Mode.RelaxedOverriding) || {
tp.widenDealias match {
case tp: TypeRef => tp.symbol.isNullableClass
case tp: RefinedOrRecType => isNullable(tp.parent)
case tp: AppliedType => isNullable(tp.tycon)
case AndType(tp1, tp2) => isNullable(tp1) && isNullable(tp2)
case OrType(tp1, tp2) => isNullable(tp1) || isNullable(tp2)
case _ => false
}
}
val sym1 = tp1.symbol
(sym1 eq NothingClass) && tp2.isValueTypeOrLambda ||
Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1112,10 +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(thisTp1, thatTp1, relaxed = !ctx.phase.erasedTypes))
val overrideCtx = if ctx.explicitNulls
then ctx.retractMode(Mode.SafeNulls).addMode(Mode.RelaxedOverriding)
else ctx
TypeComparer.matchesType(this, that, relaxed = !ctx.phase.erasedTypes)(using overrideCtx)
}

/** This is the same as `matches` except that it also matches => T with T and
Expand Down
10 changes: 3 additions & 7 deletions compiler/src/dotty/tools/dotc/transform/OverridingPairs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -221,13 +221,9 @@ object OverridingPairs:
member.name.is(DefaultGetterName) || {
if ctx.explicitNulls && (member.is(JavaDefined) || other.is(JavaDefined)) then
// 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))
// force `Null` being a bottom types during override checking.
val overrideCtx = ctx.retractMode(Mode.SafeNulls).addMode(Mode.RelaxedOverriding)
memberTp.overrides(otherTp, matchNullaryLoosely)(using overrideCtx)
else
memberTp.overrides(otherTp, matchNullaryLoosely)
}
Expand Down
5 changes: 2 additions & 3 deletions compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,8 @@ object ResolveSuper {
// we use releaxed overriding check for explicit nulls if one of the symbols is Java defined.
// This forces `Null` being a subtype of reference types during override checking.
val overridesSuper = if ctx.explicitNulls && (sym.is(JavaDefined) || acc.is(JavaDefined)) then
val otherTp1 = otherTp.stripNullsDeep
val accTp1 = accTp.stripNullsDeep
withoutMode(Mode.SafeNulls)(otherTp1.overrides(accTp1, matchLoosely = true))
val overrideCtx = ctx.retractMode(Mode.SafeNulls).addMode(Mode.RelaxedOverriding)
otherTp.overrides(accTp, matchLoosely = true)(using overrideCtx)
else
otherTp.overrides(accTp, matchLoosely = true)
if !overridesSuper then
Expand Down

0 comments on commit 10f13ea

Please sign in to comment.