Skip to content

Commit

Permalink
Fix deep NotNullInfo
Browse files Browse the repository at this point in the history
  • Loading branch information
noti0na1 committed Dec 6, 2024
1 parent f859afe commit 158af7d
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 20 deletions.
7 changes: 6 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1134,7 +1134,7 @@ trait Applications extends Compatibility {
case _ => ()
else ()

fun1.tpe match {
val result = fun1.tpe match {
case err: ErrorType => cpy.Apply(tree)(fun1, proto.typedArgs()).withType(err)
case TryDynamicCallType =>
val isInsertedApply = fun1 match {
Expand Down Expand Up @@ -1208,6 +1208,11 @@ trait Applications extends Compatibility {
else tryWithImplicitOnQualifier(fun1, proto).getOrElse(fail))
}
}

if result.tpe.isNothingType then
val nnInfo = result.notNullInfo
result.withNotNullInfo(nnInfo.terminatedInfo)
else result
}

/** Convert expression like
Expand Down
54 changes: 37 additions & 17 deletions compiler/src/dotty/tools/dotc/typer/Nullables.scala
Original file line number Diff line number Diff line change
Expand Up @@ -319,11 +319,29 @@ object Nullables:
if !info.isEmpty then tree.putAttachment(NNInfo, info)
tree

/* Collect the nullability info from parts of `tree` */
def collectNotNullInfo(using Context): NotNullInfo = tree match
case Typed(expr, _) =>
expr.notNullInfo
case Apply(fn, args) =>
val argsInfo = args.map(_.notNullInfo)
val fnInfo = fn.notNullInfo
argsInfo.foldLeft(fnInfo)(_ seq _)
case TypeApply(fn, _) =>
fn.notNullInfo
case _ =>
// Other cases are handled specially in typer.
NotNullInfo.empty

/* The nullability info of `tree` */
def notNullInfo(using Context): NotNullInfo =
stripInlined(tree).getAttachment(NNInfo) match
val tree1 = stripInlined(tree)
tree1.getAttachment(NNInfo) match
case Some(info) if !ctx.erasedTypes => info
case _ => NotNullInfo.empty
case _ =>
val nnInfo = tree1.collectNotNullInfo
tree1.withNotNullInfo(nnInfo)
nnInfo

/* The nullability info of `tree`, assuming it is a condition that evaluates to `c` */
def notNullInfoIf(c: Boolean)(using Context): NotNullInfo =
Expand Down Expand Up @@ -404,21 +422,23 @@ object Nullables:
end extension

extension (tree: Assign)
def computeAssignNullable()(using Context): tree.type = tree.lhs match
case TrackedRef(ref) =>
val rhstp = tree.rhs.typeOpt
if ctx.explicitNulls && ref.isNullableUnion then
if rhstp.isNullType || rhstp.isNullableUnion then
// If the type of rhs is nullable (`T|Null` or `Null`), then the nullability of the
// lhs variable is no longer trackable. We don't need to check whether the type `T`
// is correct here, as typer will check it.
tree.withNotNullInfo(NotNullInfo(Set(), Set(ref)))
else
// If the initial type is nullable and the assigned value is non-null,
// we add it to the NotNull.
tree.withNotNullInfo(NotNullInfo(Set(ref), Set()))
else tree
case _ => tree
def computeAssignNullable()(using Context): tree.type =
var nnInfo = tree.rhs.notNullInfo
tree.lhs match
case TrackedRef(ref) if ctx.explicitNulls && ref.isNullableUnion =>
nnInfo = nnInfo.seq:
val rhstp = tree.rhs.typeOpt
if rhstp.isNullType || rhstp.isNullableUnion then
// If the type of rhs is nullable (`T|Null` or `Null`), then the nullability of the
// lhs variable is no longer trackable. We don't need to check whether the type `T`
// is correct here, as typer will check it.
NotNullInfo(Set(), Set(ref))
else
// If the initial type is nullable and the assigned value is non-null,
// we add it to the NotNull.
NotNullInfo(Set(ref), Set())
case _ =>
tree.withNotNullInfo(nnInfo)
end extension

private val analyzedOps = Set(nme.EQ, nme.NE, nme.eq, nme.ne, nme.ZAND, nme.ZOR, nme.UNARY_!)
Expand Down
1 change: 0 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1201,7 +1201,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
untpd.unsplice(tree.expr).putAttachment(AscribedToUnit, ())
typed(tree.expr, underlyingTreeTpe.tpe.widenSkolem)
assignType(cpy.Typed(tree)(expr1, tpt), underlyingTreeTpe)
.withNotNullInfo(expr1.notNullInfo)
}

if (untpd.isWildcardStarArg(tree)) {
Expand Down
15 changes: 14 additions & 1 deletion tests/explicit-nulls/neg/i21619.scala
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,17 @@ def test5: Unit =
catch
case _ =>
val z1: String = x.replace("", "") // error
val z2: String = y.replace("", "")
val z2: String = y.replace("", "")

def test6 = {
var x: String | Null = ""
var y: String = ""
x = ""
y = if (false) x else 1 match {
case _ => {
x = null
y
}
}
x.replace("", "") // error
}

0 comments on commit 158af7d

Please sign in to comment.