Skip to content

Nullability Analysis without NotNull #7556

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 37 commits into from
Nov 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
6e271a7
Thread context through typedStats
odersky Nov 8, 2019
435024a
Nullability prototype
odersky Nov 8, 2019
06f72db
Add extractor for comparisons of null and a path
odersky Nov 8, 2019
d904a3c
Generalize expression purity
odersky Nov 8, 2019
6a7c740
Simplify more if-expressions in FirstTransform
odersky Nov 8, 2019
e96e027
Maintain constant types of applications in TimeTravellingTreeCopier
odersky Nov 8, 2019
d1981f8
Constant fold null tests
odersky Nov 8, 2019
16cc066
Fix handling of negation for nullability
odersky Nov 8, 2019
a38574c
Skip also Inlined nodes with skipBlock
odersky Nov 8, 2019
1910c87
Always compute nullable info unless ersedTypes
odersky Nov 8, 2019
a691e96
Recompute nullability info when inlining
odersky Nov 8, 2019
691996a
Tweak assert so that nullability info is propagated correctly
odersky Nov 8, 2019
94d05a4
Exclude nullable.scala from pickling and fromTasy tests
odersky Nov 8, 2019
27c92a9
Disable sjsJUnitTests
odersky Nov 8, 2019
cb6ff0c
Move null comparison extractors into Nullables
odersky Nov 9, 2019
0d2a0f0
Track nullability in pattern matches
odersky Nov 9, 2019
b20cae8
Polishings
odersky Nov 9, 2019
2d5007c
Allow retracting a not null status
odersky Nov 10, 2019
10896a1
Nullables refactorings
odersky Nov 10, 2019
5b46483
Account for side effects in conditions
odersky Nov 10, 2019
5cb0873
Track assignments for nullability
odersky Nov 10, 2019
6d5ed25
Allow local variables to be tracked for nullability
odersky Nov 10, 2019
74d5b1e
Drop debug output
odersky Nov 10, 2019
f1e2863
Constant fold null comparisons only under -Yexplicit-nulls
odersky Nov 10, 2019
ed00d96
Handle while expressions correctly for nullability
odersky Nov 11, 2019
926b08e
Don't widen T | Null in widenUnion
odersky Nov 11, 2019
947c49e
Introduce NotNull type
odersky Nov 12, 2019
62b756b
Simplify in widenRHS
odersky Nov 12, 2019
e6746e5
Special treatment of NotNull in TypeComparer
odersky Nov 12, 2019
f32f22a
Temporary hack to support testing
odersky Nov 12, 2019
e79dc49
Test case
odersky Nov 12, 2019
e56743b
Blacklist notNull as from Tasty test
odersky Nov 12, 2019
84c0fdf
Revert "Disable sjsJUnitTests"
odersky Nov 13, 2019
b32e30a
Address review suggestions
odersky Nov 13, 2019
1726660
Add notNull member to Any
odersky Nov 14, 2019
434204f
Use a definition of `def notNull` that does not require `NotNull`.
sjrd Nov 14, 2019
016f471
Revert all the changes that added NonNull.
sjrd Nov 14, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions compiler/src/dotty/tools/dotc/CompilationUnit.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import util.SourceFile
import ast.{tpd, untpd}
import tpd.{Tree, TreeTraverser}
import typer.PrepareInlineable.InlineAccessors
import typer.Nullables
import dotty.tools.dotc.core.Contexts.Context
import dotty.tools.dotc.core.SymDenotations.ClassDenotation
import dotty.tools.dotc.core.Symbols._
import dotty.tools.dotc.transform.SymUtils._
import util.{NoSource, SourceFile}
import util.Spans.Span
import core.Decorators._

class CompilationUnit protected (val source: SourceFile) {
Expand Down Expand Up @@ -42,6 +44,16 @@ class CompilationUnit protected (val source: SourceFile) {
suspended = true
ctx.run.suspendedUnits += this
throw CompilationUnit.SuspendException()

private var myAssignmentSpans: Map[Int, List[Span]] = null

/** A map from (name-) offsets of all local variables in this compilation unit
* that can be tracked for being not null to the list of spans of assignments
* to these variables.
*/
def assignmentSpans(given Context): Map[Int, List[Span]] =
if myAssignmentSpans == null then myAssignmentSpans = Nullables.assignmentSpans
myAssignmentSpans
}

object CompilationUnit {
Expand Down
10 changes: 9 additions & 1 deletion compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
/** If this is a block, its expression part */
def stripBlock(tree: Tree): Tree = unsplice(tree) match {
case Block(_, expr) => stripBlock(expr)
case Inlined(_, _, expr) => stripBlock(expr)
case _ => tree
}

def stripInlined(tree: Tree): Tree = unsplice(tree) match {
case Inlined(_, _, expr) => stripInlined(expr)
case _ => tree
}

Expand Down Expand Up @@ -391,7 +397,9 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
if (fn.symbol.is(Erased) || fn.symbol == defn.InternalQuoted_typeQuote) Pure else exprPurity(fn)
case Apply(fn, args) =>
def isKnownPureOp(sym: Symbol) =
sym.owner.isPrimitiveValueClass || sym.owner == defn.StringClass
sym.owner.isPrimitiveValueClass
|| sym.owner == defn.StringClass
|| defn.pureMethods.contains(sym)
if (tree.tpe.isInstanceOf[ConstantType] && isKnownPureOp(tree.symbol) // A constant expression with pure arguments is pure.
|| (fn.symbol.isStableMember && !fn.symbol.is(Lazy))
|| fn.symbol.isPrimaryConstructor && fn.symbol.owner.isNoInitsClass) // TODO: include in isStable?
Expand Down
14 changes: 11 additions & 3 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -711,11 +711,19 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {

class TimeTravellingTreeCopier extends TypedTreeCopier {
override def Apply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): Apply =
ta.assignType(untpdCpy.Apply(tree)(fun, args), fun, args)
tree match
case tree: Apply
if (tree.fun eq fun) && (tree.args eq args)
&& tree.tpe.isInstanceOf[ConstantType]
&& isPureExpr(tree) => tree
case _ =>
ta.assignType(untpdCpy.Apply(tree)(fun, args), fun, args)
// Note: Reassigning the original type if `fun` and `args` have the same types as before
// does not work here: The computed type depends on the widened function type, not
// the function type itself. A treetransform may keep the function type the
// does not work here in general: The computed type depends on the widened function type, not
// the function type itself. A tree transform may keep the function type the
// same but its widened type might change.
// However, we keep constant types of pure expressions. This uses the underlying assumptions
// that pure functions yielding a constant will not change in later phases.

override def TypeApply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): TypeApply =
ta.assignType(untpdCpy.TypeApply(tree)(fun, args), fun, args)
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ class ScalaSettings extends Settings.SettingGroup {

// Extremely experimental language features
val YnoKindPolymorphism: Setting[Boolean] = BooleanSetting("-Yno-kind-polymorphism", "Enable kind polymorphism (see https://dotty.epfl.ch/docs/reference/kind-polymorphism.html). Potentially unsound.")
val YexplicitNulls: Setting[Boolean] = BooleanSetting("-Yexplicit-nulls", "Make reference types non-nullable. Nullable types can be expressed with unions: e.g. String|Null.")

/** Area-specific debug output */
val YexplainLowlevel: Setting[Boolean] = BooleanSetting("-Yexplain-lowlevel", "When explaining type errors, show types at a lower level.")
Expand Down
28 changes: 25 additions & 3 deletions compiler/src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import ast.Trees._
import ast.untpd
import Flags.GivenOrImplicit
import util.{FreshNameCreator, NoSource, SimpleIdentityMap, SourceFile}
import typer.{Implicits, ImportInfo, Inliner, NamerContextOps, SearchHistory, SearchRoot, TypeAssigner, Typer}
import typer.{Implicits, ImportInfo, Inliner, NamerContextOps, SearchHistory, SearchRoot, TypeAssigner, Typer, Nullables}
import Nullables.{NotNullInfo, given}
import Implicits.ContextualImplicits
import config.Settings._
import config.Config
Expand Down Expand Up @@ -47,7 +48,11 @@ object Contexts {
private val (compilationUnitLoc, store6) = store5.newLocation[CompilationUnit]()
private val (runLoc, store7) = store6.newLocation[Run]()
private val (profilerLoc, store8) = store7.newLocation[Profiler]()
private val initialStore = store8
private val (notNullInfosLoc, store9) = store8.newLocation[List[NotNullInfo]]()
private val initialStore = store9

/** The current context */
def curCtx(given ctx: Context): Context = ctx

/** A context is passed basically everywhere in dotc.
* This is convenient but carries the risk of captured contexts in
Expand Down Expand Up @@ -207,6 +212,9 @@ object Contexts {
/** The current compiler-run profiler */
def profiler: Profiler = store(profilerLoc)

/** The paths currently known to be not null */
def notNullInfos = store(notNullInfosLoc)

/** The new implicit references that are introduced by this scope */
protected var implicitsCache: ContextualImplicits = null
def implicits: ContextualImplicits = {
Expand Down Expand Up @@ -556,6 +564,7 @@ object Contexts {
def setRun(run: Run): this.type = updateStore(runLoc, run)
def setProfiler(profiler: Profiler): this.type = updateStore(profilerLoc, profiler)
def setFreshNames(freshNames: FreshNameCreator): this.type = updateStore(freshNamesLoc, freshNames)
def setNotNullInfos(notNullInfos: List[NotNullInfo]): this.type = updateStore(notNullInfosLoc, notNullInfos)

def setProperty[T](key: Key[T], value: T): this.type =
setMoreProperties(moreProperties.updated(key, value))
Expand Down Expand Up @@ -587,6 +596,17 @@ object Contexts {
def setDebug: this.type = setSetting(base.settings.Ydebug, true)
}

given (c: Context)
def addNotNullInfo(info: NotNullInfo) =
c.withNotNullInfos(c.notNullInfos.extendWith(info))

def addNotNullRefs(refs: Set[TermRef]) =
c.addNotNullInfo(NotNullInfo(refs, Set()))

def withNotNullInfos(infos: List[NotNullInfo]): Context =
if c.notNullInfos eq infos then c else c.fresh.setNotNullInfos(infos)

// TODO: Fix issue when converting ModeChanges and FreshModeChanges to extension givens
implicit class ModeChanges(val c: Context) extends AnyVal {
final def withModeBits(mode: Mode): Context =
if (mode != c.mode) c.fresh.setMode(mode) else c
Expand Down Expand Up @@ -615,7 +635,9 @@ object Contexts {
typeAssigner = TypeAssigner
moreProperties = Map.empty
source = NoSource
store = initialStore.updated(settingsStateLoc, settingsGroup.defaultState)
store = initialStore
.updated(settingsStateLoc, settingsGroup.defaultState)
.updated(notNullInfosLoc, Nil)
typeComparer = new TypeComparer(this)
searchHistory = new SearchRoot
gadt = EmptyGadtConstraint
Expand Down
4 changes: 4 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,10 @@ class Definitions {
def ObjectMethods: List[TermSymbol] = List(Object_eq, Object_ne, Object_synchronized, Object_clone,
Object_finalize, Object_notify, Object_notifyAll, Object_wait, Object_waitL, Object_waitLI)

/** Methods in Object and Any that do not have a side effect */
@tu lazy val pureMethods: List[TermSymbol] = List(Any_==, Any_!=, Any_equals, Any_hashCode,
Any_toString, Any_##, Any_getClass, Any_isInstanceOf, Any_typeTest, Object_eq, Object_ne)

@tu lazy val AnyKindClass: ClassSymbol = {
val cls = ctx.newCompleteClassSymbol(ScalaPackageClass, tpnme.AnyKind, AbstractFinal | Permanent, Nil)
if (!ctx.settings.YnoKindPolymorphism.value)
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ object StdNames {
final val ExprApi: N = "ExprApi"
final val Mirror: N = "Mirror"
final val Nothing: N = "Nothing"
final val NotNull: N = "NotNull"
final val Null: N = "Null"
final val Object: N = "Object"
final val Product: N = "Product"
Expand Down Expand Up @@ -261,6 +262,7 @@ object StdNames {
val MIRROR_PREFIX: N = "$m."
val MIRROR_SHORT: N = "$m"
val MIRROR_UNTYPED: N = "$m$untyped"
val NOT_NULL: N = "$nn"
val REIFY_FREE_PREFIX: N = "free$"
val REIFY_FREE_THIS_SUFFIX: N = "$this"
val REIFY_FREE_VALUE_SUFFIX: N = "$value"
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1838,7 +1838,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
else if (!tp2.exists) tp1
else tp.derivedAndType(tp1, tp2)

/** If some (&-operand of) this type is a supertype of `sub` replace it with `NoType`.
/** If some (&-operand of) `tp` is a supertype of `sub` replace it with `NoType`.
*/
private def dropIfSuper(tp: Type, sub: Type): Type =
if (isSubTypeWhenFrozen(sub, tp)) NoType
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
// called which we override to set the `approximated` flag.
range(defn.NothingType, pre)
else pre
else if ((pre.termSymbol is Package) && !(thiscls is Package))
else if (pre.termSymbol.is(Package) && !thiscls.is(Package))
toPrefix(pre.select(nme.PACKAGE), cls, thiscls)
else
toPrefix(pre.baseType(cls).normalizedPrefix, cls.owner, thiscls)
Expand Down
30 changes: 27 additions & 3 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1068,8 +1068,9 @@ object Types {
* instead of `ArrayBuffer[? >: Int | A <: Int & A]`
*/
def widenUnion(implicit ctx: Context): Type = widen match {
case OrType(tp1, tp2) =>
ctx.typeComparer.lub(tp1.widenUnion, tp2.widenUnion, canConstrain = true) match {
case tp @ OrType(tp1, tp2) =>
if tp1.isNull || tp2.isNull then tp
else ctx.typeComparer.lub(tp1.widenUnion, tp2.widenUnion, canConstrain = true) match {
case union: OrType => union.join
case res => res
}
Expand Down Expand Up @@ -1399,6 +1400,11 @@ object Types {
case _ => true
}

/** Is this (an alias of) the `scala.Null` type? */
final def isNull(given Context) =
isRef(defn.NullClass)
|| classSymbol.name == tpnme.Null // !!! temporary kludge for being able to test without the explicit nulls PR

/** The resultType of a LambdaType, or ExprType, the type itself for others */
def resultType(implicit ctx: Context): Type = this

Expand Down Expand Up @@ -2293,7 +2299,7 @@ object Types {
}

/** The singleton type for path prefix#myDesignator.
*/
*/
abstract case class TermRef(override val prefix: Type,
private var myDesignator: Designator)
extends NamedType with SingletonType with ImplicitRef {
Expand Down Expand Up @@ -2886,6 +2892,24 @@ object Types {
else apply(tp1, tp2)
}

/** An extractor for `T | Null` or `Null | T`, returning the `T` */
object OrNull with
private def stripNull(tp: Type)(given Context): Type = tp match
case tp @ OrType(tp1, tp2) =>
if tp1.isNull then tp2
else if tp2.isNull then tp1
else tp.derivedOrType(stripNull(tp1), stripNull(tp2))
case tp @ AndType(tp1, tp2) =>
tp.derivedAndType(stripNull(tp1), stripNull(tp2))
case _ =>
tp
def apply(tp: Type)(given Context) =
OrType(tp, defn.NullType)
def unapply(tp: Type)(given Context): Option[Type] =
val tp1 = stripNull(tp)
if tp1 ne tp then Some(tp1) else None
end OrNull

// ----- ExprType and LambdaTypes -----------------------------------

// Note: method types are cached whereas poly types are not. The reason
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/interactive/Completion.scala
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ object Completion {
def addMemberCompletions(qual: Tree)(implicit ctx: Context): Unit =
if (!qual.tpe.widenDealias.isBottomType) {
addAccessibleMembers(qual.tpe)
if (!mode.is(Mode.Import) && !qual.tpe.isRef(defn.NullClass))
if (!mode.is(Mode.Import) && !qual.tpe.isNull)
// Implicit conversions do not kick in when importing
// and for `NullClass` they produce unapplicable completions (for unclear reasons)
implicitConversionTargets(qual)(ctx.fresh.setExploreTyperState())
Expand Down
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/Erasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -740,11 +740,12 @@ object Erasure {
override def typedAnnotated(tree: untpd.Annotated, pt: Type)(implicit ctx: Context): Tree =
typed(tree.arg, pt)

override def typedStats(stats: List[untpd.Tree], exprOwner: Symbol)(implicit ctx: Context): List[Tree] = {
override def typedStats(stats: List[untpd.Tree], exprOwner: Symbol)(implicit ctx: Context): (List[Tree], Context) = {
val stats1 =
if (takesBridges(ctx.owner)) new Bridges(ctx.owner.asClass, erasurePhase).add(stats)
else stats
super.typedStats(stats1, exprOwner).filter(!_.isEmpty)
val (stats2, finalCtx) = super.typedStats(stats1, exprOwner)
(stats2.filter(!_.isEmpty), finalCtx)
}

override def adapt(tree: Tree, pt: Type, locked: TypeVars)(implicit ctx: Context): Tree =
Expand Down
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/FirstTransform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,9 @@ class FirstTransform extends MiniPhase with InfoTransformer { thisPhase =>
constToLiteral(tree)

override def transformIf(tree: If)(implicit ctx: Context): Tree =
tree.cond match {
case Literal(Constant(c: Boolean)) => if (c) tree.thenp else tree.elsep
tree.cond.tpe match {
case ConstantType(Constant(c: Boolean)) if isPureExpr(tree.cond) =>
if (c) tree.thenp else tree.elsep
case _ => tree
}

Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/transform/TreeChecker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -434,9 +434,9 @@ class TreeChecker extends Phase with SymTransformer {
}
}

override def typedCase(tree: untpd.CaseDef, selType: Type, pt: Type)(implicit ctx: Context): CaseDef =
override def typedCase(tree: untpd.CaseDef, sel: Tree, selType: Type, pt: Type)(implicit ctx: Context): CaseDef =
withPatSyms(tpd.patVars(tree.pat.asInstanceOf[tpd.Tree])) {
super.typedCase(tree, selType, pt)
super.typedCase(tree, sel, selType, pt)
}

override def typedClosure(tree: untpd.Closure, pt: Type)(implicit ctx: Context): Tree = {
Expand Down Expand Up @@ -466,7 +466,7 @@ class TreeChecker extends Phase with SymTransformer {
* is that we should be able to pull out an expression as an initializer
* of a helper value without having to do a change owner traversal of the expression.
*/
override def typedStats(trees: List[untpd.Tree], exprOwner: Symbol)(implicit ctx: Context): List[Tree] = {
override def typedStats(trees: List[untpd.Tree], exprOwner: Symbol)(implicit ctx: Context): (List[Tree], Context) = {
for (tree <- trees) tree match {
case tree: untpd.DefTree => checkOwner(tree)
case _: untpd.Thicket => assert(false, i"unexpanded thicket $tree in statement sequence $trees%\n%")
Expand Down
6 changes: 4 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import NameKinds.DefaultGetterName
import ProtoTypes._
import Inferencing._
import transform.TypeUtils._
import Nullables.given

import collection.mutable
import config.Printers.{overload, typr, unapp}
Expand Down Expand Up @@ -864,8 +865,9 @@ trait Applications extends Compatibility {
if (proto.allArgTypesAreCurrent())
new ApplyToTyped(tree, fun1, funRef, proto.unforcedTypedArgs, pt)
else
new ApplyToUntyped(tree, fun1, funRef, proto, pt)(argCtx(tree))
convertNewGenericArray(app.result)
new ApplyToUntyped(tree, fun1, funRef, proto, pt)(
given fun1.nullableInArgContext(given argCtx(tree)))
convertNewGenericArray(app.result).computeNullable()
case _ =>
handleUnexpectedFunType(tree, fun1)
}
Expand Down
11 changes: 7 additions & 4 deletions compiler/src/dotty/tools/dotc/typer/ConstFold.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Constants._
import Names._
import StdNames._
import Contexts._
import Nullables.{CompareNull, TrackedRef}

object ConstFold {

Expand All @@ -19,15 +20,17 @@ object ConstFold {
/** If tree is a constant operation, replace with result. */
def apply[T <: Tree](tree: T)(implicit ctx: Context): T = finish(tree) {
tree match {
case CompareNull(TrackedRef(ref), testEqual)
if ctx.settings.YexplicitNulls.value && ctx.notNullInfos.impliesNotNull(ref) =>
// TODO maybe drop once we have general Nullability?
Constant(!testEqual)
case Apply(Select(xt, op), yt :: Nil) =>
xt.tpe.widenTermRefExpr.normalized match {
xt.tpe.widenTermRefExpr.normalized match
case ConstantType(x) =>
yt.tpe.widenTermRefExpr match {
yt.tpe.widenTermRefExpr match
case ConstantType(y) => foldBinop(op, x, y)
case _ => null
}
case _ => null
}
case Select(xt, op) =>
xt.tpe.widenTermRefExpr match {
case ConstantType(x) => foldUnop(op, x)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Docstrings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ object Docstrings {
expandComment(sym).map { expanded =>
val typedUsecases = expanded.usecases.map { usecase =>
ctx.typer.enterSymbol(ctx.typer.createSymbol(usecase.untpdCode))
ctx.typer.typedStats(usecase.untpdCode :: Nil, owner) match {
ctx.typer.typedStats(usecase.untpdCode :: Nil, owner)._1 match {
case List(df: tpd.DefDef) =>
usecase.typed(df)
case _ =>
Expand Down
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import ErrorReporting.errorTree
import dotty.tools.dotc.tastyreflect.ReflectionImpl
import dotty.tools.dotc.util.{SimpleIdentityMap, SimpleIdentitySet, SourceFile, SourcePosition}
import dotty.tools.dotc.parsing.Parsers.Parser
import Nullables.given

import collection.mutable
import reporting.trace
Expand Down Expand Up @@ -1064,10 +1065,10 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
errorTree(tree, em"""cannot reduce inline if
| its condition ${tree.cond}
| is not a constant value""")
else {
else
cond1.computeNullableDeeply()
val if1 = untpd.cpy.If(tree)(cond = untpd.TypedSplice(cond1))
super.typedIf(if1, pt)
}
}

override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree =
Expand Down
Loading