From bcc80ad1343a3ed01bef55f494d9658cf02226c6 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 5 Dec 2016 10:34:58 +0100 Subject: Create implicit closures to math expected implicit functions When the expected type is an implicit function, create an implicit closure to match it. --- .../tools/backend/jvm/DottyBackendInterface.scala | 6 ++-- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 23 ++++++++++----- compiler/src/dotty/tools/dotc/ast/TreeInfo.scala | 12 ++++++-- compiler/src/dotty/tools/dotc/ast/untpd.scala | 6 ++-- .../src/dotty/tools/dotc/core/Definitions.scala | 3 ++ .../src/dotty/tools/dotc/typer/Implicits.scala | 8 ++++-- compiler/src/dotty/tools/dotc/typer/Typer.scala | 33 +++++++++++++++------- 7 files changed, 64 insertions(+), 27 deletions(-) (limited to 'compiler/src/dotty/tools') diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index ed32d2df9..3b1768ef5 100644 --- a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -39,6 +39,10 @@ import dotty.tools.dotc.core.Names.TypeName import scala.annotation.tailrec class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Map[Symbol, Set[ClassSymbol]])(implicit ctx: Context) extends BackendInterface{ + import Symbols.{toDenot, toClassDenot} + // Dotty deviation: Need to (re-)import implicit decorators here because otherwise + // they would be shadowed by the more deeply nested `symHelper` decorator. + type Symbol = Symbols.Symbol type Type = Types.Type type Tree = tpd.Tree @@ -683,8 +687,6 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma else sym.enclosingClass(ctx.withPhase(ctx.flattenPhase.prev)) } //todo is handled specially for JavaDefined symbols in scalac - - // members def primaryConstructor: Symbol = toDenot(sym).primaryConstructor diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 11f8b81eb..def2caabf 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -123,6 +123,13 @@ object desugar { else vdef } + def makeImplicitParameters(tpts: List[Tree], forPrimaryConstructor: Boolean)(implicit ctx: Context) = + for (tpt <- tpts) yield { + val paramFlags: FlagSet = if (forPrimaryConstructor) PrivateLocalParamAccessor else Param + val epname = ctx.freshName(nme.EVIDENCE_PARAM_PREFIX).toTermName + ValDef(epname, tpt, EmptyTree).withFlags(paramFlags | Implicit) + } + /** Expand context bounds to evidence params. E.g., * * def f[T >: L <: H : B](params) @@ -143,19 +150,16 @@ object desugar { val epbuf = new ListBuffer[ValDef] def desugarContextBounds(rhs: Tree): Tree = rhs match { case ContextBounds(tbounds, cxbounds) => - for (cxbound <- cxbounds) { - val paramFlags: FlagSet = if (isPrimaryConstructor) PrivateLocalParamAccessor else Param - val epname = ctx.freshName(nme.EVIDENCE_PARAM_PREFIX).toTermName - epbuf += ValDef(epname, cxbound, EmptyTree).withFlags(paramFlags | Implicit) - } + for (cxbound <- cxbounds) + epbuf ++= makeImplicitParameters(cxbounds, isPrimaryConstructor) tbounds case PolyTypeTree(tparams, body) => cpy.PolyTypeTree(rhs)(tparams, desugarContextBounds(body)) case _ => rhs } - val tparams1 = tparams mapConserve { tdef => - cpy.TypeDef(tdef)(rhs = desugarContextBounds(tdef.rhs)) + val tparams1 = tparams mapConserve { tparam => + cpy.TypeDef(tparam)(rhs = desugarContextBounds(tparam)) } val meth1 = addEvidenceParams(cpy.DefDef(meth)(tparams = tparams1), epbuf.toList) @@ -679,6 +683,11 @@ object desugar { Function(param :: Nil, Block(vdefs, body)) } + def makeImplicitFunction(formals: List[Type], body: Tree)(implicit ctx: Context): Tree = { + val params = makeImplicitParameters(formals.map(TypeTree), forPrimaryConstructor = false) + new ImplicitFunction(params, body) + } + /** Add annotation with class `cls` to tree: * tree @cls */ diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index ae7c93784..da83d0644 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -290,6 +290,16 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped] case _ => false } + /** Is `tree` an implicit function or closure, possibly nested in a block? */ + def isImplicitClosure(tree: Tree)(implicit ctx: Context): Boolean = unsplice(tree) match { + case Function((param: untpd.ValDef) :: _, _) => param.mods.is(Implicit) + case Closure(_, meth, _) => true + case Block(Nil, expr) => isImplicitClosure(expr) + case Block(DefDef(nme.ANON_FUN, _, (param :: _) :: _, _, _) :: Nil, _: Closure) => + param.mods.is(Implicit) + case _ => false + } + // todo: fill with other methods from TreeInfo that only apply to untpd.Tree's } @@ -507,8 +517,6 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => } } - def isClosure(tree: Tree) = closure.unapply(tree).isDefined - /** If tree is a closure, its body, otherwise tree itself */ def closureBody(tree: Tree)(implicit ctx: Context): Tree = tree match { case Block((meth @ DefDef(nme.ANON_FUN, _, _, _, _)) :: Nil, Closure(_, _, _)) => meth.rhs diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 9707770d5..77755da81 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -55,7 +55,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { } /** An implicit function type */ - class ImplicitFunction(args: List[Tree], body: Tree) extends Function(args, body) + class ImplicitFunction(args: List[Tree], body: Tree) extends Function(args, body) { + override def toString = s"ImplicitFunction($args, $body" + } /** A function created from a wildcard expression * @param placeHolderParams a list of definitions of synthetic parameters @@ -274,8 +276,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { // ------ Additional creation methods for untyped only ----------------- - // def TypeTree(tpe: Type): TypeTree = TypeTree().withType(tpe) todo: move to untpd/tpd - /** new pre.C[Ts](args1)...(args_n) * ==> * (new pre.C).[Ts](args1)...(args_n) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index ff259f184..814bbf48f 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -790,6 +790,9 @@ class Definitions { def functionArity(tp: Type)(implicit ctx: Context) = tp.dealias.argInfos.length - 1 + def isImplicitFunctionType(tp: Type)(implicit ctx: Context) = + isFunctionType(tp) && tp.dealias.typeSymbol.name.startsWith(tpnme.ImplicitFunction) + // ----- primitive value class machinery ------------------------------------------ /** This class would also be obviated by the implicit function type design */ diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 79881dc5b..79cab8b74 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -29,7 +29,7 @@ import Inferencing.fullyDefinedType import Trees._ import Hashable._ import config.Config -import config.Printers.{implicits, implicitsDetailed} +import config.Printers.{implicits, implicitsDetailed, typr} import collection.mutable /** Implicit resolution */ @@ -146,7 +146,8 @@ object Implicits { override val level: Int = if (outerImplicits == null) 1 - else if (ctx.scope eq outerImplicits.ctx.scope) outerImplicits.level + else if ((ctx.owner eq outerImplicits.ctx.owner) && + (ctx.scope eq outerImplicits.ctx.scope)) outerImplicits.level else outerImplicits.level + 1 /** The implicit references that are eligible for type `tp`. */ @@ -211,7 +212,7 @@ object Implicits { * @param ctx The context after the implicit search */ case class SearchSuccess(tree: tpd.Tree, ref: TermRef, level: Int, tstate: TyperState) extends SearchResult { - override def toString = s"SearchSuccess($tree, $ref)" + override def toString = s"SearchSuccess($tree, $ref, $level)" } /** A failed search */ @@ -733,6 +734,7 @@ trait Implicits { self: Typer => case best :: alts => alts find (alt => isAsGood(alt.ref, best.ref, alt.level, best.level)(ctx.fresh.setExploreTyperState)) match { case Some(alt) => + typr.println(i"ambiguous implicits for $pt: ${best.ref} @ ${best.level}, ${alt.ref} @ ${alt.level}") /* !!! DEBUG println(i"ambiguous refs: ${hits map (_.ref) map (_.show) mkString ", "}") isAsGood(best.ref, alt.ref, explain = true)(ctx.fresh.withExploreTyperState) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 1967275e9..616c96019 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1494,6 +1494,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case tree: untpd.If => typedIf(tree, pt) case tree: untpd.Function => typedFunction(tree, pt) case tree: untpd.Closure => typedClosure(tree, pt) + case tree: untpd.Import => typedImport(tree, retrieveSym(tree)) case tree: untpd.Match => typedMatch(tree, pt) case tree: untpd.Return => typedReturn(tree) case tree: untpd.Try => typedTry(tree, pt) @@ -1521,9 +1522,13 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case _ => typedUnadapted(desugar(tree), pt) } - xtree match { + if (defn.isImplicitFunctionType(pt) && + xtree.isTerm && + !untpd.isImplicitClosure(xtree) && + !ctx.isAfterTyper) + makeImplicitFunction(xtree, pt) + else xtree match { case xtree: untpd.NameTree => typedNamed(encodeName(xtree), pt) - case xtree: untpd.Import => typedImport(xtree, retrieveSym(xtree)) case xtree => typedUnnamed(xtree) } } @@ -1532,6 +1537,14 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit protected def encodeName(tree: untpd.NameTree)(implicit ctx: Context): untpd.NameTree = untpd.rename(tree, tree.name.encode) + protected def makeImplicitFunction(tree: untpd.Tree, pt: Type)(implicit ctx: Context): Tree = { + val defn.FunctionOf(formals, resType, true) = pt.dealias + val paramTypes = formals.map(fullyDefinedType(_, "implicit function parameter", tree.pos)) + val ifun = desugar.makeImplicitFunction(paramTypes, tree) + typr.println(i"make implicit function $tree / $pt ---> $ifun") + typedUnadapted(ifun) + } + def typed(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = /*>|>*/ ctx.traceIndented (i"typing $tree", typr, show = true) /*<|<*/ { assertPositioned(tree) try adapt(typedUnadapted(tree, pt), pt, tree) @@ -1878,16 +1891,16 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit err.typeMismatch(tree, pt) else missingArgs - case wtp: RefinedType - if defn.isImplicitFunctionClass(wtp.underlyingClassRef(refinementOK = false).classSymbol) && - !isClosure(tree) && - !isApplyProto(pt) && - !ctx.isAfterTyper => - typr.println("insert apply on implicit $tree") - typed(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt) case _ => ctx.typeComparer.GADTused = false - if (ctx.mode is Mode.Pattern) { + if (defn.isImplicitFunctionClass(wtp.underlyingClassRef(refinementOK = false).classSymbol) && + !untpd.isImplicitClosure(tree) && + !isApplyProto(pt) && + !ctx.isAfterTyper) { + typr.println("insert apply on implicit $tree") + typed(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt) + } + else if (ctx.mode is Mode.Pattern) { tree match { case _: RefTree | _: Literal if !isVarPattern(tree) && -- cgit v1.2.3