@@ -2436,8 +2436,31 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
24362436 val capabilityProof = caughtExceptions.reduce(OrType (_, _, true ))
24372437 untpd.Block (makeCanThrow(capabilityProof), expr)
24382438
2439+ /** Graphic explaination of NotNullInfo logic:
2440+ * Leftward exit indicates exceptional case
2441+ * Downward exit indicates normal case
2442+ *
2443+ * ┌─────┐ α.retractedInfo
2444+ * │ Try ├─────┬────────┬─────┐
2445+ * └──┬──┘ ▼ ▼ │
2446+ * │ ┌───────┐┌───────┐ │
2447+ * α │ │ Catch ││ Catch ├─┤
2448+ * │ └───┬───┘└───┬───┘ │
2449+ * │ β │ │ │
2450+ * └──┬─────┴────────┘ │
2451+ * │ γ = α.alt(β) │ ε = β.retractedInfo
2452+ * ▼ ▼
2453+ * ┌─────────┐ ┌─────────┐
2454+ * δ = type(ε)│ Finally ├──────┐ │ Finally ├─────────┐
2455+ * └────┬────┘ │ └────┬────┘ │
2456+ * │ │ ▼ ▼
2457+ * │ α.seq(γ) └──────────────────────────►
2458+ * ▼ ε.seq(δ)
2459+ * We choose to use α.seq(γ) as the NotNullInfo of the
2460+ * overall tree if all the catch cases have NothingType,
2461+ * otherwise we use ε.seq(δ).
2462+ */
24392463 def typedTry (tree : untpd.Try , pt : Type )(using Context ): Try =
2440- var nnInfo = NotNullInfo .empty
24412464 val expr2 :: cases2x = harmonic(harmonize, pt) {
24422465 // We want to type check tree.expr first to comput NotNullInfo, but `addCanThrowCapabilities`
24432466 // uses the types of patterns in `tree.cases` to determine the capabilities.
@@ -2450,25 +2473,31 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
24502473 val casesEmptyBody2 = typedCases(casesEmptyBody1, EmptyTree , defn.ThrowableType , WildcardType )
24512474 val expr1 = typed(addCanThrowCapabilities(tree.expr, casesEmptyBody2), pt.dropIfProto)
24522475
2453- // Since we don't know at which point the the exception is thrown in the body,
2454- // we have to collect any reference that is once retracted.
2455- nnInfo = expr1.notNullInfo.retractedInfo
2456-
2457- val casesCtx = ctx.addNotNullInfo(nnInfo)
2476+ val casesCtx = ctx.addNotNullInfo(expr1.notNullInfo.retractedInfo)
24582477 val cases1 = typedCases(tree.cases, EmptyTree , defn.ThrowableType , pt.dropIfProto)(using casesCtx)
24592478 expr1 :: cases1
24602479 }: @ unchecked
24612480 val cases2 = cases2x.asInstanceOf [List [CaseDef ]]
2462-
2481+ val tryNNInfo = expr2.notNullInfo
24632482 // It is possible to have non-exhaustive cases, and some exceptions are thrown and not caught.
24642483 // Therefore, the code in the finalizer and after the try block can only rely on the retracted
24652484 // info from the cases' body.
2466- if cases2.nonEmpty then
2467- nnInfo = nnInfo.seq(cases2.map(_.notNullInfo.retractedInfo).reduce(_.alt(_)))
2468-
2469- val finalizer1 = typed(tree.finalizer, defn.UnitType )(using ctx.addNotNullInfo(nnInfo))
2470- nnInfo = nnInfo.seq(finalizer1.notNullInfo)
2471- assignType(cpy.Try (tree)(expr2, cases2, finalizer1), expr2, cases2).withNotNullInfo(nnInfo)
2485+ val catchNNInfo = if cases2.nonEmpty then
2486+ tryNNInfo.seq(cases2.map(_.notNullInfo).reduce(_.alt(_)))
2487+ else
2488+ NotNullInfo .empty
2489+ val normalResolveNNInfo = tryNNInfo.alt(catchNNInfo)
2490+ val exceptionalResolveNNInfo = catchNNInfo.retractedInfo
2491+
2492+ val finalizer1 = typed(tree.finalizer, defn.UnitType )(using ctx.addNotNullInfo(exceptionalResolveNNInfo))
2493+ val normalFinalNNInfo = normalResolveNNInfo.seq(finalizer1.notNullInfo)
2494+ val exceptionalFinalNNInfo = exceptionalResolveNNInfo.seq(finalizer1.notNullInfo)
2495+ val resNNInfo =
2496+ if (cases2.forall(_.tpe == defn.NothingType )) then
2497+ normalFinalNNInfo
2498+ else
2499+ exceptionalFinalNNInfo
2500+ assignType(cpy.Try (tree)(expr2, cases2, finalizer1), expr2, cases2).withNotNullInfo(resNNInfo)
24722501
24732502 def typedTry (tree : untpd.ParsedTry , pt : Type )(using Context ): Try =
24742503 val cases : List [untpd.CaseDef ] = tree.handler match
0 commit comments