Skip to content

Fix #7312: Choose root imports based on the source file #9394

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 1 commit into from
Aug 10, 2020
Merged
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
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)
}
}
25 changes: 25 additions & 0 deletions tests/run/java-no-scala-import/Config.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
public class Config {
long longVal;
Long longObj;
Integer boxed;

public long getLongVal() {
return longVal;
}

public void setLongVal(long longVal) {
this.longVal = longVal;
}

public Long getLongObj() {
return longObj;
}

public void setLongObj(Long longObj) {
this.longObj = longObj;
}

public Long longLength(String str) {
return Long.valueOf(str.length());
}
}
6 changes: 6 additions & 0 deletions tests/run/java-no-scala-import/Test.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
object Test extends App {
val c = new Config()
c.setLongObj(10)
println(c.getLongObj)
val l = c.longLength("test")
}