Skip to content

Commit 39457c6

Browse files
committed
Update hi nullification rule
1 parent 977224f commit 39457c6

File tree

5 files changed

+41
-31
lines changed

5 files changed

+41
-31
lines changed

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ import Variances.Invariant
2424
import TastyUnpickler.NameTable
2525
import typer.ConstFold
2626
import typer.Checking.checkNonCyclic
27+
import typer.Nullables._
2728
import util.Spans._
2829
import util.SourceFile
2930
import ast.{TreeTypeMap, Trees, tpd, untpd}
3031
import Trees._
3132
import Decorators._
32-
import NullOpsDecorator._
3333
import transform.SymUtils._
3434

3535
import dotty.tools.tasty.{TastyBuffer, TastyReader}
@@ -364,11 +364,8 @@ class TreeUnpickler(reader: TastyReader,
364364
if lo.isMatch then MatchAlias(readVariances(lo))
365365
else TypeAlias(readVariances(lo))
366366
else
367-
val hi0 = readVariances(readType())
368-
val hi =
369-
if ctx.explicitNulls && lo.isBottomTypeAfterErasure && hi0.isNullableAfterErasure
370-
then OrNull(hi0) else hi0
371-
TypeBounds(lo, hi)
367+
val hi = readVariances(readType())
368+
createNullableTypeBounds(lo, hi)
372369
case ANNOTATEDtype =>
373370
AnnotatedType(readType(), Annotation(readTerm()))
374371
case ANDtype =>
@@ -1249,12 +1246,9 @@ class TreeUnpickler(reader: TastyReader,
12491246
MatchTypeTree(bound, scrut, readCases(end))
12501247
case TYPEBOUNDStpt =>
12511248
val lo = readTpt()
1252-
val hi0 = if currentAddr == end then lo else readTpt()
1253-
val hi =
1254-
if ctx.explicitNulls && lo.tpe.isBottomTypeAfterErasure && hi0.tpe.isNullableAfterErasure
1255-
then TypeTree(OrNull(hi0.tpe)) else hi0
1249+
val hi = if currentAddr == end then lo else readTpt()
12561250
val alias = if currentAddr == end then EmptyTree else readTpt()
1257-
TypeBoundsTree(lo, hi, alias)
1251+
createNullableTypeBoundsTree(lo, hi, alias)
12581252
case HOLE =>
12591253
val idx = readNat()
12601254
val tpe = readType()

compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ import printing.Printer
1818
import io.AbstractFile
1919
import util.common._
2020
import typer.Checking.checkNonCyclic
21+
import typer.Nullables._
2122
import transform.SymUtils._
2223
import PickleBuffer._
2324
import PickleFormat._
2425
import Decorators._
2526
import TypeApplications._
26-
import NullOpsDecorator._
2727
import classfile.ClassfileParser
2828
import scala.collection.mutable
2929
import scala.collection.mutable.ListBuffer
@@ -772,11 +772,8 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
772772
else tycon
773773
case TYPEBOUNDStpe =>
774774
val lo = readTypeRef()
775-
val hi0 = readTypeRef()
776-
val hi =
777-
if ctx.explicitNulls && lo.isBottomTypeAfterErasure && hi0.isNullableAfterErasure
778-
then OrNull(hi0) else hi0
779-
TypeBounds(lo, hi)
775+
val hi = readTypeRef()
776+
createNullableTypeBounds(lo, hi)
780777
case REFINEDtpe =>
781778
val clazz = readSymbolRef().asClass
782779
val decls = symScope(clazz)
@@ -1254,11 +1251,8 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
12541251

12551252
case TYPEBOUNDStree =>
12561253
val lo = readTreeRef()
1257-
val hi0 = readTreeRef()
1258-
val hi =
1259-
if ctx.explicitNulls && lo.tpe.isBottomTypeAfterErasure && hi0.tpe.isNullableAfterErasure
1260-
then TypeTree(OrNull(hi0.tpe)) else hi0
1261-
TypeBoundsTree(lo, hi)
1254+
val hi = readTreeRef()
1255+
createNullableTypeBoundsTree(lo, hi)
12621256

12631257
case EXISTENTIALTYPEtree =>
12641258
val tpt = readTreeRef()

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

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,28 @@ import ast.Trees.mods
2020
object Nullables:
2121
import ast.tpd._
2222

23-
inline def useUnsafeNullsSubTypeIf[T](cond: Boolean)(inline op: Context ?=> T)(using Context): T =
23+
private def needNullifyHi(lo: Type, hi: Type)(using Context): Boolean =
24+
ctx.explicitNulls
25+
&& lo.isBottomTypeAfterErasure
26+
&& !hi.isAny
27+
&& !hi.isBottomTypeAfterErasure
28+
&& hi.isValueType
29+
&& hi.isNullableAfterErasure
30+
31+
def createNullableTypeBounds(lo: Type, hi: Type)(using Context): TypeBounds =
32+
TypeBounds(lo,
33+
if needNullifyHi(lo, hi)
34+
then OrNull(hi)
35+
else hi)
36+
37+
def createNullableTypeBoundsTree(lo: Tree, hi: Tree, alias: Tree = EmptyTree)(using Context): TypeBoundsTree =
38+
val hiTpe = hi.typeOpt
39+
TypeBoundsTree(lo,
40+
if needNullifyHi(lo.typeOpt, hiTpe)
41+
then TypeTree(OrNull(hiTpe))
42+
else hi, alias)
43+
44+
inline def useUnsafeNullsSubTypeIf[T](cond: Boolean)(inline op: Context ?=> T)(using Context): T =
2445
val c = if cond then ctx.addMode(Mode.UnsafeNullsSubType) else ctx
2546
op(using c)
2647

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3554,7 +3554,7 @@ class Typer extends Namer
35543554
}
35553555
}
35563556

3557-
val treeTpe = tree.tpe
3557+
val treeTpe = tree.typeOpt
35583558

35593559
def tryUnsafeNullConver(fail: => Tree)(using Context): Tree =
35603560
if pt.isValueType
@@ -3579,14 +3579,15 @@ class Typer extends Namer
35793579
else ctx
35803580

35813581
inContext(searchCtx) {
3582-
if ctx.mode.is(Mode.ImplicitsEnabled) && tree.typeOpt.isValueType then
3583-
if pt.isRef(defn.AnyValClass) || pt.isRef(defn.ObjectClass) then
3584-
// We want to allow `null` to `AnyRef` if UnsafeNullConversion is enabled
3585-
if !(useUnsafeNullsSubTypeIf(ctx.mode.is(Mode.UnsafeNullConversion))(
3586-
treeTpe <:< pt)) then
3587-
report.error(em"the result of an implicit conversion must be more specific than $pt", tree.srcPos)
3582+
if ctx.mode.is(Mode.ImplicitsEnabled) && treeTpe.isValueType then
3583+
if ctx.mode.is(Mode.UnsafeNullConversion)
3584+
&& pt.isValueType
3585+
&& (treeTpe.isNullableAfterErasure && pt.isRef(defn.ObjectClass)
3586+
|| treeTpe.isNullType && pt.isNullableAfterErasure) then
35883587
tree.cast(pt)
35893588
else
3589+
if pt.isRef(defn.AnyValClass) || pt.isRef(defn.ObjectClass) then
3590+
report.error(em"the result of an implicit conversion must be more specific than $pt", tree.srcPos)
35903591
searchTree(tree)(failure => tryUnsafeNullConver(cannotFind(failure)))
35913592
else tryUnsafeNullConver(recover(NoMatchingImplicits))
35923593
}

tests/explicit-nulls/unsafe-common/unsafe-eq.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
val s1: String = ???
2-
val s2: String | Null = ???
2+
val s2: String | Null = ???
33

44
def f = {
55
s1 eq s2 // error

0 commit comments

Comments
 (0)