Skip to content

Commit

Permalink
Fix scala#7312: choose root imports based on the source file
Browse files Browse the repository at this point in the history
Instead of setting the scala root imports in the rootContext,
set it later and choose the right import depending on the language (java and scala).
  • Loading branch information
TheElectronWill committed Jul 21, 2020
1 parent aa6b172 commit 7e05e1e
Show file tree
Hide file tree
Showing 15 changed files with 141 additions and 66 deletions.
20 changes: 12 additions & 8 deletions compiler/src/dotty/tools/dotc/Run.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import Periods._
import Symbols._
import Types._
import Scopes._
import typer.{ImportInfo, Typer}
import typer.Typer
import typer.ImportInfo._
import Decorators._
import io.{AbstractFile, PlainFile}
import Phases.unfusedPhases
Expand Down Expand Up @@ -74,9 +75,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
.addMode(Mode.ImplicitsEnabled)
.setTyperState(ctx.typerState.fresh(ctx.reporter))
ctx.initialize()(using start) // re-initialize the base context with start
def addImport(ctx: Context, rootRef: ImportInfo.RootRef) =
ctx.fresh.setImportInfo(ImportInfo.rootImport(rootRef))
defn.RootImportFns.foldLeft(start.setRun(this))(addImport)
start.setRun(this)
}

private var compiling = false
Expand All @@ -92,8 +91,8 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
private var myFiles: Set[AbstractFile] = _

/** The compilation units currently being compiled, this may return different
* results over time.
*/
* results over time.
*/
def units: List[CompilationUnit] = myUnits

var suspendedUnits: mutable.ListBuffer[CompilationUnit] = mutable.ListBuffer()
Expand Down Expand Up @@ -205,15 +204,20 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
compiling = false
}

/** Enter top-level definitions of classes and objects contain in Scala source file `file`.
/** Enter top-level definitions of classes and objects contained in source file `file`.
* The newly added symbols replace any previously entered symbols.
* If `typeCheck = true`, also run typer on the compilation unit, and set
* `rootTreeOrProvider`.
*/
def lateCompile(file: AbstractFile, typeCheck: Boolean)(using Context): Unit =
if (!files.contains(file) && !lateFiles.contains(file)) {
lateFiles += file

val unit = CompilationUnit(ctx.getSource(file.path))
val unitCtx = runContext.fresh
.setCompilationUnit(unit)
.withRootImports

def process()(using Context) = {
unit.untpdTree =
if (unit.isJava) new JavaParser(unit.source).parse()
Expand All @@ -227,7 +231,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
if (typeCheck)
if (compiling) finalizeActions += (() => processUnit()) else processUnit()
}
process()(using runContext.fresh.setCompilationUnit(unit))
process()(using unitCtx)
}

private sealed trait PrintedTree
Expand Down
61 changes: 47 additions & 14 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1140,28 +1140,61 @@ class Definitions {
def isPredefClass(cls: Symbol): Boolean =
(cls.owner eq ScalaPackageClass) && predefClassNames.contains(cls.name)

val StaticRootImportFns: List[RootRef] = List[RootRef](
(() => JavaLangPackageVal.termRef, false),
(() => ScalaPackageVal.termRef, false)
private val JavaImportFns: List[RootRef] = List(
RootRef(() => JavaLangPackageVal.termRef)
)

val PredefImportFns: List[RootRef] = List[RootRef](
(() => ScalaPredefModule.termRef, true),
(() => DottyPredefModule.termRef, false)
private val ScalaImportFns: List[RootRef] =
JavaImportFns :+
RootRef(() => ScalaPackageVal.termRef)

private val PredefImportFns: List[RootRef] = List(
RootRef(() => ScalaPredefModule.termRef, isPredef=true),
RootRef(() => DottyPredefModule.termRef)
)

@tu lazy val RootImportFns: List[RootRef] =
if (ctx.settings.YnoImports.value) Nil
else if (ctx.settings.YnoPredef.value) StaticRootImportFns
else StaticRootImportFns ++ PredefImportFns
@tu private lazy val JavaRootImportFns: List[RootRef] =
if ctx.settings.YnoImports.value then Nil
else JavaImportFns

@tu lazy val ShadowableImportNames: Set[TermName] = Set("Predef", "DottyPredef").map(_.toTermName)
@tu lazy val RootImportTypes: List[TermRef] = RootImportFns.map(_._1())
@tu private lazy val ScalaRootImportFns: List[RootRef] =
if ctx.settings.YnoImports.value then Nil
else if ctx.settings.YnoPredef.value then ScalaImportFns
else ScalaImportFns ++ PredefImportFns

@tu private lazy val JavaRootImportTypes: List[TermRef] = JavaRootImportFns.map(_.refFn())
@tu private lazy val ScalaRootImportTypes: List[TermRef] = ScalaRootImportFns.map(_.refFn())
@tu private lazy val JavaUnqualifiedOwnerTypes: Set[NamedType] = unqualifiedTypes(JavaRootImportTypes)
@tu private lazy val ScalaUnqualifiedOwnerTypes: Set[NamedType] = unqualifiedTypes(ScalaRootImportTypes)

/** Are we compiling a java source file? */
private def isJavaContext(using Context): Boolean =
val unit = ctx.compilationUnit
unit != null && unit.isJava

private def unqualifiedTypes(refs: List[TermRef]) =
val types = refs.toSet[NamedType]
types ++ types.map(_.symbol.moduleClass.typeRef)

/** Lazy references to the root imports */
def rootImportFns(using Context): List[RootRef] =
if isJavaContext then JavaRootImportFns
else ScalaRootImportFns

/** Root types imported by default */
def rootImportTypes(using Context): List[TermRef] =
if isJavaContext then JavaRootImportTypes
else ScalaRootImportTypes

/** Modules whose members are in the default namespace and their module classes */
@tu lazy val UnqualifiedOwnerTypes: Set[NamedType] =
RootImportTypes.toSet[NamedType] ++ RootImportTypes.map(_.symbol.moduleClass.typeRef)
def unqualifiedOwnerTypes(using Context): Set[NamedType] =
if isJavaContext then JavaUnqualifiedOwnerTypes
else ScalaUnqualifiedOwnerTypes

/** Names of the root import symbols that can be hidden by other imports */
@tu lazy val ShadowableImportNames: Set[TermName] = Set("Predef", "DottyPredef").map(_.toTermName)

/** Class symbols for which no class exist at runtime */
@tu lazy val NotRuntimeClasses: Set[Symbol] = Set(AnyClass, AnyValClass, NullClass, NothingClass)

/** Classes that are known not to have an initializer irrespective of
Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/core/Phases.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import dotty.tools.dotc.transform.MegaPhase._
import dotty.tools.dotc.transform._
import Periods._
import typer.{FrontEnd, RefChecks}
import typer.ImportInfo.withRootImports
import ast.tpd

object Phases {
Expand Down Expand Up @@ -291,7 +292,7 @@ object Phases {
/** @pre `isRunnable` returns true */
def runOn(units: List[CompilationUnit])(using Context): List[CompilationUnit] =
units.map { unit =>
val unitCtx = ctx.fresh.setPhase(this.start).setCompilationUnit(unit)
val unitCtx = ctx.fresh.setPhase(this.start).setCompilationUnit(unit).withRootImports
run(using unitCtx)
unitCtx.compilationUnit
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ object JavaParsers {
def javaLangObject(): Tree = javaLangDot(tpnme.Object)

def arrayOf(tpt: Tree): AppliedTypeTree =
AppliedTypeTree(Ident(nme.Array.toTypeName), List(tpt))
AppliedTypeTree(scalaDot(tpnme.Array), List(tpt))

def makeTemplate(parents: List[Tree], stats: List[Tree], tparams: List[TypeDef], needsDummyConstr: Boolean): Template = {
def pullOutFirstConstr(stats: List[Tree]): (Tree, List[Tree]) = stats match {
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ class PlainPrinter(_ctx: Context) extends Printer {
}

protected def isOmittablePrefix(sym: Symbol): Boolean =
defn.UnqualifiedOwnerTypes.exists(_.symbol == sym) || isEmptyPrefix(sym)
defn.unqualifiedOwnerTypes.exists(_.symbol == sym) || isEmptyPrefix(sym)

protected def isEmptyPrefix(sym: Symbol): Boolean =
sym.isEffectiveRoot || sym.isAnonymousClass || sym.name.isReplWrapperName
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/patmat/Space.scala
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,7 @@ class SpaceEngine(using Context) extends SpaceLogic {

def isOmittable(sym: Symbol) =
sym.isEffectiveRoot || sym.isAnonymousClass || sym.name.isReplWrapperName ||
ctx.definitions.UnqualifiedOwnerTypes.exists(_.symbol == sym) ||
ctx.definitions.unqualifiedOwnerTypes.exists(_.symbol == sym) ||
sym.showFullName.startsWith("scala.") ||
sym == enclosingCls || sym == enclosingCls.sourceModule

Expand Down
7 changes: 4 additions & 3 deletions compiler/src/dotty/tools/dotc/typer/FrontEnd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import Phases._
import Contexts._
import Symbols._
import Decorators._
import dotty.tools.dotc.parsing.JavaParsers.JavaParser
import ImportInfo.withRootImports
import parsing.JavaParsers.JavaParser
import parsing.Parsers.Parser
import config.Config
import config.Printers.{typr, default}
Expand Down Expand Up @@ -98,7 +99,7 @@ class FrontEnd extends Phase {
val unitContexts =
for unit <- units yield
report.inform(s"compiling ${unit.source}")
ctx.fresh.setCompilationUnit(unit)
ctx.fresh.setCompilationUnit(unit).withRootImports
unitContexts.foreach(parse(using _))
record("parsedTrees", ast.Trees.ntrees)
remaining = unitContexts
Expand All @@ -123,7 +124,7 @@ class FrontEnd extends Phase {
| ${suspendedUnits.toList}%, %
|"""
val enableXprintSuspensionHint =
if (ctx.settings.XprintSuspension.value) ""
if ctx.settings.XprintSuspension.value then ""
else "\n\nCompiling with -Xprint-suspension gives more information."
report.error(em"""Cyclic macro dependencies $where
|Compilation stopped since no further progress can be made.
Expand Down
36 changes: 22 additions & 14 deletions compiler/src/dotty/tools/dotc/typer/ImportInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,32 @@ import Decorators._

object ImportInfo {

type RootRef = (
() => TermRef, // a lazy reference to the root module to be imported
Boolean // true if this will refer to scala.Predef
)

/** The import info for a root import from given symbol `sym` */
def rootImport(rootRef: RootRef)(using Context): ImportInfo =
val (refFn, isPredef) = rootRef
case class RootRef(refFn: () => TermRef, isPredef: Boolean = false)

/** The import info for a root import */
def rootImport(ref: RootRef)(using Context): ImportInfo =
var selectors =
untpd.ImportSelector(untpd.Ident(nme.WILDCARD)) // import all normal members...
:: untpd.ImportSelector(untpd.Ident(nme.EMPTY)) // ... and also all given members
:: Nil
if isPredef then // do not import any2stringadd
if ref.isPredef then // do not import any2stringadd
selectors = untpd.ImportSelector(untpd.Ident(nme.any2stringadd), untpd.Ident(nme.WILDCARD))
:: selectors
def expr(using Context) = tpd.Ident(refFn())
def imp(using Context) = tpd.Import(expr, selectors)
ImportInfo(imp.symbol, selectors, None, isRootImport = true)

def sym(using Context) =
val expr = tpd.Ident(ref.refFn()) // refFn must be called in the context of ImportInfo.sym
tpd.Import(expr, selectors).symbol

ImportInfo(sym, selectors, None, isRootImport = true)

extension (c: Context):
def withRootImports(rootRefs: List[RootRef])(using Context): Context =
rootRefs.foldLeft(c)((ctx, ref) => ctx.fresh.setImportInfo(rootImport(ref)))

def withRootImports: Context =
given Context = c
c.withRootImports(defn.rootImportFns)

}

/** Info relating to an import clause
Expand Down Expand Up @@ -164,7 +172,7 @@ class ImportInfo(symf: Context ?=> Symbol,
case Some(symName) => defn.ShadowableImportNames.contains(symName)
case None => false
myUnimported =
if maybeShadowsRoot && defn.RootImportTypes.exists(_.symbol == sym) then sym
if maybeShadowsRoot && defn.rootImportTypes.exists(_.symbol == sym) then sym
else NoSymbol
assert(myUnimported != null)
myUnimported
Expand Down Expand Up @@ -193,4 +201,4 @@ class ImportInfo(symf: Context ?=> Symbol,
end featureImported

def toText(printer: Printer): Text = printer.toText(this)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ trait ImportSuggestions:

try
val roots = suggestionRoots
.filterNot(root => defn.RootImportTypes.exists(_.symbol == root.symbol))
.filterNot(root => defn.rootImportTypes.exists(_.symbol == root.symbol))
// don't suggest things that are imported by default

def extensionImports = pt match
Expand Down
16 changes: 7 additions & 9 deletions compiler/src/dotty/tools/repl/ReplCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import dotty.tools.dotc.core.StdNames._
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.ImportInfo._
import dotty.tools.dotc.util.Spans._
import dotty.tools.dotc.util.{ParsedComment, SourceFile}
import dotty.tools.dotc.{CompilationUnit, Compiler, Run}
Expand Down Expand Up @@ -48,21 +48,19 @@ class ReplCompiler extends Compiler {
def importPreviousRun(id: Int)(using Context) = {
// we first import the wrapper object id
val path = nme.EMPTY_PACKAGE ++ "." ++ objectNames(id)
def importWrapper(c: Context, importGiven: Boolean) = {
val importInfo = ImportInfo.rootImport(() =>
requiredModuleRef(path), importGiven)(using c)
c.fresh.setNewScope.setImportInfo(importInfo)
}
val ctx0 = importWrapper(importWrapper(ctx, false), true)
val ctx0 = ctx.fresh
.setNewScope
.withRootImports(RootRef(() => requiredModuleRef(path)) :: Nil)

// then its user defined imports
val imports = state.imports.getOrElse(id, Nil)
if (imports.isEmpty) ctx0
if imports.isEmpty then ctx0
else imports.foldLeft(ctx0.fresh.setNewScope)((ctx, imp) =>
importContext(imp)(using ctx))
}

(1 to state.objectIndex).foldLeft(super.rootContext)((ctx, id) =>
val rootCtx = super.rootContext.withRootImports
(1 to state.objectIndex).foldLeft(rootCtx)((ctx, id) =>
importPreviousRun(id)(using ctx))
}
}
Expand Down
7 changes: 4 additions & 3 deletions compiler/src/dotty/tools/repl/ReplFrontEnd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package repl
import dotc.typer.FrontEnd
import dotc.CompilationUnit
import dotc.core.Contexts._
import dotc.typer.ImportInfo.withRootImports

/** A customized `FrontEnd` for the REPL
*
Expand All @@ -17,10 +18,10 @@ private[repl] class REPLFrontEnd extends FrontEnd {

override def runOn(units: List[CompilationUnit])(using Context): List[CompilationUnit] = {
assert(units.size == 1) // REPl runs one compilation unit at a time

val unitContext = ctx.fresh.setCompilationUnit(units.head)
val unit = units.head
val unitContext = ctx.fresh.setCompilationUnit(unit).withRootImports
enterSyms(using unitContext)
typeCheck(using unitContext)
List(unitContext.compilationUnit)
List(unit)
}
}
3 changes: 1 addition & 2 deletions doc-tool/src/dotty/tools/dottydoc/DocCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,9 @@ import dotty.tools.dotc.transform.CookComments
class DocCompiler extends Compiler {

override def newRun(using Context): Run = {
if (ctx.settings.fromTasty.value) {
if ctx.settings.fromTasty.value then
reset()
new TASTYRun(this, ctx.addMode(Mode.ReadPositions).addMode(Mode.ReadComments))
}
else
super.newRun
}
Expand Down
15 changes: 7 additions & 8 deletions doc-tool/test/dotty/tools/dottydoc/DottyDocTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,18 @@ package dottydoc

import vulpix.TestConfiguration

import dotc.Compiler
import dotc.{Compiler, Run}
import dotc.core.Contexts.{ Context, ContextBase, FreshContext }
import dotc.core.Comments.{ ContextDoc, ContextDocstrings }
import dotc.util.SourceFile
import dotc.core.Phases.Phase
import dotty.tools.io.AbstractFile
import dotc.typer.FrontEnd
import dottydoc.core.{ DocASTPhase, ContextDottydoc }
import model.Package
import dotty.tools.dottydoc.util.syntax._
import dotty.tools.io.AbstractFile
import dotc.reporting.{ StoreReporter, MessageRendering }
import dotc.interfaces.Diagnostic.ERROR
import io.Directory
import dotc.reporting.{ StoreReporter, MessageRendering }
import util.syntax._
import io.{AbstractFile, Directory}
import core.{ DocASTPhase, ContextDottydoc }
import model.Package
import org.junit.Assert.fail

import java.io.{ BufferedWriter, OutputStreamWriter }
Expand Down Expand Up @@ -115,6 +113,7 @@ trait DottyDocTest extends MessageRendering {
ctx.setSetting(ctx.settings.outputDir, AbstractFile.getDirectory(out))
}
val dotc = new Compiler

val run = dotc.newRun(using dotcCtx)
run.compileSources(sources)
assert(!dotcCtx.reporter.hasErrors)
Expand Down
Loading

0 comments on commit 7e05e1e

Please sign in to comment.