Skip to content

Avoid accidental captures of Context #1974

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 13 commits into from
Feb 21, 2017
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
14 changes: 10 additions & 4 deletions compiler/src/dotty/tools/dotc/core/Annotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,17 @@ object Annotations {
}

/** Create an annotation where the symbol and the tree are computed lazily. */
def deferredSymAndTree(sym: => Symbol, treeFn: Context => Tree)(implicit ctx: Context): Annotation =
def deferredSymAndTree(symf: Context => Symbol, treeFn: Context => Tree)(implicit ctx: Context): Annotation =
new LazyAnnotation {
lazy val symf = sym

override def symbol(implicit ctx: Context): Symbol = symf
private[this] var mySym: Symbol = _

override def symbol(implicit ctx: Context): Symbol = {
if (mySym == null || mySym.defRunId != ctx.runId) {
mySym = symf(ctx)
assert(mySym != null)
}
mySym
}
def complete(implicit ctx: Context) = treeFn(ctx)
}

Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/core/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -401,12 +401,12 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
def forwardRefs(from: Symbol, to: Type, prefs: List[TypeRef]) = to match {
case to @ TypeBounds(lo1, hi1) if lo1 eq hi1 =>
for (pref <- prefs) {
def forward(): Unit =
def forward()(implicit ctx: Context): Unit =
for (argSym <- pref.decls)
if (argSym is BaseTypeArg)
forwardRef(argSym, from, to, cls, decls)
pref.info match {
case info: TempClassInfo => info.addSuspension(forward)
case info: TempClassInfo => info.addSuspension(implicit ctx => forward())
case _ => forward()
}
}
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3089,14 +3089,14 @@ object Types {
* be no longer temporary. These actions will be performed once `cls` gets a real
* ClassInfo.
*/
private var suspensions: List[() => Unit] = Nil
private var suspensions: List[Context => Unit] = Nil

def addSuspension(suspension: () => Unit): Unit = suspensions ::= suspension
def addSuspension(suspension: Context => Unit): Unit = suspensions ::= suspension

/** Install classinfo with known parents in `denot` and resume all suspensions */
def finalize(denot: SymDenotation, parents: List[TypeRef])(implicit ctx: Context) = {
denot.info = derivedClassInfo(classParents = parents)
suspensions.foreach(_())
suspensions.foreach(_(ctx))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -806,7 +806,7 @@ class ClassfileParser(
def classSymbol(externalName: Name)(implicit ctx: Context): Symbol = {
/** Return the symbol of `innerName`, having the given `externalName`. */
def innerSymbol(externalName: Name, innerName: Name, static: Boolean): Symbol = {
def getMember(sym: Symbol, name: Name): Symbol =
def getMember(sym: Symbol, name: Name)(implicit ctx: Context): Symbol =
if (static)
if (sym == classRoot.symbol) staticScope.lookup(name)
else sym.companionModule.info.member(name).symbol
Expand Down
6 changes: 4 additions & 2 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,9 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle
val end = readEnd()
val tp = readType()
val lazyAnnotTree = readLater(end, rdr => ctx => rdr.readTerm()(ctx))
annots += Annotation.deferredSymAndTree(tp.typeSymbol, _ => lazyAnnotTree.complete)
annots += Annotation.deferredSymAndTree(
implicit ctx => tp.typeSymbol,
implicit ctx => lazyAnnotTree.complete)
case tag =>
assert(false, s"illegal modifier tag $tag at $currentAddr, end = $end")
}
Expand Down Expand Up @@ -769,7 +771,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle
cls.setApplicableFlags(fork.indexStats(end))
val constr = readIndexedDef().asInstanceOf[DefDef]

def mergeTypeParamsAndAliases(tparams: List[TypeDef], stats: List[Tree]): (List[Tree], List[Tree]) =
def mergeTypeParamsAndAliases(tparams: List[TypeDef], stats: List[Tree])(implicit ctx: Context): (List[Tree], List[Tree]) =
(tparams, stats) match {
case (tparam :: tparams1, (alias: TypeDef) :: stats1)
if tparam.name == alias.name.expandedName(cls) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -932,9 +932,10 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
protected def deferredAnnot(end: Int)(implicit ctx: Context): Annotation = {
val start = readIndex
val atp = readTypeRef()
val phase = ctx.phase
Annotation.deferred(
atp.typeSymbol, implicit ctx1 =>
atReadPos(start, () => readAnnotationContents(end)(ctx1.withPhase(ctx.phase))))
atp.typeSymbol, implicit ctx =>
atReadPos(start, () => readAnnotationContents(end)(ctx.withPhase(phase))))
}

/* Read an abstract syntax tree */
Expand Down
12 changes: 12 additions & 0 deletions compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Contexts.Context, Scopes.Scope, Denotations.Denotation, Annotations.Annot
import StdNames.{nme, tpnme}
import ast.Trees._, ast._
import typer.Implicits._
import typer.ImportInfo
import config.Config
import java.lang.Integer.toOctalString
import config.Config.summarizeDepth
Expand Down Expand Up @@ -502,6 +503,17 @@ class PlainPrinter(_ctx: Context) extends Printer {
"?Unknown Implicit Result?"
}

def toText(importInfo: ImportInfo): Text = {
val siteStr = importInfo.site.show
val exprStr = if (siteStr endsWith ".type") siteStr dropRight 5 else siteStr
val selectorStr = importInfo.selectors match {
case Ident(name) :: Nil => name.show
case _ => "{...}"
}
s"import $exprStr.$selectorStr"
}


private var maxSummarized = Int.MaxValue

def summarized[T](depth: Int)(op: => T): T = {
Expand Down
4 changes: 4 additions & 0 deletions compiler/src/dotty/tools/dotc/printing/Printer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Texts._, ast.Trees._
import Types.Type, Symbols.Symbol, Contexts.Context, Scopes.Scope, Constants.Constant,
Names.Name, Denotations._, Annotations.Annotation
import typer.Implicits.SearchResult
import typer.ImportInfo

/** The base class of all printers
*/
Expand Down Expand Up @@ -98,6 +99,9 @@ abstract class Printer {
/** Textual representation of implicit search result */
def toText(result: SearchResult): Text

/** Textual representation of info relating to an import clause */
def toText(result: ImportInfo): Text

/** Perform string or text-producing operation `op` so that only a
* summarized text with given recursion depth is shown
*/
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/TailRec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
// now this speculatively transforms tree and throws away result in many cases
val rhsSemiTransformed = {
val transformer = new TailRecElimination(origMeth, dd.tparams, owner, thisTpe, mandatory, label, abstractOverClass = defIsTopLevel)
val rhs = atGroupEnd(transformer.transform(dd.rhs)(_))
val rhs = atGroupEnd(implicit ctx => transformer.transform(dd.rhs))
rewrote = transformer.rewrote
rhs
}
Expand Down
14 changes: 7 additions & 7 deletions compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
*
* { val xs = es; e' = e' + args }
*/
def typedOpAssign: Tree = track("typedOpAssign") {
def typedOpAssign(implicit ctx: Context): Tree = track("typedOpAssign") {
val Apply(Select(lhs, name), rhss) = tree
val lhs1 = typedExpr(lhs)
val liftedDefs = new mutable.ListBuffer[Tree]
Expand Down Expand Up @@ -805,16 +805,16 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
* whereas overloaded variants need to have a conforming variant.
*/
def trySelectUnapply(qual: untpd.Tree)(fallBack: Tree => Tree): Tree = {
val genericProto = new UnapplyFunProto(WildcardType, this)
def specificProto = new UnapplyFunProto(selType, this)
// try first for non-overloaded, then for overloaded ocurrences
def tryWithName(name: TermName)(fallBack: Tree => Tree)(implicit ctx: Context): Tree =
tryEither {
implicit ctx => typedExpr(untpd.Select(qual, name), specificProto)
tryEither { implicit ctx =>
val specificProto = new UnapplyFunProto(selType, this)
typedExpr(untpd.Select(qual, name), specificProto)
} {
(sel, _) =>
tryEither {
implicit ctx => typedExpr(untpd.Select(qual, name), genericProto)
tryEither { implicit ctx =>
val genericProto = new UnapplyFunProto(WildcardType, this)
typedExpr(untpd.Select(qual, name), genericProto)
} {
(_, _) => fallBack(sel)
}
Expand Down
47 changes: 23 additions & 24 deletions compiler/src/dotty/tools/dotc/typer/ImportInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package typer
import ast.{tpd, untpd}
import ast.Trees._
import core._
import printing.{Printer, Showable}
import util.SimpleMap
import Symbols._, Names._, Denotations._, Types._, Contexts._, StdNames._, Flags._
import Decorators.StringInterpolators
Expand All @@ -13,9 +14,9 @@ object ImportInfo {
/** The import info for a root import from given symbol `sym` */
def rootImport(refFn: () => TermRef)(implicit ctx: Context) = {
val selectors = untpd.Ident(nme.WILDCARD) :: Nil
def expr = tpd.Ident(refFn())
def imp = tpd.Import(expr, selectors)
new ImportInfo(imp.symbol, selectors, None, isRootImport = true)
def expr(implicit ctx: Context) = tpd.Ident(refFn())
def imp(implicit ctx: Context) = tpd.Import(expr, selectors)
new ImportInfo(implicit ctx => imp.symbol, selectors, None, isRootImport = true)
}
}

Expand All @@ -27,14 +28,14 @@ object ImportInfo {
* @param isRootImport true if this is one of the implicit imports of scala, java.lang,
* scala.Predef or dotty.DottyPredef in the start context, false otherwise.
*/
class ImportInfo(symf: => Symbol, val selectors: List[untpd.Tree],
symNameOpt: Option[TermName], val isRootImport: Boolean = false)(implicit ctx: Context) {
class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree],
symNameOpt: Option[TermName], val isRootImport: Boolean = false) extends Showable {

// Dotty deviation: we cannot use a lazy val here for the same reason
// that we cannot use one for `DottyPredefModuleRef`.
def sym = {
def sym(implicit ctx: Context) = {
if (mySym == null) {
mySym = symf
mySym = symf(ctx)
assert(mySym != null)
}
mySym
Expand Down Expand Up @@ -91,7 +92,7 @@ class ImportInfo(symf: => Symbol, val selectors: List[untpd.Tree],
}

/** The implicit references imported by this import clause */
def importedImplicits: List[TermRef] = {
def importedImplicits(implicit ctx: Context): List[TermRef] = {
val pre = site
if (isWildcardImport) {
val refs = pre.implicitMembers
Expand All @@ -115,23 +116,21 @@ class ImportInfo(symf: => Symbol, val selectors: List[untpd.Tree],
* override import Predef.{any2stringAdd => _, StringAdd => _, _} // disables String +
* override import java.lang.{} // disables all imports
*/
lazy val unimported: Symbol = {
lazy val sym = site.termSymbol
def maybeShadowsRoot = symNameOpt match {
case Some(symName) => defn.ShadowableImportNames.contains(symName)
case None => false
def unimported(implicit ctx: Context): Symbol = {
if (myUnimported == null) {
lazy val sym = site.termSymbol
def maybeShadowsRoot = symNameOpt match {
case Some(symName) => defn.ShadowableImportNames.contains(symName)
case None => false
}
myUnimported =
if (maybeShadowsRoot && defn.RootImportTypes.exists(_.symbol == sym)) sym
else NoSymbol
assert(myUnimported != null)
}
if (maybeShadowsRoot && defn.RootImportTypes.exists(_.symbol == sym)) sym
else NoSymbol
myUnimported
}
private[this] var myUnimported: Symbol = _

override def toString = {
val siteStr = site.show
val exprStr = if (siteStr endsWith ".type") siteStr dropRight 5 else siteStr
val selectorStr = selectors match {
case Ident(name) :: Nil => name.show
case _ => "{...}"
}
i"import $exprStr.$selectorStr"
}
def toText(printer: Printer) = printer.toText(this)
}
28 changes: 16 additions & 12 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import language.implicitConversions
import reporting.diagnostic.messages._

trait NamerContextOps { this: Context =>
import NamerContextOps._

/** Enter symbol into current class, if current class is owner of current context,
* or into current scope, if not. Should always be called instead of scope.enter
Expand Down Expand Up @@ -119,22 +120,25 @@ trait NamerContextOps { this: Context =>
else monotpe
}

/** Find moduleClass/sourceModule in effective scope */
private def findModuleBuddy(name: Name)(implicit ctx: Context) = {
val scope = effectiveScope
val it = scope.lookupAll(name).filter(_ is Module)
assert(it.hasNext, s"no companion $name in $scope")
it.next
}

/** Add moduleClass or sourceModule functionality to completer
* for a module or module class
*/
def adjustModuleCompleter(completer: LazyType, name: Name) =
def adjustModuleCompleter(completer: LazyType, name: Name) = {
val scope = this.effectiveScope
if (name.isTermName)
completer withModuleClass (_ => findModuleBuddy(name.moduleClassName))
completer withModuleClass (implicit ctx => findModuleBuddy(name.moduleClassName, scope))
else
completer withSourceModule (_ => findModuleBuddy(name.sourceModuleName))
completer withSourceModule (implicit ctx => findModuleBuddy(name.sourceModuleName, scope))
}
}

object NamerContextOps {
/** Find moduleClass/sourceModule in effective scope */
private def findModuleBuddy(name: Name, scope: Scope)(implicit ctx: Context) = {
val it = scope.lookupAll(name).filter(_ is Module)
assert(it.hasNext, s"no companion $name in $scope")
it.next
}
}

/** This class creates symbols from definitions and imports and gives them
Expand Down Expand Up @@ -378,7 +382,7 @@ class Namer { typer: Typer =>
case ref: RefTree => Some(ref.name.asTermName)
case _ => None
}
ctx.fresh.setImportInfo(new ImportInfo(sym, imp.selectors, impNameOpt))
ctx.fresh.setImportInfo(new ImportInfo(implicit ctx => sym, imp.selectors, impNameOpt))
}

/** A new context for the interior of a class */
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
*/
def bindingString(prec: Int, whereFound: Context, qualifier: String = "") =
if (prec == wildImport || prec == namedImport) {
ex"""imported$qualifier by ${hl"${whereFound.importInfo.toString}"}"""
ex"""imported$qualifier by ${hl"${whereFound.importInfo}"}"""
} else
ex"""defined$qualifier in ${hl"${whereFound.owner.toString}"}"""

Expand Down
2 changes: 1 addition & 1 deletion tests/repl/imports.check
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ scala> buf += xs
11 |buf += xs
| ^^
| found: scala.collection.immutable.List[Int](o.xs)
| required: String
| required: Int
|
scala> buf ++= xs
val res1: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3)
Expand Down