Skip to content

Commit 0add896

Browse files
committed
Changes to implicit function types
- Introduce `|=>` syntax for implicit function types and implicit closures - Drop old `implicit A => B` syntax - Make implicit function types contextual, i.e. parameters have to be passed with `with`.
1 parent 5e9e3e6 commit 0add896

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+346
-239
lines changed

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,11 @@ object desugar {
150150
else vdef
151151
}
152152

153-
def makeImplicitParameters(tpts: List[Tree], forPrimaryConstructor: Boolean = false)(implicit ctx: Context): List[ValDef] =
153+
def makeImplicitParameters(tpts: List[Tree], contextualFlag: FlagSet = EmptyFlags, forPrimaryConstructor: Boolean = false)(implicit ctx: Context): List[ValDef] =
154154
for (tpt <- tpts) yield {
155155
val paramFlags: FlagSet = if (forPrimaryConstructor) PrivateLocalParamAccessor else Param
156156
val epname = EvidenceParamName.fresh()
157-
ValDef(epname, tpt, EmptyTree).withFlags(paramFlags | Implicit)
157+
ValDef(epname, tpt, EmptyTree).withFlags(paramFlags | Implicit | contextualFlag)
158158
}
159159

160160
/** 1. Expand context bounds to evidence params. E.g.,
@@ -189,7 +189,7 @@ object desugar {
189189
val epbuf = new ListBuffer[ValDef]
190190
def desugarContextBounds(rhs: Tree): Tree = rhs match {
191191
case ContextBounds(tbounds, cxbounds) =>
192-
epbuf ++= makeImplicitParameters(cxbounds, isPrimaryConstructor)
192+
epbuf ++= makeImplicitParameters(cxbounds, forPrimaryConstructor = isPrimaryConstructor)
193193
tbounds
194194
case LambdaTypeTree(tparams, body) =>
195195
cpy.LambdaTypeTree(rhs)(tparams, desugarContextBounds(body))
@@ -957,10 +957,10 @@ object desugar {
957957
* def $anonfun(params) = body
958958
* Closure($anonfun)
959959
*/
960-
def makeClosure(params: List[ValDef], body: Tree, tpt: Tree = TypeTree(), isImplicit: Boolean)(implicit ctx: Context): Block =
960+
def makeClosure(params: List[ValDef], body: Tree, tpt: Tree = TypeTree(), isContextual: Boolean)(implicit ctx: Context): Block =
961961
Block(
962962
DefDef(nme.ANON_FUN, Nil, params :: Nil, tpt, body).withMods(synthetic | Artifact),
963-
Closure(Nil, Ident(nme.ANON_FUN), if (isImplicit) ImplicitEmptyTree else EmptyTree))
963+
Closure(Nil, Ident(nme.ANON_FUN), if (isContextual) ContextualEmptyTree else EmptyTree))
964964

965965
/** If `nparams` == 1, expand partial function
966966
*
@@ -1013,9 +1013,9 @@ object desugar {
10131013
Function(param :: Nil, Block(vdefs, body))
10141014
}
10151015

1016-
def makeImplicitFunction(formals: List[Type], body: Tree)(implicit ctx: Context): Tree = {
1017-
val params = makeImplicitParameters(formals.map(TypeTree))
1018-
new FunctionWithMods(params, body, Modifiers(Implicit))
1016+
def makeContextualFunction(formals: List[Type], body: Tree)(implicit ctx: Context): Tree = {
1017+
val params = makeImplicitParameters(formals.map(TypeTree), Contextual)
1018+
new FunctionWithMods(params, body, Modifiers(Implicit | Contextual))
10191019
}
10201020

10211021
/** Add annotation to tree:

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -330,15 +330,15 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped]
330330
functionWithUnknownParamType(tree).isDefined
331331

332332
/** Is `tree` an implicit function or closure, possibly nested in a block? */
333-
def isImplicitClosure(tree: Tree)(implicit ctx: Context): Boolean = unsplice(tree) match {
334-
case tree: FunctionWithMods => tree.mods.is(Implicit)
335-
case Function((param: untpd.ValDef) :: _, _) => param.mods.is(Implicit)
333+
def isContextualClosure(tree: Tree)(implicit ctx: Context): Boolean = unsplice(tree) match {
334+
case tree: FunctionWithMods => tree.mods.is(Contextual)
335+
case Function((param: untpd.ValDef) :: _, _) => param.mods.is(Contextual)
336336
case Closure(_, meth, _) => true
337-
case Block(Nil, expr) => isImplicitClosure(expr)
337+
case Block(Nil, expr) => isContextualClosure(expr)
338338
case Block(DefDef(nme.ANON_FUN, _, params :: _, _, _) :: Nil, cl: Closure) =>
339339
params match {
340-
case param :: _ => param.mods.is(Implicit)
341-
case Nil => cl.tpt.eq(untpd.ImplicitEmptyTree) || defn.isImplicitFunctionType(cl.tpt.typeOpt)
340+
case param :: _ => param.mods.is(Contextual)
341+
case Nil => cl.tpt.eq(untpd.ContextualEmptyTree) || defn.isImplicitFunctionType(cl.tpt.typeOpt)
342342
}
343343
case _ => false
344344
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,7 @@ object Trees {
937937

938938
@sharable val EmptyTree: Thicket = genericEmptyTree
939939
@sharable val EmptyValDef: ValDef = genericEmptyValDef
940-
@sharable val ImplicitEmptyTree: Thicket = Thicket(Nil) // an empty tree marking an implicit closure
940+
@sharable val ContextualEmptyTree: Thicket = Thicket(Nil) // an empty tree marking a contextual closure
941941

942942
// ----- Auxiliary creation methods ------------------
943943

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

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ class Definitions {
9898
*
9999
* ImplicitFunctionN traits follow this template:
100100
*
101-
* trait ImplicitFunctionN[T0,...,T{N-1}, R] extends Object with FunctionN[T0,...,T{N-1}, R] {
102-
* def apply(implicit $x0: T0, ..., $x{N_1}: T{N-1}): R
101+
* trait ImplicitFunctionN[T0,...,T{N-1}, R] extends Object {
102+
* def apply with ($x0: T0, ..., $x{N_1}: T{N-1}): R
103103
* }
104104
*
105105
* ErasedFunctionN traits follow this template:
@@ -110,8 +110,8 @@ class Definitions {
110110
*
111111
* ErasedImplicitFunctionN traits follow this template:
112112
*
113-
* trait ErasedImplicitFunctionN[T0,...,T{N-1}, R] extends Object with ErasedFunctionN[T0,...,T{N-1}, R] {
114-
* def apply(erased implicit $x0: T0, ..., $x{N_1}: T{N-1}): R
113+
* trait ErasedImplicitFunctionN[T0,...,T{N-1}, R] extends Object {
114+
* def apply with (erased $x0: T0, ..., $x{N_1}: T{N-1}): R
115115
* }
116116
*
117117
* ErasedFunctionN and ErasedImplicitFunctionN erase to Function0.
@@ -127,13 +127,14 @@ class Definitions {
127127
enterTypeParam(cls, paramNamePrefix ++ "T" ++ (i + 1).toString, Contravariant, decls).typeRef
128128
}
129129
val resParamRef = enterTypeParam(cls, paramNamePrefix ++ "R", Covariant, decls).typeRef
130-
val methodType = MethodType.maker(isJava = false, name.isImplicitFunction, name.isErasedFunction)
131-
val parentTraits =
132-
if (!name.isImplicitFunction) Nil
133-
else FunctionType(arity, isErased = name.isErasedFunction).appliedTo(argParamRefs ::: resParamRef :: Nil) :: Nil
130+
val methodType = MethodType.maker(
131+
isJava = false,
132+
isImplicit = name.isImplicitFunction,
133+
isContextual = name.isImplicitFunction,
134+
isErased = name.isErasedFunction)
134135
decls.enter(newMethod(cls, nme.apply, methodType(argParamRefs, resParamRef), Deferred))
135136
denot.info =
136-
ClassInfo(ScalaPackageClass.thisType, cls, ObjectType :: parentTraits, decls)
137+
ClassInfo(ScalaPackageClass.thisType, cls, ObjectType :: Nil, decls)
137138
}
138139
}
139140
newClassSymbol(ScalaPackageClass, name, Trait | NoInits, completer)
@@ -830,9 +831,8 @@ class Definitions {
830831
sym.owner.linkedClass.typeRef
831832

832833
object FunctionOf {
833-
// TODO: make implicit function types contextual
834-
def apply(args: List[Type], resultType: Type, isImplicit: Boolean = false, isErased: Boolean = false)(implicit ctx: Context): Type =
835-
FunctionType(args.length, isImplicit, isErased).appliedTo(args ::: resultType :: Nil)
834+
def apply(args: List[Type], resultType: Type, isContextual: Boolean = false, isErased: Boolean = false)(implicit ctx: Context): Type =
835+
FunctionType(args.length, isContextual, isErased).appliedTo(args ::: resultType :: Nil)
836836
def unapply(ft: Type)(implicit ctx: Context): Option[(List[Type], Type, Boolean, Boolean)] = {
837837
val tsym = ft.typeSymbol
838838
if (isFunctionClass(tsym)) {
@@ -920,10 +920,10 @@ class Definitions {
920920

921921
lazy val TupleType: Array[TypeRef] = mkArityArray("scala.Tuple", MaxTupleArity, 1)
922922

923-
def FunctionClass(n: Int, isImplicit: Boolean = false, isErased: Boolean = false)(implicit ctx: Context): Symbol =
924-
if (isImplicit && isErased)
923+
def FunctionClass(n: Int, isContextual: Boolean = false, isErased: Boolean = false)(implicit ctx: Context): Symbol =
924+
if (isContextual && isErased)
925925
ctx.requiredClass("scala.ErasedImplicitFunction" + n.toString)
926-
else if (isImplicit)
926+
else if (isContextual)
927927
ctx.requiredClass("scala.ImplicitFunction" + n.toString)
928928
else if (isErased)
929929
ctx.requiredClass("scala.ErasedFunction" + n.toString)
@@ -935,9 +935,9 @@ class Definitions {
935935
lazy val Function0_applyR: TermRef = ImplementedFunctionType(0).symbol.requiredMethodRef(nme.apply)
936936
def Function0_apply(implicit ctx: Context): Symbol = Function0_applyR.symbol
937937

938-
def FunctionType(n: Int, isImplicit: Boolean = false, isErased: Boolean = false)(implicit ctx: Context): TypeRef =
939-
if (n <= MaxImplementedFunctionArity && (!isImplicit || ctx.erasedTypes) && !isErased) ImplementedFunctionType(n)
940-
else FunctionClass(n, isImplicit, isErased).typeRef
938+
def FunctionType(n: Int, isContextual: Boolean = false, isErased: Boolean = false)(implicit ctx: Context): TypeRef =
939+
if (n <= MaxImplementedFunctionArity && (!isContextual || ctx.erasedTypes) && !isErased) ImplementedFunctionType(n)
940+
else FunctionClass(n, isContextual, isErased).typeRef
941941

942942
/** If `cls` is a class in the scala package, its name, otherwise EmptyTypeName */
943943
def scalaClassName(cls: Symbol)(implicit ctx: Context): TypeName =

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

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,9 @@ object Types {
337337
/** Is this a Method or PolyType which has contextual parameters as first value parameter list? */
338338
def isContextual: Boolean = false
339339

340+
/** Is this a Method or PolyType which has implicit parameters as first value parameter list? */
341+
def isImplicit: Boolean = false
342+
340343
/** Is this a MethodType for which the parameters will not be used */
341344
def isErasedMethod: Boolean = false
342345

@@ -1432,11 +1435,11 @@ object Types {
14321435
def toFunctionType(dropLast: Int = 0)(implicit ctx: Context): Type = this match {
14331436
case mt: MethodType if !mt.isParamDependent =>
14341437
val formals1 = if (dropLast == 0) mt.paramInfos else mt.paramInfos dropRight dropLast
1435-
val isImplicit = mt.isImplicitMethod && !ctx.erasedTypes
1438+
val isContextual = mt.isContextual && !ctx.erasedTypes
14361439
val isErased = mt.isErasedMethod && !ctx.erasedTypes
14371440
val funType = defn.FunctionOf(
14381441
formals1 mapConserve (_.underlyingIfRepeated(mt.isJavaMethod)),
1439-
mt.nonDependentResultApprox, isImplicit, isErased)
1442+
mt.nonDependentResultApprox, isContextual, isErased)
14401443
if (mt.isResultDependent) RefinedType(funType, nme.apply, mt)
14411444
else funType
14421445
}
@@ -3056,10 +3059,17 @@ object Types {
30563059

30573060
final override def isJavaMethod: Boolean = companion eq JavaMethodType
30583061
final override def isImplicitMethod: Boolean =
3059-
companion.eq(ImplicitMethodType) || companion.eq(ErasedImplicitMethodType) || isContextual
3060-
final override def isErasedMethod: Boolean = companion.eq(ErasedMethodType) || companion.eq(ErasedImplicitMethodType)
3062+
companion.eq(ImplicitMethodType) ||
3063+
companion.eq(ErasedImplicitMethodType) ||
3064+
isContextual
3065+
final override def isErasedMethod: Boolean =
3066+
companion.eq(ErasedMethodType) ||
3067+
companion.eq(ErasedImplicitMethodType) ||
3068+
companion.eq(ErasedContextualMethodType)
30613069
final override def isContextual: Boolean =
3062-
companion.eq(ContextualMethodType) || companion.eq(ErasedContextualMethodType)
3070+
companion.eq(ContextualMethodType) ||
3071+
companion.eq(ErasedContextualMethodType)
3072+
final override def isImplicit = isImplicitMethod
30633073

30643074
def computeSignature(implicit ctx: Context): Signature = {
30653075
val params = if (isErasedMethod) Nil else paramInfos
@@ -3252,6 +3262,7 @@ object Types {
32523262
def computeSignature(implicit ctx: Context): Signature = resultSignature
32533263

32543264
override def isContextual = resType.isContextual
3265+
override def isImplicit = resType.isImplicit
32553266

32563267
/** Merge nested polytypes into one polytype. nested polytypes are normally not supported
32573268
* but can arise as temporary data structures.

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -352,15 +352,15 @@ object Parsers {
352352

353353
/** Convert tree to formal parameter list
354354
*/
355-
def convertToParams(tree: Tree): List[ValDef] = tree match {
356-
case Parens(t) => convertToParam(t) :: Nil
357-
case Tuple(ts) => ts map (convertToParam(_))
358-
case t => convertToParam(t) :: Nil
355+
def convertToParams(tree: Tree, mods: Modifiers): List[ValDef] = tree match {
356+
case Parens(t) => convertToParam(t, mods) :: Nil
357+
case Tuple(ts) => ts map (convertToParam(_, mods))
358+
case t => convertToParam(t, mods) :: Nil
359359
}
360360

361361
/** Convert tree to formal parameter
362362
*/
363-
def convertToParam(tree: Tree, mods: Modifiers = Modifiers(), expected: String = "formal parameter"): ValDef = tree match {
363+
def convertToParam(tree: Tree, mods: Modifiers, expected: String = "formal parameter"): ValDef = tree match {
364364
case Ident(name) =>
365365
makeParameter(name.asTermName, TypeTree(), mods) withPos tree.pos
366366
case Typed(Ident(name), tpt) =>
@@ -754,7 +754,7 @@ object Parsers {
754754
*/
755755
def toplevelTyp(): Tree = checkWildcard(typ())
756756

757-
/** Type ::= [FunArgMods] FunArgTypes `=>' Type
757+
/** Type ::= [‘erased’] FunArgTypes (‘=>’ | ‘|=>’) Type
758758
* | HkTypeParamClause `->' Type
759759
* | InfixType
760760
* FunArgTypes ::= InfixType
@@ -763,11 +763,20 @@ object Parsers {
763763
*/
764764
def typ(): Tree = {
765765
val start = in.offset
766-
val imods = modifiers(funArgMods)
766+
val imods = modifiers(BitSet(ERASED))
767767
def functionRest(params: List[Tree]): Tree =
768-
atPos(start, accept(ARROW)) {
768+
atPos(start, in.offset) {
769+
val pmods =
770+
if (in.token == CARROW) {
771+
in.nextToken()
772+
imods | (Contextual | Implicit)
773+
}
774+
else {
775+
accept(ARROW)
776+
imods
777+
}
769778
val t = typ()
770-
if (imods.is(Implicit) || imods.is(Erased)) new FunctionWithMods(params, t, imods)
779+
if (pmods.flags.is(Implicit | Contextual | Erased)) new FunctionWithMods(params, t, pmods)
771780
else Function(params, t)
772781
}
773782
def funArgTypesRest(first: Tree, following: () => Tree) = {
@@ -801,7 +810,8 @@ object Parsers {
801810
}
802811
openParens.change(LPAREN, -1)
803812
accept(RPAREN)
804-
if (imods.is(Implicit) || isValParamList || in.token == ARROW) functionRest(ts)
813+
if (imods.is(Implicit) || isValParamList || in.token == ARROW || in.token == CARROW)
814+
functionRest(ts)
805815
else {
806816
val ts1 =
807817
for (t <- ts) yield {
@@ -832,7 +842,7 @@ object Parsers {
832842
else infixType()
833843

834844
in.token match {
835-
case ARROW => functionRest(t :: Nil)
845+
case ARROW | CARROW => functionRest(t :: Nil)
836846
case MATCH => matchType(EmptyTree, t)
837847
case FORSOME => syntaxError(ExistentialTypesNoLongerSupported()); t
838848
case _ =>
@@ -1109,6 +1119,7 @@ object Parsers {
11091119
}
11101120

11111121
/** Expr ::= [FunArgMods] FunParams =>' Expr
1122+
* | [‘erased’] FunParams ‘|=>’ Expr
11121123
* | Expr1
11131124
* FunParams ::= Bindings
11141125
* | id
@@ -1157,9 +1168,11 @@ object Parsers {
11571168
finally placeholderParams = saved
11581169

11591170
val t = expr1(location)
1160-
if (in.token == ARROW) {
1171+
if (in.token == ARROW || in.token == CARROW) {
11611172
placeholderParams = Nil // don't interpret `_' to the left of `=>` as placeholder
1162-
wrapPlaceholders(closureRest(start, location, convertToParams(t)))
1173+
val impliedMods =
1174+
if (in.token == CARROW) Modifiers(Implicit | Contextual) else EmptyModifiers
1175+
wrapPlaceholders(closureRest(start, location, convertToParams(t, impliedMods)))
11631176
}
11641177
else if (isWildcard(t)) {
11651178
placeholderParams = placeholderParams ::: saved
@@ -1340,16 +1353,28 @@ object Parsers {
13401353
}
13411354
else ident()
13421355

1343-
/** Expr ::= implicit id `=>' Expr
1356+
/** Expr ::= FunArgMods FunParams `=>' Expr
1357+
* | [‘erased’] FunParams ‘|=>’ Expr
13441358
* BlockResult ::= implicit id [`:' InfixType] `=>' Block // Scala2 only
13451359
*/
13461360
def implicitClosure(start: Int, location: Location.Value, implicitMods: Modifiers): Tree =
13471361
closureRest(start, location, funParams(implicitMods, location))
13481362

13491363
def closureRest(start: Int, location: Location.Value, params: List[Tree]): Tree =
13501364
atPos(start, in.offset) {
1351-
accept(ARROW)
1352-
Function(params, if (location == Location.InBlock) block() else expr())
1365+
val params1 =
1366+
if (in.token == CARROW) {
1367+
in.nextToken()
1368+
params.map {
1369+
case param: ValDef => param.withMods(param.mods | (Implicit | Contextual))
1370+
case param => param
1371+
}
1372+
}
1373+
else {
1374+
accept(ARROW)
1375+
params
1376+
}
1377+
Function(params1, if (location == Location.InBlock) block() else expr())
13531378
}
13541379

13551380
/** PostfixExpr ::= InfixExpr [id [nl]]
@@ -2622,7 +2647,7 @@ object Parsers {
26222647
case Typed(tree @ This(EmptyTypeIdent), tpt) =>
26232648
self = makeSelfDef(nme.WILDCARD, tpt).withPos(first.pos)
26242649
case _ =>
2625-
val ValDef(name, tpt, _) = convertToParam(first, expected = "self type clause")
2650+
val ValDef(name, tpt, _) = convertToParam(first, EmptyModifiers, "self type clause")
26262651
if (name != nme.ERROR)
26272652
self = makeSelfDef(name, tpt).withPos(first.pos)
26282653
}

compiler/src/dotty/tools/dotc/parsing/Tokens.scala

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ abstract class TokensCommon {
112112
//final val SUPERTYPE = 81; enter(SUPERTYPE, ">:")
113113
//final val HASH = 82; enter(HASH, "#")
114114
final val AT = 83; enter(AT, "@")
115-
//final val VIEWBOUND = 84; enter(VIEWBOUND, "<%") // TODO: deprecate
115+
//final val CARROW = 84;
116+
//final val VIEWBOUND = 85; enter(VIEWBOUND, "<%") // TODO: deprecate
116117

117118
val keywords: TokenSet
118119

@@ -192,10 +193,11 @@ object Tokens extends TokensCommon {
192193
final val SUBTYPE = 80; enter(SUBTYPE, "<:")
193194
final val SUPERTYPE = 81; enter(SUPERTYPE, ">:")
194195
final val HASH = 82; enter(HASH, "#")
195-
final val VIEWBOUND = 84; enter(VIEWBOUND, "<%") // TODO: deprecate
196-
final val QPAREN = 85; enter(QPAREN, "'(")
197-
final val QBRACE = 86; enter(QBRACE, "'{")
198-
final val QBRACKET = 87; enter(QBRACKET, "'[")
196+
final val CARROW = 84; enter(CARROW, "|=>")
197+
final val VIEWBOUND = 85; enter(VIEWBOUND, "<%") // TODO: deprecate
198+
final val QPAREN = 86; enter(QPAREN, "'(")
199+
final val QBRACE = 87; enter(QBRACE, "'{")
200+
final val QBRACKET = 88; enter(QBRACKET, "'[")
199201

200202
/** XML mode */
201203
final val XMLSTART = 96; enter(XMLSTART, "$XMLSTART$<") // TODO: deprecate

0 commit comments

Comments
 (0)