Skip to content

Commit 23a6d60

Browse files
committed
Introduce contextual method types
1 parent 9ac0ee0 commit 23a6d60

File tree

15 files changed

+82
-40
lines changed

15 files changed

+82
-40
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
226226
val previousParamRefs = if (isParamDependent) new mutable.ListBuffer[TermRef]() else null
227227

228228
def valueParam(name: TermName, origInfo: Type): TermSymbol = {
229-
val maybeImplicit = if (tp.isImplicitMethod) Implicit else EmptyFlags
229+
val maybeImplicit =
230+
if (tp.isContextualMethod) Implicit | Contextual
231+
else if (tp.isImplicitMethod) Implicit
232+
else EmptyFlags
230233
val maybeErased = if (tp.isErasedMethod) Erased else EmptyFlags
231234

232235
def makeSym(info: Type) = ctx.newSymbol(sym, name, TermParam | maybeImplicit | maybeErased, info, coord = sym.coord)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
5555
override def isType: Boolean = body.isType
5656
}
5757

58-
/** A function type with `implicit` or `erased` modifiers */
58+
/** A function type with `implicit`, `erased`, or `contextual` modifiers */
5959
class FunctionWithMods(args: List[Tree], body: Tree, val mods: Modifiers) extends Function(args, body)
6060

6161
/** A function created from a wildcard expression

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,7 @@ class Definitions {
830830
sym.owner.linkedClass.typeRef
831831

832832
object FunctionOf {
833+
// TODO: make implicit function types contextual
833834
def apply(args: List[Type], resultType: Type, isImplicit: Boolean = false, isErased: Boolean = false)(implicit ctx: Context): Type =
834835
FunctionType(args.length, isImplicit, isErased).appliedTo(args ::: resultType :: Nil)
835836
def unapply(ft: Type)(implicit ctx: Context): Option[(List[Type], Type, Boolean, Boolean)] = {

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

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ object Types {
7272
* +- OrType
7373
* +- MethodOrPoly ---+-- PolyType
7474
* +-- MethodType ---+- ImplicitMethodType
75+
* +- ContextualMethodType
7576
* | +- JavaMethodType
7677
* +- ClassInfo
7778
* |
@@ -330,9 +331,12 @@ object Types {
330331
/** Is this a MethodType which is from Java */
331332
def isJavaMethod: Boolean = false
332333

333-
/** Is this a MethodType which has implicit parameters */
334+
/** Is this a MethodType which has implicit or contextual parameters */
334335
def isImplicitMethod: Boolean = false
335336

337+
/** Is this a MethodType which has contextual parameters */
338+
def isContextualMethod: Boolean = false
339+
336340
/** Is this a MethodType for which the parameters will not be used */
337341
def isErasedMethod: Boolean = false
338342

@@ -3051,15 +3055,18 @@ object Types {
30513055
def companion: MethodTypeCompanion
30523056

30533057
final override def isJavaMethod: Boolean = companion eq JavaMethodType
3054-
final override def isImplicitMethod: Boolean = companion.eq(ImplicitMethodType) || companion.eq(ErasedImplicitMethodType)
3058+
final override def isImplicitMethod: Boolean =
3059+
companion.eq(ImplicitMethodType) || companion.eq(ErasedImplicitMethodType) || isContextualMethod
3060+
final override def isContextualMethod: Boolean =
3061+
companion.eq(ContextualMethodType) || companion.eq(ErasedContextualMethodType)
30553062
final override def isErasedMethod: Boolean = companion.eq(ErasedMethodType) || companion.eq(ErasedImplicitMethodType)
30563063

30573064
def computeSignature(implicit ctx: Context): Signature = {
30583065
val params = if (isErasedMethod) Nil else paramInfos
30593066
resultSignature.prepend(params, isJavaMethod)
30603067
}
30613068

3062-
protected def prefixString: String = "MethodType"
3069+
protected def prefixString: String = companion.prefixString
30633070
}
30643071

30653072
final class CachedMethodType(paramNames: List[TermName])(paramInfosExp: MethodType => List[Type], resultTypeExp: MethodType => Type, val companion: MethodTypeCompanion)
@@ -3108,7 +3115,7 @@ object Types {
31083115
def syntheticParamName(n: Int): TypeName = tpnme.syntheticTypeParamName(n)
31093116
}
31103117

3111-
abstract class MethodTypeCompanion extends TermLambdaCompanion[MethodType] { self =>
3118+
abstract class MethodTypeCompanion(val prefixString: String) extends TermLambdaCompanion[MethodType] { self =>
31123119

31133120
/** Produce method type from parameter symbols, with special mappings for repeated
31143121
* and inline parameters:
@@ -3144,23 +3151,28 @@ object Types {
31443151
}
31453152
}
31463153

3147-
object MethodType extends MethodTypeCompanion {
3148-
def maker(isJava: Boolean = false, isImplicit: Boolean = false, isErased: Boolean = false): MethodTypeCompanion = {
3154+
object MethodType extends MethodTypeCompanion("MethodType") {
3155+
def maker(isJava: Boolean = false, isImplicit: Boolean = false, isErased: Boolean = false, isContextual: Boolean = false): MethodTypeCompanion = {
31493156
if (isJava) {
31503157
assert(!isImplicit)
31513158
assert(!isErased)
3159+
assert(!isContextual)
31523160
JavaMethodType
31533161
}
3154-
else if (isImplicit && isErased) ErasedImplicitMethodType
3155-
else if (isImplicit) ImplicitMethodType
3156-
else if (isErased) ErasedMethodType
3157-
else MethodType
3162+
else if (isContextual)
3163+
if (isErased) ErasedContextualMethodType else ContextualMethodType
3164+
else if (isImplicit)
3165+
if (isErased) ErasedImplicitMethodType else ImplicitMethodType
3166+
else
3167+
if (isErased) ErasedMethodType else MethodType
31583168
}
31593169
}
3160-
object JavaMethodType extends MethodTypeCompanion
3161-
object ImplicitMethodType extends MethodTypeCompanion
3162-
object ErasedMethodType extends MethodTypeCompanion
3163-
object ErasedImplicitMethodType extends MethodTypeCompanion
3170+
object JavaMethodType extends MethodTypeCompanion("JavaMethodType")
3171+
object ErasedMethodType extends MethodTypeCompanion("ErasedMethodType")
3172+
object ContextualMethodType extends MethodTypeCompanion("ContextualMethodType")
3173+
object ErasedContextualMethodType extends MethodTypeCompanion("ErasedContextualMethodType")
3174+
object ImplicitMethodType extends MethodTypeCompanion("ImplicitMethodType")
3175+
object ErasedImplicitMethodType extends MethodTypeCompanion("ErasedImplicitMethodType")
31643176

31653177
/** A ternary extractor for MethodType */
31663178
object MethodTpe {

compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ Standard-Section: "ASTs" TopLevelStat*
203203
DEFAULTparameterized // Method with default parameters
204204
STABLE // Method that is assumed to be stable
205205
EXTENSION // An extension method
206+
CONTEXTUAL // new style implicit parameters, introduced with `with`
206207
PARAMsetter // A setter without a body named `x_=` where `x` is pickled as a PARAM
207208
Annotation
208209
@@ -311,7 +312,8 @@ object TastyFormat {
311312
final val ERASED = 34
312313
final val OPAQUE = 35
313314
final val EXTENSION = 36
314-
final val PARAMsetter = 37
315+
final val CONTEXTUAL = 37
316+
final val PARAMsetter = 38
315317

316318
// Cat. 2: tag Nat
317319

@@ -416,21 +418,23 @@ object TastyFormat {
416418
final val TYPEREFin = 175
417419
final val OBJECTDEF = 176
418420

419-
// In binary: 101101EI
420-
// I = implicit method type
421-
// E = erased method type
422421
final val METHODtype = 180
423-
final val IMPLICITMETHODtype = 181
424-
final val ERASEDMETHODtype = 182
425-
final val ERASEDIMPLICITMETHODtype = 183
422+
final val ERASEDMETHODtype = 181
423+
final val CONTEXTUALMETHODtype = 182
424+
final val ERASEDCONTEXTUALMETHODtype = 183
425+
final val IMPLICITMETHODtype = 184
426+
final val ERASEDIMPLICITMETHODtype = 185
426427

427428
final val MATCHtype = 190
428429
final val MATCHtpt = 191
429430

430-
def methodType(isImplicit: Boolean = false, isErased: Boolean = false): Int = {
431-
val implicitOffset = if (isImplicit) 1 else 0
432-
val erasedOffset = if (isErased) 2 else 0
433-
METHODtype + implicitOffset + erasedOffset
431+
def methodType(isContextual: Boolean, isImplicit: Boolean, isErased: Boolean): Int = {
432+
val implicitOffset =
433+
if (isContextual) 2
434+
else if (isImplicit) 4
435+
else 0
436+
val erasedOffset = if (isErased) 1 else 0
437+
METHODtype + erasedOffset + implicitOffset
434438
}
435439

436440
final val HOLE = 255
@@ -482,6 +486,7 @@ object TastyFormat {
482486
| DEFAULTparameterized
483487
| STABLE
484488
| EXTENSION
489+
| CONTEXTUAL
485490
| PARAMsetter
486491
| ANNOTATION
487492
| PRIVATEqualified
@@ -541,6 +546,7 @@ object TastyFormat {
541546
case DEFAULTparameterized => "DEFAULTparameterized"
542547
case STABLE => "STABLE"
543548
case EXTENSION => "EXTENSION"
549+
case CONTEXTUAL => "CONTEXTUAL"
544550
case PARAMsetter => "PARAMsetter"
545551

546552
case SHAREDterm => "SHAREDterm"
@@ -632,8 +638,10 @@ object TastyFormat {
632638
case BYNAMEtpt => "BYNAMEtpt"
633639
case POLYtype => "POLYtype"
634640
case METHODtype => "METHODtype"
635-
case IMPLICITMETHODtype => "IMPLICITMETHODtype"
636641
case ERASEDMETHODtype => "ERASEDMETHODtype"
642+
case CONTEXTUALMETHODtype => "CONTEXTUALMETHODtype"
643+
case ERASEDCONTEXTUALMETHODtype => "ERASEDCONTEXTUALMETHODtype"
644+
case IMPLICITMETHODtype => "IMPLICITMETHODtype"
637645
case ERASEDIMPLICITMETHODtype => "ERASEDIMPLICITMETHODtype"
638646
case TYPELAMBDAtype => "TYPELAMBDAtype"
639647
case LAMBDAtpt => "LAMBDAtpt"
@@ -653,7 +661,10 @@ object TastyFormat {
653661
case VALDEF | DEFDEF | TYPEDEF | OBJECTDEF | TYPEPARAM | PARAM | NAMEDARG | RETURN | BIND |
654662
SELFDEF | REFINEDtype | TERMREFin | TYPEREFin | HOLE => 1
655663
case RENAMED | PARAMtype => 2
656-
case POLYtype | METHODtype | TYPELAMBDAtype => -1
664+
case POLYtype | TYPELAMBDAtype |
665+
METHODtype | ERASEDMETHODtype |
666+
CONTEXTUALMETHODtype | ERASEDCONTEXTUALMETHODtype |
667+
IMPLICITMETHODtype | ERASEDIMPLICITMETHODtype => -1
657668
case _ => 0
658669
}
659670
}

compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,10 @@ class TastyPrinter(bytes: Array[Byte])(implicit ctx: Context) {
6767
printName(); printTree(); printTrees()
6868
case RETURN | HOLE =>
6969
printNat(); printTrees()
70-
case METHODtype | IMPLICITMETHODtype | ERASEDMETHODtype | ERASEDIMPLICITMETHODtype | POLYtype | TYPELAMBDAtype =>
70+
case METHODtype | ERASEDMETHODtype |
71+
CONTEXTUALMETHODtype | ERASEDCONTEXTUALMETHODtype |
72+
IMPLICITMETHODtype | ERASEDIMPLICITMETHODtype |
73+
POLYtype | TYPELAMBDAtype =>
7174
printTree()
7275
until(end) { printName(); printTree() }
7376
case PARAMtype =>

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ class TreePickler(pickler: TastyPickler) {
255255
case tpe: PolyType if richTypes =>
256256
pickleMethodic(POLYtype, tpe)
257257
case tpe: MethodType if richTypes =>
258-
pickleMethodic(methodType(isImplicit = tpe.isImplicitMethod, isErased = tpe.isErasedMethod), tpe)
258+
pickleMethodic(methodType(tpe.isContextualMethod, tpe.isImplicitMethod, tpe.isErasedMethod), tpe)
259259
case tpe: ParamRef =>
260260
assert(pickleParamRef(tpe), s"orphan parameter reference: $tpe")
261261
case tpe: LazyRef =>
@@ -639,6 +639,7 @@ class TreePickler(pickler: TastyPickler) {
639639
if (flags is DefaultParameterized) writeByte(DEFAULTparameterized)
640640
if (flags is Stable) writeByte(STABLE)
641641
if (flags is Extension) writeByte(EXTENSION)
642+
if (flags is Contextual) writeByte(CONTEXTUAL)
642643
if (flags is ParamAccessor) writeByte(PARAMsetter)
643644
assert(!(flags is Label))
644645
} else {

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,10 +351,14 @@ class TreeUnpickler(reader: TastyReader,
351351
readMethodic(PolyType, _.toTypeName)
352352
case METHODtype =>
353353
readMethodic(MethodType, _.toTermName)
354-
case IMPLICITMETHODtype =>
355-
readMethodic(ImplicitMethodType, _.toTermName)
356354
case ERASEDMETHODtype =>
357355
readMethodic(ErasedMethodType, _.toTermName)
356+
case CONTEXTUALMETHODtype =>
357+
readMethodic(ContextualMethodType, _.toTermName)
358+
case ERASEDCONTEXTUALMETHODtype =>
359+
readMethodic(ErasedContextualMethodType, _.toTermName)
360+
case IMPLICITMETHODtype =>
361+
readMethodic(ImplicitMethodType, _.toTermName)
358362
case ERASEDIMPLICITMETHODtype =>
359363
readMethodic(ErasedImplicitMethodType, _.toTermName)
360364
case TYPELAMBDAtype =>
@@ -623,6 +627,7 @@ class TreeUnpickler(reader: TastyReader,
623627
case DEFAULTparameterized => addFlag(DefaultParameterized)
624628
case STABLE => addFlag(Stable)
625629
case EXTENSION => addFlag(Extension)
630+
case CONTEXTUAL => addFlag(Contextual)
626631
case PARAMsetter =>
627632
addFlag(ParamAccessor)
628633
case PRIVATEqualified =>

compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,9 @@ class PlainPrinter(_ctx: Context) extends Printer {
181181
"<noprefix>"
182182
case tp: MethodType =>
183183
changePrec(GlobalPrec) {
184+
(if (tp.isContextualMethod) " with " else "") ~
184185
("(" + (if (tp.isErasedMethod) "erased " else "")
185-
+ (if (tp.isImplicitMethod) "implicit " else "")
186+
+ (if (tp.isImplicitMethod && !tp.isContextualMethod) "implicit " else "")
186187
) ~ paramsText(tp) ~
187188
(if (tp.resultType.isInstanceOf[MethodType]) ")" else "): ") ~
188189
toText(tp.resultType)

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
139139
}
140140

141141
def toTextDependentFunction(appType: MethodType): Text = {
142+
// TODO: change to |=>
142143
(keywordText("implicit ") provided appType.isImplicitMethod) ~
143144
"(" ~ paramsText(appType) ~ ") => " ~ toText(appType.resultType)
144145
}

compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsOpsImpl.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ trait TypeOrBoundsOpsImpl extends scala.tasty.reflect.TypeOrBoundsOps with CoreI
1414
def MethodTypeDeco(tpe: MethodType): MethodTypeAPI = new MethodTypeAPI {
1515
def isErased: Boolean = tpe.isErasedMethod
1616
def isImplicit: Boolean = tpe.isImplicitMethod
17+
def isContextual: Boolean = tpe.isContextualMethod
1718
def paramNames(implicit ctx: Context): List[String] = tpe.paramNames.map(_.toString)
1819
def paramTypes(implicit ctx: Context): List[Type] = tpe.paramInfos
1920
def resultTpe(implicit ctx: Context): Type = tpe.resType

compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ object EtaExpansion extends LiftImpure {
206206
if (isLastApplication && mt.paramInfos.length == xarity) mt.paramInfos map (_ => TypeTree())
207207
else mt.paramInfos map TypeTree
208208
var paramFlag = Synthetic | Param
209+
if (mt.isContextualMethod) paramFlag |= Contextual
209210
if (mt.isImplicitMethod) paramFlag |= Implicit
210211
val params = (mt.paramNames, paramTypes).zipped.map((name, tpe) =>
211212
ValDef(name, tpe, EmptyTree).withFlags(paramFlag).withPos(tree.pos.startPos))
@@ -215,7 +216,8 @@ object EtaExpansion extends LiftImpure {
215216
var body: Tree = Apply(lifted, ids)
216217
if (!isLastApplication) body = PostfixOp(body, Ident(nme.WILDCARD))
217218
val fn =
218-
if (mt.isImplicitMethod) new untpd.FunctionWithMods(params, body, Modifiers(Implicit))
219+
if (mt.isContextualMethod) new untpd.FunctionWithMods(params, body, Modifiers(Implicit | Contextual))
220+
else if (mt.isImplicitMethod) new untpd.FunctionWithMods(params, body, Modifiers(Implicit))
219221
else untpd.Function(params, body)
220222
if (defs.nonEmpty) untpd.Block(defs.toList map (untpd.TypedSplice(_)), fn) else fn
221223
}

compiler/src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,10 @@ trait NamerContextOps { this: Context =>
132132
def methodType(typeParams: List[Symbol], valueParamss: List[List[Symbol]], resultType: Type, isJava: Boolean = false)(implicit ctx: Context): Type = {
133133
val monotpe =
134134
(valueParamss :\ resultType) { (params, resultType) =>
135-
val (isImplicit, isErased) =
136-
if (params.isEmpty) (false, false)
137-
else (params.head is Implicit, params.head is Erased)
138-
val make = MethodType.maker(isJava, isImplicit, isErased)
135+
val (isImplicit, isErased, isContextual) =
136+
if (params.isEmpty) (false, false, false)
137+
else (params.head is Implicit, params.head is Erased, params.head.is(Contextual))
138+
val make = MethodType.maker(isJava, isImplicit, isErased, isContextual)
139139
if (isJava)
140140
for (param <- params)
141141
if (param.info.isDirectRef(defn.ObjectClass)) param.info = defn.AnyType

compiler/test/dotc/pos-test-pickling.blacklist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ phantom-Eq2
1818
power-macro
1919
quote-lift-inline-params
2020
quote-nested-object
21+
reference
2122
scala2traits
2223
sepComp
2324
seqtype-cycle

docs/docs/reference/match-types.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ Using `can-reduce`, we can now define match type reduction proper in the `reduce
8686
```
8787
if
8888
```
89-
Match(S, C1, ..., Cn) can-reduce i, T
89+
Match(S, C1, ..., Cn) can-reduce i, T
9090
```
9191
and, for `j` in `1..i-1`: `C_j` is disjoint from `C_i`, or else `S` cannot possibly match `C_j`.
9292
See the section on overlapping patterns for an elaboration of "disjoint" and "cannot possibly match".

0 commit comments

Comments
 (0)