diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index 25151126eb20..caca90bad586 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -16,6 +16,7 @@ import typer.Nullables import transform.SymUtils._ import core.Decorators._ import config.SourceVersion +import scala.annotation.internal.sharable class CompilationUnit protected (val source: SourceFile) { @@ -89,15 +90,24 @@ class CompilationUnit protected (val source: SourceFile) { myAssignmentSpans.nn } +@sharable object NoCompilationUnit extends CompilationUnit(NoSource) { + + override def isJava: Boolean = false + + override def suspend()(using Context): Nothing = + throw CompilationUnit.SuspendException() + + override def assignmentSpans(using Context): Map[Int, List[Span]] = Map.empty +} + object CompilationUnit { class SuspendException extends Exception /** Make a compilation unit for top class `clsd` with the contents of the `unpickled` tree */ def apply(clsd: ClassDenotation, unpickled: Tree, forceTrees: Boolean)(using Context): CompilationUnit = - val file = clsd.symbol.associatedFile - // TODO: could file be null? - apply(new SourceFile(file.nn, Array.empty[Char]), unpickled, forceTrees) + val file = clsd.symbol.associatedFile.nn + apply(new SourceFile(file, Array.empty[Char]), unpickled, forceTrees) /** Make a compilation unit, given picked bytes and unpickled tree */ def apply(source: SourceFile, unpickled: Tree, forceTrees: Boolean)(using Context): CompilationUnit = { diff --git a/compiler/src/dotty/tools/dotc/Driver.scala b/compiler/src/dotty/tools/dotc/Driver.scala index 311060f49aa3..f6d9df7d3822 100644 --- a/compiler/src/dotty/tools/dotc/Driver.scala +++ b/compiler/src/dotty/tools/dotc/Driver.scala @@ -42,10 +42,7 @@ class Driver { finish(compiler, run) catch case ex: FatalError => - val msg = ex.getMessage - if msg != null then - report.error(msg) // signals that we should fail compilation. - else report.error("null") + report.error(ex.getMessage.nn) // signals that we should fail compilation. case ex: TypeError => println(s"${ex.toMessage} while compiling ${files.map(_.path).mkString(", ")}") throw ex diff --git a/compiler/src/dotty/tools/dotc/Resident.scala b/compiler/src/dotty/tools/dotc/Resident.scala index 6025dd93078d..f1370d915250 100644 --- a/compiler/src/dotty/tools/dotc/Resident.scala +++ b/compiler/src/dotty/tools/dotc/Resident.scala @@ -55,8 +55,8 @@ class Resident extends Driver { } if (line.startsWith(quit)) ctx.reporter else - // assuming split returns non-nullable values - loop((line split "\\s+").asInstanceOf, nextCtx) + // split returns non-nullable values + loop((line split "\\s+").asInstanceOf[Array[String]], nextCtx) case None => prevCtx.reporter } diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 7fa21372a6ff..cf5c1fdb9b70 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -825,8 +825,8 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => * tree must be reachable from come tree stored in an enclosing context. */ def definingStats(sym: Symbol)(using Context): List[Tree] = - val unit: CompilationUnit | Null = ctx.compilationUnit - if (!sym.span.exists || (ctx eq NoContext) || unit == null) Nil + val unit = ctx.compilationUnit + if (!sym.span.exists || (ctx eq NoContext) || (unit eq NoCompilationUnit)) Nil else defPath(sym, unit.tpdTree) match { case defn :: encl :: _ => def verify(stats: List[Tree]) = diff --git a/compiler/src/dotty/tools/dotc/config/Feature.scala b/compiler/src/dotty/tools/dotc/config/Feature.scala index dccbc9eb21ee..a037a0d4fcbc 100644 --- a/compiler/src/dotty/tools/dotc/config/Feature.scala +++ b/compiler/src/dotty/tools/dotc/config/Feature.scala @@ -78,9 +78,7 @@ object Feature: SourceVersion.valueOf(ctx.settings.source.value) def sourceVersion(using Context): SourceVersion = - val unit: CompilationUnit | Null = ctx.compilationUnit - if unit == null then sourceVersionSetting - else unit.sourceVersion match + ctx.compilationUnit.sourceVersion match case Some(v) => v case none => sourceVersionSetting diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index 9f0400ffefde..64311f75c657 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -349,8 +349,7 @@ object Contexts { if ctx1 == null then util.Stats.record("Context.withSource.new") val ctx2 = fresh.setSource(source) - val ctx2unit: CompilationUnit | Null = ctx2.compilationUnit - if ctx2unit == null then + if ctx2.compilationUnit eq NoCompilationUnit then // `source` might correspond to a file not necessarily // in the current project (e.g. when inlining library code), // so set `mustExist` to false. @@ -394,12 +393,7 @@ object Contexts { final def erasedTypes = phase.erasedTypes /** Are we in a Java compilation unit? */ - final def isJava: Boolean = - // FIXME: It would be much nicer if compilationUnit was non-nullable, - // perhaps we need to introduce a `NoCompilationUnit` compilation unit - // to be used as a default value. - val unit: CompilationUnit | Null = compilationUnit - unit != null && unit.isJava + final def isJava: Boolean = compilationUnit.isJava /** Is current phase after TyperPhase? */ final def isAfterTyper = base.isAfterTyper(phase) @@ -837,6 +831,7 @@ object Contexts { store = initialStore .updated(settingsStateLoc, settingsGroup.defaultState) .updated(notNullInfosLoc, Nil) + .updated(compilationUnitLoc, NoCompilationUnit) searchHistory = new SearchRoot gadt = EmptyGadtConstraint } diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 59f108fe0330..933d63667273 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1431,8 +1431,7 @@ class Definitions { /** Are we compiling a java source file? */ private def isJavaContext(using Context): Boolean = - val unit: CompilationUnit | Null = ctx.compilationUnit - unit != null && unit.isJava + ctx.compilationUnit.isJava private def unqualifiedTypes(refs: List[TermRef]) = val types = refs.toSet[NamedType] diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 6bbd487374dc..3a95a212437e 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1230,10 +1230,10 @@ object SymDenotations { * lookup its companion in the same scope. */ private def companionNamed(name: TypeName)(using Context): Symbol = - val unit: CompilationUnit | Null = ctx.compilationUnit + val unit = ctx.compilationUnit if (owner.isClass) owner.unforcedDecls.lookup(name).suchThat(_.isCoDefinedWith(symbol)).symbol - else if (!owner.exists || unit == null) + else if (!owner.exists || (unit eq NoCompilationUnit)) NoSymbol else if (!unit.tpdTree.isEmpty) tpd.definingStats(symbol).iterator diff --git a/compiler/src/dotty/tools/dotc/typer/Nullables.scala b/compiler/src/dotty/tools/dotc/typer/Nullables.scala index 63a67ce1f4c0..d08265566834 100644 --- a/compiler/src/dotty/tools/dotc/typer/Nullables.scala +++ b/compiler/src/dotty/tools/dotc/typer/Nullables.scala @@ -167,10 +167,10 @@ object Nullables: def isTracked(ref: TermRef)(using Context) = ref.isStable || { val sym = ref.symbol - val unit: CompilationUnit | Null = ctx.compilationUnit + val unit = ctx.compilationUnit !ref.usedOutOfOrder && sym.span.exists - && unit != null // could be null under -Ytest-pickler + && (unit ne NoCompilationUnit) // could be null under -Ytest-pickler && unit.assignmentSpans.contains(sym.span.start) } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 3397a5da8fdf..a0ad8ec9aae3 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -313,7 +313,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer */ def isDefinedInCurrentUnit(denot: Denotation)(using Context): Boolean = denot match { case MultiDenotation(d1, d2) => isDefinedInCurrentUnit(d1) || isDefinedInCurrentUnit(d2) - case denot: SingleDenotation => (ctx.compilationUnit: CompilationUnit | Null) != null && denot.symbol.source == ctx.compilationUnit.source + case denot: SingleDenotation => (ctx.compilationUnit ne NoCompilationUnit) && denot.symbol.source == ctx.compilationUnit.source } /** Is `denot` the denotation of a self symbol? */ diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 926b3fd04003..0ab435991354 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -14,6 +14,7 @@ import dotty.tools.dotc.quoted.reflect._ import dotty.tools.dotc.quoted.QuoteUtils._ import dotty.tools.dotc.core.Decorators._ import dotty.tools.dotc.CompilationUnit +import dotty.tools.dotc.NoCompilationUnit import dotty.tools.dotc.quoted.{MacroExpansion, PickledQuotes, QuoteUtils} @@ -2797,8 +2798,8 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object SourceFile extends SourceFileModule { def current: SourceFile = - val unit: CompilationUnit | Null = ctx.compilationUnit - if unit == null then + val unit = ctx.compilationUnit + if unit == NoCompilationUnit then throw new java.lang.UnsupportedOperationException( "`reflect.SourceFile.current` cannot be called within the TASTy ispector") else unit.source