diff options
author | odersky <odersky@gmail.com> | 2017-02-25 19:13:58 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-02-25 19:13:58 +0100 |
commit | 8467e5551ec7733c876c6764db1601aedc9767b9 (patch) | |
tree | 40bfc3ad04c3bf42696c27a34a35bad686940af6 /compiler/src/dotty/tools | |
parent | 8a0a4fbdb7fc118a44cbc3dbd85095093717e7b4 (diff) | |
parent | d8c7a7ae0e981a2abd9e973617ef575270dd30a5 (diff) | |
download | dotty-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')
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 |