Skip to content

[WIP] Separate the namer out from the typer. #13762

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

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 5 additions & 1 deletion compiler/src/dotty/tools/dotc/CompilationUnit.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import ast.{tpd, untpd}
import tpd.{Tree, TreeTraverser}
import typer.PrepareInlineable.InlineAccessors
import typer.Nullables
import transform.AccessProxies
import transform.SymUtils._
import core.Decorators._
import config.SourceVersion
Expand Down Expand Up @@ -62,7 +63,7 @@ class CompilationUnit protected (val source: SourceFile) {
/** Can this compilation unit be suspended */
def isSuspendable: Boolean = true

/** Suspends the compilation unit by thowing a SuspendException
/** Suspends the compilation unit by throwing a SuspendException
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo

* and recording the suspended compilation unit
*/
def suspend()(using Context): Nothing =
Expand All @@ -85,6 +86,9 @@ class CompilationUnit protected (val source: SourceFile) {
def assignmentSpans(using Context): Map[Int, List[Span]] =
if myAssignmentSpans == null then myAssignmentSpans = Nullables.assignmentSpans
myAssignmentSpans

private[dotc] var inlineAccessors: InlineAccessors = null
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the new unpleasant state.

private[dotc] var protectedAccessors: AccessProxies = null
}

object CompilationUnit {
Expand Down
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package dotc

import core._
import Contexts._
import typer.{TyperPhase, RefChecks}
import typer.{NamerPhase, TyperPhase, RefChecks}
import parsing.Parser
import Phases.Phase
import transform._
Expand Down Expand Up @@ -38,7 +38,8 @@ class Compiler {
/** Phases dealing with the frontend up to trees ready for TASTY pickling */
protected def frontendPhases: List[List[Phase]] =
List(new Parser) :: // Compiler frontend: scanner, parser
List(new TyperPhase) :: // Compiler frontend: namer, typer
List(new NamerPhase) :: // Compiler frontend: namer
List(new TyperPhase) :: // Compiler frontend: typer
List(new YCheckPositions) :: // YCheck positions
List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks
List(new semanticdb.ExtractSemanticDB) :: // Extract info into .semanticdb files
Expand Down
23 changes: 0 additions & 23 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1410,29 +1410,6 @@ object desugar {
FunctionWithMods(params, body, Modifiers(mods))
}

/** Add annotation to tree:
* tree @fullName
*
* The annotation is usually represented as a TypeTree referring to the class
* with the given name `fullName`. However, if the annotation matches a file name
* that is still to be entered, the annotation is represented as a cascade of `Selects`
* following `fullName`. This is necessary so that we avoid reading an annotation from
* the classpath that is also compiled from source.
*/
def makeAnnotated(fullName: String, tree: Tree)(using Context): Annotated = {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused.

val parts = fullName.split('.')
val ttree = typerPhase match {
case phase: TyperPhase if phase.stillToBeEntered(parts.last) =>
val prefix =
parts.init.foldLeft(Ident(nme.ROOTPKG): Tree)((qual, name) =>
Select(qual, name.toTermName))
Select(prefix, parts.last.toTypeName)
case _ =>
TypeTree(requiredClass(fullName).typeRef)
}
Annotated(tree, New(ttree, Nil))
}

private def derivedValDef(original: Tree, named: NameTree, tpt: Tree, rhs: Tree, mods: Modifiers)(using Context) = {
val vdef = ValDef(named.name.asTermName, tpt, rhs)
.withMods(mods)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class InteractiveCompiler extends Compiler {
// after each phase group instead of waiting for the pipeline to finish.
override def phases: List[List[Phase]] = List(
List(new Parser),
List(new NamerPhase),
List(new TyperPhase),
List(new transform.SetRootTree),
List(new transform.CookComments)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,11 @@ class ProtectedAccessors extends MiniPhase {
ctx.property(AccessorsKey).get

override def prepareForUnit(tree: Tree)(using Context): Context =
ctx.fresh.setProperty(AccessorsKey, new Accessors)
var acc = ctx.compilationUnit.protectedAccessors.asInstanceOf[Accessors]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could make this a little cleaner but I wanted to wait for feedback on the right place to put the state.

if (acc == null)
acc = new Accessors()
ctx.compilationUnit.protectedAccessors = acc
ctx.fresh.setProperty(AccessorsKey, acc)

private class Accessors extends AccessProxies {
val insert: Insert = new Insert {
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ object Inliner {
&& StagingContext.level == 0
&& (
ctx.phase == Phases.inliningPhase
|| (ctx.phase == Phases.typerPhase && needsTransparentInlining(tree))
|| (ctx.phase.isTyper && needsTransparentInlining(tree))
)
&& !ctx.typer.hasInliningErrors
&& !ctx.base.stopInlining
Expand Down
78 changes: 78 additions & 0 deletions compiler/src/dotty/tools/dotc/typer/NamerPhase.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package dotty.tools
package dotc
package typer

import core.*
import Phases.*
import Contexts.*
import Symbols.*
import Decorators.*
import ImportInfo.withRootImports
import parsing.JavaParsers.JavaParser
import parsing.Parsers.Parser
import parsing.Parser as ParserPhase
import config.Config
import config.Printers.{ default, typr }
import util.Stats.*
import util.{ NoSourcePosition, SourcePosition }
import scala.util.control.NonFatal

import ast.Trees.*
import dotty.tools.dotc.core.Denotations.SingleDenotation

/**
*
* @param addRootImports Set to false in the REPL. Calling [[ImportInfo.withRootImports]] on the [[Context]]
* for each [[CompilationUnit]] causes dotty.tools.repl.ScriptedTests to fail.
*/
class NamerPhase(addRootImports: Boolean = true) extends Phase {

override def phaseName: String = NamerPhase.name
override def isTyper: Boolean = true

// We run TreeChecker only after type checking
override def isCheckable: Boolean = false

override def allowsImplicitSearch: Boolean = true

// Run regardless of parsing errors
override def isRunnable(implicit ctx: Context): Boolean = true

def enterSyms(using Context): Unit = monitor("indexing") {
val unit = ctx.compilationUnit
ctx.typer.index(unit.untpdTree)
typr.println("entered: " + unit.source)
}

override def runOn(units: List[CompilationUnit])(using Context): List[CompilationUnit] =
val unitContexts =
for unit <- units yield
val newCtx0 = ctx.fresh.setPhase(this.start).setCompilationUnit(unit)
val newCtx = PrepareInlineable.initContext(newCtx0)
report.inform(s"naming ${unit.source}")
if (addRootImports)
newCtx.withRootImports
else
newCtx

unitContexts.foreach(enterSyms(using _))

ctx.base.parserPhase match {
case p: ParserPhase =>
if p.firstXmlPos.exists && !defn.ScalaXmlPackageClass.exists then
report.error(
"""To support XML literals, your project must depend on scala-xml.
|See https://github.com/scala/scala-xml for more information.""".stripMargin,
p.firstXmlPos)
case _ =>
}


unitContexts.map(_.compilationUnit)

def run(using Context): Unit = unsupported("run")
}

object NamerPhase {
val name: String = "namer"
}
6 changes: 5 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ object PrepareInlineable {
private val InlineAccessorsKey = new Property.Key[InlineAccessors]

def initContext(ctx: Context): Context =
ctx.fresh.setProperty(InlineAccessorsKey, new InlineAccessors)
var acc = ctx.compilationUnit.inlineAccessors
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above.

if (acc == null)
ctx.compilationUnit.inlineAccessors = new InlineAccessors()
acc = ctx.compilationUnit.inlineAccessors
ctx.fresh.setProperty(InlineAccessorsKey, acc)

def makeInlineable(tree: Tree)(using Context): Tree =
ctx.property(InlineAccessorsKey).get.makeInlineable(tree)
Expand Down
35 changes: 0 additions & 35 deletions compiler/src/dotty/tools/dotc/typer/TyperPhase.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,9 @@ class TyperPhase(addRootImports: Boolean = true) extends Phase {

override def allowsImplicitSearch: Boolean = true

/** The contexts for compilation units that are parsed but not yet entered */
private var remaining: List[Context] = Nil

/** Does a source file ending with `<name>.scala` belong to a compilation unit
* that is parsed but not yet entered?
*/
def stillToBeEntered(name: String): Boolean =
remaining.exists(_.compilationUnit.toString.endsWith(name + ".scala"))

// Run regardless of parsing errors
override def isRunnable(implicit ctx: Context): Boolean = true

def enterSyms(using Context): Unit = monitor("indexing") {
val unit = ctx.compilationUnit
ctx.typer.index(unit.untpdTree)
typr.println("entered: " + unit.source)
}

def typeCheck(using Context): Unit = monitor("typechecking") {
try
val unit = ctx.compilationUnit
Expand All @@ -68,13 +53,6 @@ class TyperPhase(addRootImports: Boolean = true) extends Phase {
JavaChecks.check(unit.tpdTree)
}


private def firstTopLevelDef(trees: List[tpd.Tree])(using Context): Symbol = trees match
case PackageDef(_, defs) :: _ => firstTopLevelDef(defs)
case Import(_, _) :: defs => firstTopLevelDef(defs)
case (tree @ TypeDef(_, _)) :: _ => tree.symbol
case _ => NoSymbol

protected def discardAfterTyper(unit: CompilationUnit)(using Context): Boolean =
unit.isJava || unit.suspended

Expand All @@ -89,19 +67,6 @@ class TyperPhase(addRootImports: Boolean = true) extends Phase {
else
newCtx

remaining = unitContexts
while remaining.nonEmpty do
enterSyms(using remaining.head)
remaining = remaining.tail
val firstXmlPos = ctx.base.parserPhase match {
case p: ParserPhase =>
if p.firstXmlPos.exists && !defn.ScalaXmlPackageClass.exists then
report.error(
"""To support XML literals, your project must depend on scala-xml.
|See https://github.com/scala/scala-xml for more information.""".stripMargin,
p.firstXmlPos)
case _ =>
}

unitContexts.foreach(typeCheck(using _))
record("total trees after typer", ast.Trees.ntrees)
Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/repl/ReplCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import dotty.tools.dotc.core.Symbols._
import dotty.tools.dotc.reporting.Diagnostic
import dotty.tools.dotc.transform.{PostTyper, Staging}
import dotty.tools.dotc.typer.ImportInfo._
import dotty.tools.dotc.typer.TyperPhase
import dotty.tools.dotc.typer.{NamerPhase, TyperPhase}
import dotty.tools.dotc.util.Spans._
import dotty.tools.dotc.util.{ParsedComment, SourceFile}
import dotty.tools.dotc.{CompilationUnit, Compiler, Run}
Expand All @@ -33,6 +33,7 @@ import scala.collection.mutable
class ReplCompiler extends Compiler {

override protected def frontendPhases: List[List[Phase]] = List(
List(new NamerPhase(addRootImports = false)),
List(new TyperPhase(addRootImports = false)),
List(new CollectTopLevelImports),
List(new PostTyper),
Expand Down
3 changes: 2 additions & 1 deletion docs/docs/internals/overall-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ phases. The current list of phases is specified in class [Compiler] as follows:
/** Phases dealing with the frontend up to trees ready for TASTY pickling */
protected def frontendPhases: List[List[Phase]] =
List(new Parser) :: // scanner, parser
List(new TyperPhase) :: // namer, typer
List(new NamerPhase) :: // namer
List(new TyperPhase) :: // typer
List(new YCheckPositions) :: // YCheck positions
List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks
List(new semanticdb.ExtractSemanticDB) :: // Extract info into .semanticdb files
Expand Down