Skip to content

Commit 7866bc2

Browse files
authored
Merge pull request #1775 from dotty-staging/add-implicit-funtypes
Add implicit function types
2 parents 18b8daa + 2e99511 commit 7866bc2

40 files changed

+1516
-191
lines changed

compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ import dotty.tools.dotc.core.Names.TypeName
3939
import scala.annotation.tailrec
4040

4141
class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Map[Symbol, Set[ClassSymbol]])(implicit ctx: Context) extends BackendInterface{
42+
import Symbols.{toDenot, toClassDenot}
43+
// Dotty deviation: Need to (re-)import implicit decorators here because otherwise
44+
// they would be shadowed by the more deeply nested `symHelper` decorator.
45+
4246
type Symbol = Symbols.Symbol
4347
type Type = Types.Type
4448
type Tree = tpd.Tree
@@ -683,8 +687,6 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
683687
else sym.enclosingClass(ctx.withPhase(ctx.flattenPhase.prev))
684688
} //todo is handled specially for JavaDefined symbols in scalac
685689

686-
687-
688690
// members
689691
def primaryConstructor: Symbol = toDenot(sym).primaryConstructor
690692

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class Compiler {
6161
new PatternMatcher, // Compile pattern matches
6262
new ExplicitOuter, // Add accessors to outer classes from nested ones.
6363
new ExplicitSelf, // Make references to non-trivial self types explicit as casts
64+
new ShortcutImplicits, // Allow implicit functions without creating closures
6465
new CrossCastAnd, // Normalize selections involving intersection types.
6566
new Splitter), // Expand selections involving union types into conditionals
6667
List(new VCInlineMethods, // Inlines calls to value class methods

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,13 @@ object desugar {
123123
else vdef
124124
}
125125

126+
def makeImplicitParameters(tpts: List[Tree], forPrimaryConstructor: Boolean)(implicit ctx: Context) =
127+
for (tpt <- tpts) yield {
128+
val paramFlags: FlagSet = if (forPrimaryConstructor) PrivateLocalParamAccessor else Param
129+
val epname = ctx.freshName(nme.EVIDENCE_PARAM_PREFIX).toTermName
130+
ValDef(epname, tpt, EmptyTree).withFlags(paramFlags | Implicit)
131+
}
132+
126133
/** Expand context bounds to evidence params. E.g.,
127134
*
128135
* def f[T >: L <: H : B](params)
@@ -143,19 +150,16 @@ object desugar {
143150
val epbuf = new ListBuffer[ValDef]
144151
def desugarContextBounds(rhs: Tree): Tree = rhs match {
145152
case ContextBounds(tbounds, cxbounds) =>
146-
for (cxbound <- cxbounds) {
147-
val paramFlags: FlagSet = if (isPrimaryConstructor) PrivateLocalParamAccessor else Param
148-
val epname = ctx.freshName(nme.EVIDENCE_PARAM_PREFIX).toTermName
149-
epbuf += ValDef(epname, cxbound, EmptyTree).withFlags(paramFlags | Implicit)
150-
}
153+
for (cxbound <- cxbounds)
154+
epbuf ++= makeImplicitParameters(cxbounds, isPrimaryConstructor)
151155
tbounds
152156
case PolyTypeTree(tparams, body) =>
153157
cpy.PolyTypeTree(rhs)(tparams, desugarContextBounds(body))
154158
case _ =>
155159
rhs
156160
}
157-
val tparams1 = tparams mapConserve { tdef =>
158-
cpy.TypeDef(tdef)(rhs = desugarContextBounds(tdef.rhs))
161+
val tparams1 = tparams mapConserve { tparam =>
162+
cpy.TypeDef(tparam)(rhs = desugarContextBounds(tparam.rhs))
159163
}
160164

161165
val meth1 = addEvidenceParams(cpy.DefDef(meth)(tparams = tparams1), epbuf.toList)
@@ -680,6 +684,11 @@ object desugar {
680684
Function(param :: Nil, Block(vdefs, body))
681685
}
682686

687+
def makeImplicitFunction(formals: List[Type], body: Tree)(implicit ctx: Context): Tree = {
688+
val params = makeImplicitParameters(formals.map(TypeTree), forPrimaryConstructor = false)
689+
new ImplicitFunction(params, body)
690+
}
691+
683692
/** Add annotation with class `cls` to tree:
684693
* tree @cls
685694
*/

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,16 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped]
290290
case _ => false
291291
}
292292

293+
/** Is `tree` an implicit function or closure, possibly nested in a block? */
294+
def isImplicitClosure(tree: Tree)(implicit ctx: Context): Boolean = unsplice(tree) match {
295+
case Function((param: untpd.ValDef) :: _, _) => param.mods.is(Implicit)
296+
case Closure(_, meth, _) => true
297+
case Block(Nil, expr) => isImplicitClosure(expr)
298+
case Block(DefDef(nme.ANON_FUN, _, (param :: _) :: _, _, _) :: Nil, _: Closure) =>
299+
param.mods.is(Implicit)
300+
case _ => false
301+
}
302+
293303
// todo: fill with other methods from TreeInfo that only apply to untpd.Tree's
294304
}
295305

@@ -501,7 +511,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
501511
*/
502512
object closure {
503513
def unapply(tree: Tree): Option[(List[Tree], Tree, Tree)] = tree match {
504-
case Block(_, Closure(env, meth, tpt)) => Some(env, meth, tpt)
514+
case Block(_, expr) => unapply(expr)
505515
case Closure(env, meth, tpt) => Some(env, meth, tpt)
506516
case _ => None
507517
}

compiler/src/dotty/tools/dotc/ast/Trees.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -890,6 +890,11 @@ object Trees {
890890
case tree: Select if (qualifier eq tree.qualifier) && (name == tree.name) => tree
891891
case _ => finalize(tree, untpd.Select(qualifier, name))
892892
}
893+
/** Copy Ident or Select trees */
894+
def Ref(tree: RefTree)(name: Name)(implicit ctx: Context) = tree match {
895+
case Ident(_) => Ident(tree)(name)
896+
case Select(qual, _) => Select(tree)(qual, name)
897+
}
893898
def This(tree: Tree)(qual: untpd.Ident): This = tree match {
894899
case tree: This if qual eq tree.qual => tree
895900
case _ => finalize(tree, untpd.This(qual))

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
450450
} else foldOver(sym, tree)
451451
}
452452

453-
override val cpy = new TypedTreeCopier
453+
override val cpy: TypedTreeCopier = // Type ascription needed to pick up any new members in TreeCopier (currently there are none)
454+
new TypedTreeCopier
454455

455456
class TypedTreeCopier extends TreeCopier {
456457
def postProcess(tree: Tree, copied: untpd.Tree): copied.ThisTree[Type] =

compiler/src/dotty/tools/dotc/ast/untpd.scala

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
5353
override def isTerm = body.isTerm
5454
override def isType = body.isType
5555
}
56+
57+
/** An implicit function type */
58+
class ImplicitFunction(args: List[Tree], body: Tree) extends Function(args, body) {
59+
override def toString = s"ImplicitFunction($args, $body)"
60+
}
61+
5662
/** A function created from a wildcard expression
5763
* @param placeHolderParams a list of definitions of synthetic parameters
5864
* @param body the function body where wildcards are replaced by
@@ -111,7 +117,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
111117

112118
case class Var() extends Mod(Flags.Mutable)
113119

114-
case class Implicit(flag: FlagSet = Flags.ImplicitCommon) extends Mod(flag)
120+
case class Implicit() extends Mod(Flags.ImplicitCommon)
115121

116122
case class Final() extends Mod(Flags.Final)
117123

@@ -270,8 +276,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
270276

271277
// ------ Additional creation methods for untyped only -----------------
272278

273-
// def TypeTree(tpe: Type): TypeTree = TypeTree().withType(tpe) todo: move to untpd/tpd
274-
275279
/** new pre.C[Ts](args1)...(args_n)
276280
* ==>
277281
* (new pre.C).<init>[Ts](args1)...(args_n)

compiler/src/dotty/tools/dotc/config/JavaPlatform.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class JavaPlatform extends Platform {
1818
currentClassPath = Some(new PathResolver().result)
1919
val cp = currentClassPath.get
2020
//println(cp)
21+
//println("------------------")
2122
cp
2223
}
2324

compiler/src/dotty/tools/dotc/core/Comments.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ object Comments {
119119
def apply(comment: Comment, code: String, codePos: Position)(implicit ctx: Context) =
120120
new UseCase(comment, code, codePos) {
121121
val untpdCode = {
122-
val tree = new Parser(new SourceFile("<usecase>", code)).localDef(codePos.start, EmptyFlags)
122+
val tree = new Parser(new SourceFile("<usecase>", code)).localDef(codePos.start)
123123

124124
tree match {
125125
case tree: untpd.DefDef =>

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 66 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -86,24 +86,51 @@ class Definitions {
8686
newClassSymbol(ScalaPackageClass, name, EmptyFlags, completer).entered
8787
}
8888

89-
/** The trait FunctionN, for some N */
90-
private def newFunctionNTrait(n: Int) = {
89+
/** The trait FunctionN or ImplicitFunctionN, for some N
90+
* @param name The name of the trait to be created
91+
*
92+
* FunctionN traits follow this template:
93+
*
94+
* trait FunctionN[T0,...T{N-1}, R] extends Object {
95+
* def apply($x0: T0, ..., $x{N_1}: T{N-1}): R
96+
* }
97+
*
98+
* That is, they follow the template given for Function2..Function22 in the
99+
* standard library, but without `tupled` and `curried` methods and without
100+
* a `toString`.
101+
*
102+
* ImplicitFunctionN traits follow this template:
103+
*
104+
* trait ImplicitFunctionN[T0,...,T{N-1}, R] extends Object with FunctionN[T0,...,T{N-1}, R] {
105+
* def apply(implicit $x0: T0, ..., $x{N_1}: T{N-1}): R
106+
* }
107+
*/
108+
private def newFunctionNTrait(name: TypeName) = {
91109
val completer = new LazyType {
92110
def complete(denot: SymDenotation)(implicit ctx: Context): Unit = {
93111
val cls = denot.asClass.classSymbol
94112
val decls = newScope
113+
val arity = name.functionArity
95114
val argParams =
96-
for (i <- List.range(0, n)) yield
97-
enterTypeParam(cls, s"T$i".toTypeName, Contravariant, decls)
98-
val resParam = enterTypeParam(cls, s"R".toTypeName, Covariant, decls)
115+
for (i <- List.range(0, arity)) yield
116+
enterTypeParam(cls, name ++ "$T" ++ i.toString, Contravariant, decls)
117+
val resParam = enterTypeParam(cls, name ++ "$R", Covariant, decls)
118+
val (methodType, parentTraits) =
119+
if (name.startsWith(tpnme.ImplicitFunction)) {
120+
val superTrait =
121+
FunctionType(arity).appliedTo(argParams.map(_.typeRef) ::: resParam.typeRef :: Nil)
122+
(ImplicitMethodType, ctx.normalizeToClassRefs(superTrait :: Nil, cls, decls))
123+
}
124+
else (MethodType, Nil)
99125
val applyMeth =
100126
decls.enter(
101127
newMethod(cls, nme.apply,
102-
MethodType(argParams.map(_.typeRef), resParam.typeRef), Deferred))
103-
denot.info = ClassInfo(ScalaPackageClass.thisType, cls, ObjectType :: Nil, decls)
128+
methodType(argParams.map(_.typeRef), resParam.typeRef), Deferred))
129+
denot.info =
130+
ClassInfo(ScalaPackageClass.thisType, cls, ObjectType :: parentTraits, decls)
104131
}
105132
}
106-
newClassSymbol(ScalaPackageClass, s"Function$n".toTypeName, Trait, completer)
133+
newClassSymbol(ScalaPackageClass, name, Trait, completer)
107134
}
108135

109136
private def newMethod(cls: ClassSymbol, name: TermName, info: Type, flags: FlagSet = EmptyFlags): TermSymbol =
@@ -590,15 +617,16 @@ class Definitions {
590617
sym.owner.linkedClass.typeRef
591618

592619
object FunctionOf {
593-
def apply(args: List[Type], resultType: Type)(implicit ctx: Context) =
594-
FunctionType(args.length).appliedTo(args ::: resultType :: Nil)
620+
def apply(args: List[Type], resultType: Type, isImplicit: Boolean = false)(implicit ctx: Context) =
621+
FunctionType(args.length, isImplicit).appliedTo(args ::: resultType :: Nil)
595622
def unapply(ft: Type)(implicit ctx: Context) = {
596623
val tsym = ft.typeSymbol
597-
if (isFunctionClass(tsym)) {
598-
lazy val targs = ft.argInfos
624+
val isImplicitFun = isImplicitFunctionClass(tsym)
625+
if (isImplicitFun || isFunctionClass(tsym)) {
626+
val targs = ft.argInfos
599627
val numArgs = targs.length - 1
600-
if (numArgs >= 0 && FunctionType(numArgs).symbol == tsym)
601-
Some(targs.init, targs.last)
628+
if (numArgs >= 0 && FunctionType(numArgs, isImplicitFun).symbol == tsym)
629+
Some(targs.init, targs.last, isImplicitFun)
602630
else None
603631
}
604632
else None
@@ -659,8 +687,12 @@ class Definitions {
659687
lazy val Function0_applyR = ImplementedFunctionType(0).symbol.requiredMethodRef(nme.apply)
660688
def Function0_apply(implicit ctx: Context) = Function0_applyR.symbol
661689

662-
def FunctionType(n: Int)(implicit ctx: Context): TypeRef =
663-
if (n < MaxImplementedFunctionArity) ImplementedFunctionType(n)
690+
def ImplicitFunctionClass(n: Int)(implicit ctx: Context) =
691+
ctx.requiredClass("scala.ImplicitFunction" + n.toString)
692+
693+
def FunctionType(n: Int, isImplicit: Boolean = false)(implicit ctx: Context): TypeRef =
694+
if (isImplicit && !ctx.erasedTypes) ImplicitFunctionClass(n).typeRef
695+
else if (n < MaxImplementedFunctionArity) ImplementedFunctionType(n)
664696
else FunctionClass(n).typeRef
665697

666698
private lazy val TupleTypes: Set[TypeRef] = TupleType.toSet
@@ -686,6 +718,7 @@ class Definitions {
686718
tp.derivesFrom(NothingClass) || tp.derivesFrom(NullClass)
687719

688720
def isFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.Function)
721+
def isImplicitFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.ImplicitFunction)
689722
def isUnimplementedFunctionClass(cls: Symbol) =
690723
isFunctionClass(cls) && cls.name.functionArity > MaxImplementedFunctionArity
691724
def isAbstractFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.AbstractFunction)
@@ -745,14 +778,21 @@ class Definitions {
745778
}
746779
else -1
747780

748-
def isFunctionType(tp: Type)(implicit ctx: Context) =
749-
isFunctionClass(tp.dealias.typeSymbol) && {
750-
val arity = functionArity(tp)
751-
arity >= 0 && tp.isRef(FunctionType(functionArity(tp)).typeSymbol)
752-
}
781+
/** Is `tp` (an alias) of either a scala.FunctionN or a scala.ImplicitFunctionN ? */
782+
def isFunctionType(tp: Type)(implicit ctx: Context) = {
783+
val arity = functionArity(tp)
784+
val sym = tp.dealias.typeSymbol
785+
arity >= 0 && (
786+
isFunctionClass(sym) && tp.isRef(FunctionType(arity, isImplicit = false).typeSymbol) ||
787+
isImplicitFunctionClass(sym) && tp.isRef(FunctionType(arity, isImplicit = true).typeSymbol)
788+
)
789+
}
753790

754791
def functionArity(tp: Type)(implicit ctx: Context) = tp.dealias.argInfos.length - 1
755792

793+
def isImplicitFunctionType(tp: Type)(implicit ctx: Context) =
794+
isFunctionType(tp) && tp.dealias.typeSymbol.name.startsWith(tpnme.ImplicitFunction)
795+
756796
// ----- primitive value class machinery ------------------------------------------
757797

758798
/** This class would also be obviated by the implicit function type design */
@@ -825,6 +865,9 @@ class Definitions {
825865

826866
// ----- Initialization ---------------------------------------------------
827867

868+
private def maxImplemented(name: Name) =
869+
if (name `startsWith` tpnme.Function) MaxImplementedFunctionArity else 0
870+
828871
/** Give the scala package a scope where a FunctionN trait is automatically
829872
* added when someone looks for it.
830873
*/
@@ -834,8 +877,8 @@ class Definitions {
834877
val newDecls = new MutableScope(oldDecls) {
835878
override def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = {
836879
val res = super.lookupEntry(name)
837-
if (res == null && name.functionArity > MaxImplementedFunctionArity)
838-
newScopeEntry(newFunctionNTrait(name.functionArity))
880+
if (res == null && name.isTypeName && name.functionArity > maxImplemented(name))
881+
newScopeEntry(newFunctionNTrait(name.asTypeName))
839882
else res
840883
}
841884
}

compiler/src/dotty/tools/dotc/core/NameOps.scala

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ object NameOps {
188188

189189
def errorName: N = likeTyped(name ++ nme.ERROR)
190190

191+
def directName: N = likeTyped(name ++ DIRECT_SUFFIX)
192+
191193
def freshened(implicit ctx: Context): N =
192194
likeTyped(
193195
if (name.isModuleClassName) name.stripModuleClassSuffix.freshened.moduleClassName
@@ -229,11 +231,14 @@ object NameOps {
229231
}
230232
}
231233

232-
def functionArity: Int =
233-
if (name.startsWith(tpnme.Function))
234-
try name.drop(tpnme.Function.length).toString.toInt
235-
catch { case ex: NumberFormatException => -1 }
236-
else -1
234+
def functionArity: Int = {
235+
def test(prefix: Name): Int =
236+
if (name.startsWith(prefix))
237+
try name.drop(prefix.length).toString.toInt
238+
catch { case ex: NumberFormatException => -1 }
239+
else -1
240+
test(tpnme.Function) max test(tpnme.ImplicitFunction)
241+
}
237242

238243
/** The name of the generic runtime operation corresponding to an array operation */
239244
def genericArrayOp: TermName = name match {

compiler/src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ object StdNames {
129129
val COMPANION_MODULE_METHOD: N = "companion$module"
130130
val COMPANION_CLASS_METHOD: N = "companion$class"
131131
val TRAIT_SETTER_SEPARATOR: N = "$_setter_$"
132+
val DIRECT_SUFFIX: N = "$direct"
132133

133134
// value types (and AnyRef) are all used as terms as well
134135
// as (at least) arguments to the @specialize annotation.
@@ -181,6 +182,7 @@ object StdNames {
181182
final val AnyVal: N = "AnyVal"
182183
final val ExprApi: N = "ExprApi"
183184
final val Function: N = "Function"
185+
final val ImplicitFunction: N = "ImplicitFunction"
184186
final val Mirror: N = "Mirror"
185187
final val Nothing: N = "Nothing"
186188
final val Null: N = "Null"

compiler/src/dotty/tools/dotc/core/Symbols.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,10 @@ object Symbols {
393393
denot
394394
}
395395

396+
/** The initial denotation of this symbol, without going through `current` */
397+
final def initialDenot(implicit ctx: Context): SymDenotation =
398+
lastDenot.initial
399+
396400
private[core] def defRunId: RunId =
397401
if (lastDenot == null) NoRunId else lastDenot.validFor.runId
398402

0 commit comments

Comments
 (0)