aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/typer
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2017-02-17 11:03:37 +0100
committerMartin Odersky <odersky@gmail.com>2017-02-17 11:17:17 +0100
commit9f26d32db25824f75e5c5b2c2314352c42b074c1 (patch)
tree97ac0e5dc62b19f89d1fc4a07339e2adb472cc4b /compiler/src/dotty/tools/dotc/typer
parent6df672c7e7be65d7be1cd6524c610aed4f35178c (diff)
downloaddotty-9f26d32db25824f75e5c5b2c2314352c42b074c1.tar.gz
dotty-9f26d32db25824f75e5c5b2c2314352c42b074c1.tar.bz2
dotty-9f26d32db25824f75e5c5b2c2314352c42b074c1.zip
Treat implicit by-name arguments as lazy values
With the previous rules, the two test cases produce a diverging implicit expansion. We avoid this by creating for every implicit by-name argument of type T a lazy implicit value of the same type. The implicit value is visible for all nested implicit searches of by-name arguments. That way, we tie the knot and obtain a recursive lazy value instead of a diverging expansion.
Diffstat (limited to 'compiler/src/dotty/tools/dotc/typer')
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Implicits.scala52
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Typer.scala2
2 files changed, 46 insertions, 8 deletions
diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala
index 6949221fb..82eb260a4 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,59 @@ 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 `DelayedIplicit` visible and
+ * stores in turn the given `lazyImplicit` as new `DelayedImplicit`.
+ */
+ def lazyImplicitCtx(lazyImplicit: Symbol): Context = {
+ val lctx = ctx.fresh
+ ctx.property(DelayedImplicit) match {
+ case Some(delayedRef) =>
+ lctx.setImplicits(new ContextualImplicits(delayedRef :: Nil, ctx.implicits)(ctx))
+ case None =>
+ }
+ lctx.setProperty(DelayedImplicit, lazyImplicit.termRef)
+ }
+
+ /** formalValue: The value type for which an implicit is searched
+ * lazyIplicit: 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 2b57cf778..16aeb4c6d 100644
--- a/compiler/src/dotty/tools/dotc/typer/Typer.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala
@@ -1885,7 +1885,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