Skip to content

Commit f859afe

Browse files
committed
Add terminated info
1 parent d44147b commit f859afe

File tree

2 files changed

+36
-34
lines changed

2 files changed

+36
-34
lines changed

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

+28-15
Original file line numberDiff line numberDiff line change
@@ -53,37 +53,45 @@ object Nullables:
5353
TypeBoundsTree(lo, hiTree, alias)
5454

5555
/** A set of val or var references that are known to be not null
56-
* after the tree finishes executing normally (non-exceptionally),
56+
* after the tree finishes executing normally (non-exceptionally),
5757
* plus a set of variable references that are ever assigned to null,
5858
* and may therefore be null if execution of the tree is interrupted
5959
* by an exception.
6060
*/
61-
case class NotNullInfo(asserted: Set[TermRef], retracted: Set[TermRef]):
61+
case class NotNullInfo(asserted: Set[TermRef] | Null, retracted: Set[TermRef]):
6262
def isEmpty = this eq NotNullInfo.empty
6363

6464
def retractedInfo = NotNullInfo(Set(), retracted)
6565

66+
def terminatedInfo = NotNullInfo(null, retracted)
67+
6668
/** The sequential combination with another not-null info */
6769
def seq(that: NotNullInfo): NotNullInfo =
6870
if this.isEmpty then that
6971
else if that.isEmpty then this
70-
else NotNullInfo(
71-
this.asserted.diff(that.retracted).union(that.asserted),
72-
this.retracted.union(that.retracted))
72+
else
73+
val newAsserted =
74+
if this.asserted == null || that.asserted == null then null
75+
else this.asserted.diff(that.retracted).union(that.asserted)
76+
val newRetracted = this.retracted.union(that.retracted)
77+
NotNullInfo(newAsserted, newRetracted)
7378

7479
/** The alternative path combination with another not-null info. Used to merge
75-
* the nullability info of the two branches of an if.
80+
* the nullability info of the branches of an if or match.
7681
*/
7782
def alt(that: NotNullInfo): NotNullInfo =
78-
NotNullInfo(this.asserted.intersect(that.asserted), this.retracted.union(that.retracted))
79-
80-
def withRetracted(that: NotNullInfo): NotNullInfo =
81-
NotNullInfo(this.asserted, this.retracted.union(that.retracted))
83+
val newAsserted =
84+
if this.asserted == null then that.asserted
85+
else if that.asserted == null then this.asserted
86+
else this.asserted.intersect(that.asserted)
87+
val newRetracted = this.retracted.union(that.retracted)
88+
NotNullInfo(newAsserted, newRetracted)
89+
end NotNullInfo
8290

8391
object NotNullInfo:
8492
val empty = new NotNullInfo(Set(), Set())
85-
def apply(asserted: Set[TermRef], retracted: Set[TermRef]): NotNullInfo =
86-
if asserted.isEmpty && retracted.isEmpty then empty
93+
def apply(asserted: Set[TermRef] | Null, retracted: Set[TermRef]): NotNullInfo =
94+
if asserted != null && asserted.isEmpty && retracted.isEmpty then empty
8795
else new NotNullInfo(asserted, retracted)
8896
end NotNullInfo
8997

@@ -227,7 +235,7 @@ object Nullables:
227235
*/
228236
@tailrec def impliesNotNull(ref: TermRef): Boolean = infos match
229237
case info :: infos1 =>
230-
if info.asserted.contains(ref) then true
238+
if info.asserted != null && info.asserted.contains(ref) then true
231239
else if info.retracted.contains(ref) then false
232240
else infos1.impliesNotNull(ref)
233241
case _ =>
@@ -243,7 +251,9 @@ object Nullables:
243251
/** Retract all references to mutable variables */
244252
def retractMutables(using Context) =
245253
val mutables = infos.foldLeft(Set[TermRef]()):
246-
(ms, info) => ms.union(info.asserted.filter(_.symbol.is(Mutable)))
254+
(ms, info) => ms.union(
255+
if info.asserted == null then Set.empty
256+
else info.asserted.filter(_.symbol.is(Mutable)))
247257
infos.extendWith(NotNullInfo(Set(), mutables))
248258

249259
end extension
@@ -516,7 +526,10 @@ object Nullables:
516526
&& assignmentSpans.getOrElse(sym.span.start, Nil).exists(whileSpan.contains(_))
517527
&& ctx.notNullInfos.impliesNotNull(ref)
518528

519-
val retractedVars = ctx.notNullInfos.flatMap(_.asserted.filter(isRetracted)).toSet
529+
val retractedVars = ctx.notNullInfos.flatMap(info =>
530+
if info.asserted == null then Set.empty
531+
else info.asserted.filter(isRetracted)
532+
).toSet
520533
ctx.addNotNullInfo(NotNullInfo(Set(), retractedVars))
521534
end whileContext
522535

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

+8-19
Original file line numberDiff line numberDiff line change
@@ -1551,13 +1551,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
15511551

15521552
def thenPathInfo = cond1.notNullInfoIf(true).seq(result.thenp.notNullInfo)
15531553
def elsePathInfo = cond1.notNullInfoIf(false).seq(result.elsep.notNullInfo)
1554-
result.withNotNullInfo(
1555-
if result.thenp.tpe.isNothingType then
1556-
elsePathInfo.withRetracted(thenPathInfo)
1557-
else if result.elsep.tpe.isNothingType then
1558-
thenPathInfo.withRetracted(elsePathInfo)
1559-
else thenPathInfo.alt(elsePathInfo)
1560-
)
1554+
result.withNotNullInfo(thenPathInfo.alt(elsePathInfo))
15611555
end typedIf
15621556

15631557
/** Decompose function prototype into a list of parameter prototypes and a result
@@ -2154,14 +2148,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
21542148
}
21552149

21562150
private def notNullInfoFromCases(initInfo: NotNullInfo, cases: List[CaseDef])(using Context): NotNullInfo =
2157-
var nnInfo = initInfo
21582151
if cases.nonEmpty then
2159-
val (nothingCases, normalCases) = cases.partition(_.body.tpe.isNothingType)
2160-
nnInfo = nothingCases.foldLeft(nnInfo):
2161-
(nni, c) => nni.withRetracted(c.notNullInfo)
2162-
if normalCases.nonEmpty then
2163-
nnInfo = nnInfo.seq(normalCases.map(_.notNullInfo).reduce(_.alt(_)))
2164-
nnInfo
2152+
initInfo.seq(cases.map(_.notNullInfo).reduce(_.alt(_)))
2153+
else initInfo
21652154

21662155
def typedCases(cases: List[untpd.CaseDef], sel: Tree, wideSelType0: Type, pt: Type)(using Context): List[CaseDef] =
21672156
var caseCtx = ctx
@@ -2251,7 +2240,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
22512240
def typedLabeled(tree: untpd.Labeled)(using Context): Labeled = {
22522241
val bind1 = typedBind(tree.bind, WildcardType).asInstanceOf[Bind]
22532242
val expr1 = typed(tree.expr, bind1.symbol.info)
2254-
assignType(cpy.Labeled(tree)(bind1, expr1))
2243+
assignType(cpy.Labeled(tree)(bind1, expr1)).withNotNullInfo(expr1.notNullInfo.retractedInfo)
22552244
}
22562245

22572246
/** Type a case of a type match */
@@ -2301,7 +2290,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
23012290
// Hence no adaptation is possible, and we assume WildcardType as prototype.
23022291
(from, proto)
23032292
val expr1 = typedExpr(tree.expr orElse untpd.syntheticUnitLiteral.withSpan(tree.span), proto)
2304-
assignType(cpy.Return(tree)(expr1, from))
2293+
assignType(cpy.Return(tree)(expr1, from)).withNotNullInfo(expr1.notNullInfo.terminatedInfo)
23052294
end typedReturn
23062295

23072296
def typedWhileDo(tree: untpd.WhileDo)(using Context): Tree =
@@ -2388,15 +2377,15 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
23882377
def typedThrow(tree: untpd.Throw)(using Context): Tree =
23892378
val expr1 = typed(tree.expr, defn.ThrowableType)
23902379
val cap = checkCanThrow(expr1.tpe.widen, tree.span)
2391-
val res = Throw(expr1).withSpan(tree.span)
2380+
var res = Throw(expr1).withSpan(tree.span)
23922381
if Feature.ccEnabled && !cap.isEmpty && !ctx.isAfterTyper then
23932382
// Record access to the CanThrow capabulity recovered in `cap` by wrapping
23942383
// the type of the `throw` (i.e. Nothing) in a `@requiresCapability` annotation.
2395-
Typed(res,
2384+
res = Typed(res,
23962385
TypeTree(
23972386
AnnotatedType(res.tpe,
23982387
Annotation(defn.RequiresCapabilityAnnot, cap, tree.span))))
2399-
else res
2388+
res.withNotNullInfo(expr1.notNullInfo.terminatedInfo)
24002389

24012390
def typedSeqLiteral(tree: untpd.SeqLiteral, pt: Type)(using Context): SeqLiteral = {
24022391
val elemProto = pt.stripNull().elemType match {

0 commit comments

Comments
 (0)