aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools
diff options
context:
space:
mode:
authorodersky <odersky@gmail.com>2017-02-25 19:13:58 +0100
committerGitHub <noreply@github.com>2017-02-25 19:13:58 +0100
commit8467e5551ec7733c876c6764db1601aedc9767b9 (patch)
tree40bfc3ad04c3bf42696c27a34a35bad686940af6 /compiler/src/dotty/tools
parent8a0a4fbdb7fc118a44cbc3dbd85095093717e7b4 (diff)
parentd8c7a7ae0e981a2abd9e973617ef575270dd30a5 (diff)
downloaddotty-8467e5551ec7733c876c6764db1601aedc9767b9.tar.gz
dotty-8467e5551ec7733c876c6764db1601aedc9767b9.tar.bz2
dotty-8467e5551ec7733c876c6764db1601aedc9767b9.zip
Merge pull request #1993 from dotty-staging/add-lazy-implicits
Treat implicit by-name arguments as lazy values
Diffstat (limited to 'compiler/src/dotty/tools')
-rw-r--r--compiler/src/dotty/tools/dotc/core/Contexts.scala3
-rw-r--r--compiler/src/dotty/tools/dotc/core/StdNames.scala1
-rw-r--r--compiler/src/dotty/tools/dotc/core/Symbols.scala4
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Implicits.scala49
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Typer.scala2
5 files changed, 50 insertions, 9 deletions
diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala
index 29629e505..6b7be12be 100644
--- a/compiler/src/dotty/tools/dotc/core/Contexts.scala
+++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala
@@ -202,7 +202,7 @@ object Contexts {
private[core] var pendingMemberSearches: List[Name] = Nil
/** The new implicit references that are introduced by this scope */
- private var implicitsCache: ContextualImplicits = null
+ protected var implicitsCache: ContextualImplicits = null
def implicits: ContextualImplicits = {
if (implicitsCache == null )
implicitsCache = {
@@ -469,6 +469,7 @@ object Contexts {
def setTypeAssigner(typeAssigner: TypeAssigner): this.type = { this.typeAssigner = typeAssigner; this }
def setTyper(typer: Typer): this.type = { this.scope = typer.scope; setTypeAssigner(typer) }
def setImportInfo(importInfo: ImportInfo): this.type = { this.importInfo = importInfo; this }
+ def setImplicits(implicits: ContextualImplicits): this.type = { this.implicitsCache = implicits; this }
def setRunInfo(runInfo: RunInfo): this.type = { this.runInfo = runInfo; this }
def setDiagnostics(diagnostics: Option[StringBuilder]): this.type = { this.diagnostics = diagnostics; this }
def setGadt(gadt: GADTMap): this.type = { this.gadt = gadt; this }
diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala
index c424c6182..1e36361f8 100644
--- a/compiler/src/dotty/tools/dotc/core/StdNames.scala
+++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala
@@ -130,6 +130,7 @@ object StdNames {
val COMPANION_CLASS_METHOD: N = "companion$class"
val TRAIT_SETTER_SEPARATOR: N = "$_setter_$"
val DIRECT_SUFFIX: N = "$direct"
+ val LAZY_IMPLICIT_PREFIX: N = "$lazy_implicit$"
// value types (and AnyRef) are all used as terms as well
// as (at least) arguments to the @specialize annotation.
diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala
index 5d0dd2123..c5e064478 100644
--- a/compiler/src/dotty/tools/dotc/core/Symbols.scala
+++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala
@@ -258,6 +258,10 @@ trait Symbols { this: Context =>
def newDefaultConstructor(cls: ClassSymbol) =
newConstructor(cls, EmptyFlags, Nil, Nil)
+ /** Create a synthetic lazy implicit value */
+ def newLazyImplicit(info: Type) =
+ newSymbol(owner, freshName(nme.LAZY_IMPLICIT_PREFIX).toTermName, Lazy, info)
+
/** Create a symbol representing a selftype declaration for class `cls`. */
def newSelfSym(cls: ClassSymbol, name: TermName = nme.WILDCARD, selfInfo: Type = NoType): TermSymbol =
ctx.newSymbol(cls, name, SelfSymFlags, selfInfo orElse cls.classInfo.selfType, coord = cls.coord)
diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala
index 48cf0cfac..d5afae90c 100644
--- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala
@@ -29,6 +29,7 @@ import reporting.diagnostic.MessageContainer
import Inferencing.fullyDefinedType
import Trees._
import Hashable._
+import util.Property
import config.Config
import config.Printers.{implicits, implicitsDetailed, typr}
import collection.mutable
@@ -36,6 +37,11 @@ import collection.mutable
/** Implicit resolution */
object Implicits {
+ /** A reference to an implicit value to be made visible on the next nested call to
+ * inferImplicitArg with a by-name expected type.
+ */
+ val DelayedImplicit = new Property.Key[TermRef]
+
/** An eligible implicit candidate, consisting of an implicit reference and a nesting level */
case class Candidate(ref: TermRef, level: Int)
@@ -202,7 +208,7 @@ object Implicits {
}
override def toString = {
- val own = s"(implicits: ${refs mkString ","})"
+ val own = i"(implicits: $refs%, %)"
if (isOuterMost) own else own + "\n " + outerImplicits
}
@@ -537,27 +543,56 @@ trait Implicits { self: Typer =>
else EmptyTree
}
- inferImplicit(formal, EmptyTree, pos) match {
+ /** The context to be used when resolving a by-name implicit argument.
+ * This makes any implicit stored under `DelayedImplicit` visible and
+ * stores in turn the given `lazyImplicit` as new `DelayedImplicit`.
+ */
+ def lazyImplicitCtx(lazyImplicit: Symbol): Context = {
+ val lctx = ctx.fresh
+ for (delayedRef <- ctx.property(DelayedImplicit))
+ lctx.setImplicits(new ContextualImplicits(delayedRef :: Nil, ctx.implicits)(ctx))
+ lctx.setProperty(DelayedImplicit, lazyImplicit.termRef)
+ }
+
+ /** formalValue: The value type for which an implicit is searched
+ * lazyImplicit: An implicit symbol to install for nested by-name resolutions
+ * argCtx : The context to be used for searching the implicit argument
+ */
+ val (formalValue, lazyImplicit, argCtx) = formal match {
+ case ExprType(fv) =>
+ val lazyImplicit = ctx.newLazyImplicit(fv)
+ (fv, lazyImplicit, lazyImplicitCtx(lazyImplicit))
+ case _ => (formal, NoSymbol, ctx)
+ }
+
+ inferImplicit(formalValue, EmptyTree, pos)(argCtx) match {
case SearchSuccess(arg, _, _, _) =>
- arg
+ def refersToLazyImplicit = arg.existsSubTree {
+ case id: Ident => id.symbol == lazyImplicit
+ case _ => false
+ }
+ if (lazyImplicit.exists && refersToLazyImplicit)
+ Block(ValDef(lazyImplicit.asTerm, arg).withPos(pos) :: Nil, ref(lazyImplicit))
+ else
+ arg
case ambi: AmbiguousImplicits =>
error(where => s"ambiguous implicits: ${ambi.explanation} of $where")
EmptyTree
case failure: SearchFailure =>
- val arg = synthesizedClassTag(formal, pos)
+ val arg = synthesizedClassTag(formalValue, pos)
if (!arg.isEmpty) arg
else {
var msgFn = (where: String) =>
em"no implicit argument of type $formal found for $where" + failure.postscript
for {
- notFound <- formal.typeSymbol.getAnnotation(defn.ImplicitNotFoundAnnot)
+ notFound <- formalValue.typeSymbol.getAnnotation(defn.ImplicitNotFoundAnnot)
Trees.Literal(Constant(raw: String)) <- notFound.argument(0)
} {
msgFn = where =>
err.implicitNotFoundString(
raw,
- formal.typeSymbol.typeParams.map(_.name.unexpandedName.toString),
- formal.argInfos)
+ formalValue.typeSymbol.typeParams.map(_.name.unexpandedName.toString),
+ formalValue.argInfos)
}
error(msgFn)
EmptyTree
diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala
index 06200d3e4..33e320ce5 100644
--- a/compiler/src/dotty/tools/dotc/typer/Typer.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala
@@ -1890,7 +1890,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
def implicitArgError(msg: String => String) =
errors += (() => msg(em"parameter $pname of $methodStr"))
if (errors.nonEmpty) EmptyTree
- else inferImplicitArg(formal.widenExpr, implicitArgError, tree.pos.endPos)
+ else inferImplicitArg(formal, implicitArgError, tree.pos.endPos)
}
if (errors.nonEmpty) {
// If there are several arguments, some arguments might already