Skip to content

Commit 4ca8f4e

Browse files
committed
Type checking of contextual applications
1 parent 23a6d60 commit 4ca8f4e

File tree

11 files changed

+41
-30
lines changed

11 files changed

+41
-30
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,8 @@ object Trees {
444444
case class Apply[-T >: Untyped] private[ast] (fun: Tree[T], args: List[Tree[T]])
445445
extends GenericApply[T] {
446446
type ThisTree[-T >: Untyped] = Apply[T]
447+
448+
def isContextual = getAttachment(untpd.WithApply).nonEmpty
447449
}
448450

449451
/** fun[args] */

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
227227

228228
def valueParam(name: TermName, origInfo: Type): TermSymbol = {
229229
val maybeImplicit =
230-
if (tp.isContextualMethod) Implicit | Contextual
230+
if (tp.isContextual) Implicit | Contextual
231231
else if (tp.isImplicitMethod) Implicit
232232
else EmptyFlags
233233
val maybeErased = if (tp.isErasedMethod) Erased else EmptyFlags
@@ -1030,9 +1030,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
10301030
}
10311031
}
10321032

1033-
def applyOverloaded(receiver: Tree, method: TermName, args: List[Tree], targs: List[Type], expectedType: Type)(implicit ctx: Context): Tree = {
1033+
def applyOverloaded(receiver: Tree, method: TermName, args: List[Tree], targs: List[Type],
1034+
expectedType: Type, isContextual: Boolean = false)(implicit ctx: Context): Tree = {
10341035
val typer = ctx.typer
1035-
val proto = new FunProtoTyped(args, expectedType)(typer)
1036+
val proto = new FunProtoTyped(args, expectedType)(typer, isContextual)
10361037
val denot = receiver.tpe.member(method)
10371038
assert(denot.exists, i"no member $receiver . $method, members = ${receiver.tpe.decls}")
10381039
val selected =

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -334,8 +334,8 @@ object Types {
334334
/** Is this a MethodType which has implicit or contextual parameters */
335335
def isImplicitMethod: Boolean = false
336336

337-
/** Is this a MethodType which has contextual parameters */
338-
def isContextualMethod: Boolean = false
337+
/** Is this a Method or PolyType which has contextual parameters as first value parameter list? */
338+
def isContextual: Boolean = false
339339

340340
/** Is this a MethodType for which the parameters will not be used */
341341
def isErasedMethod: Boolean = false
@@ -3056,10 +3056,10 @@ object Types {
30563056

30573057
final override def isJavaMethod: Boolean = companion eq JavaMethodType
30583058
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)
3059+
companion.eq(ImplicitMethodType) || companion.eq(ErasedImplicitMethodType) || isContextual
30623060
final override def isErasedMethod: Boolean = companion.eq(ErasedMethodType) || companion.eq(ErasedImplicitMethodType)
3061+
final override def isContextual: Boolean =
3062+
companion.eq(ContextualMethodType) || companion.eq(ErasedContextualMethodType)
30633063

30643064
def computeSignature(implicit ctx: Context): Signature = {
30653065
val params = if (isErasedMethod) Nil else paramInfos
@@ -3251,6 +3251,8 @@ object Types {
32513251

32523252
def computeSignature(implicit ctx: Context): Signature = resultSignature
32533253

3254+
override def isContextual = resType.isContextual
3255+
32543256
/** Merge nested polytypes into one polytype. nested polytypes are normally not supported
32553257
* but can arise as temporary data structures.
32563258
*/

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

Lines changed: 1 addition & 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(tpe.isContextualMethod, tpe.isImplicitMethod, tpe.isErasedMethod), tpe)
258+
pickleMethodic(methodType(tpe.isContextual, tpe.isImplicitMethod, tpe.isErasedMethod), tpe)
259259
case tpe: ParamRef =>
260260
assert(pickleParamRef(tpe), s"orphan parameter reference: $tpe")
261261
case tpe: LazyRef =>

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

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

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +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
17+
def isContextual: Boolean = tpe.isContextual
1818
def paramNames(implicit ctx: Context): List[String] = tpe.paramNames.map(_.toString)
1919
def paramTypes(implicit ctx: Context): List[Type] = tpe.paramInfos
2020
def resultTpe(implicit ctx: Context): Type = tpe.resType

compiler/src/dotty/tools/dotc/transform/Erasure.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,7 @@ object Erasure {
502502
val Apply(fun, args) = tree
503503
if (fun.symbol == defn.cbnArg)
504504
typedUnadapted(args.head, pt)
505-
else typedExpr(fun, FunProto(args, pt)(this)) match {
505+
else typedExpr(fun, FunProto(args, pt)(this, isContextual = false)) match {
506506
case fun1: Apply => // arguments passed in prototype were already passed
507507
fun1
508508
case fun1 =>

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -743,10 +743,9 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
743743
* Block node.
744744
*/
745745
def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = {
746-
val isContextual = tree.getAttachment(untpd.WithApply).nonEmpty
747746

748747
def realApply(implicit ctx: Context): Tree = track("realApply") {
749-
val originalProto = new FunProto(tree.args, IgnoredProto(pt))(this)(argCtx(tree))
748+
val originalProto = new FunProto(tree.args, IgnoredProto(pt))(this, tree.isContextual)(argCtx(tree))
750749
val fun1 = typedExpr(tree.fun, originalProto)
751750

752751
// Warning: The following lines are dirty and fragile. We record that auto-tupling was demanded as

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +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
209+
if (mt.isContextual) paramFlag |= Contextual
210210
if (mt.isImplicitMethod) paramFlag |= Implicit
211211
val params = (mt.paramNames, paramTypes).zipped.map((name, tpe) =>
212212
ValDef(name, tpe, EmptyTree).withFlags(paramFlag).withPos(tree.pos.startPos))
@@ -216,7 +216,7 @@ object EtaExpansion extends LiftImpure {
216216
var body: Tree = Apply(lifted, ids)
217217
if (!isLastApplication) body = PostfixOp(body, Ident(nme.WILDCARD))
218218
val fn =
219-
if (mt.isContextualMethod) new untpd.FunctionWithMods(params, body, Modifiers(Implicit | Contextual))
219+
if (mt.isContextual) new untpd.FunctionWithMods(params, body, Modifiers(Implicit | Contextual))
220220
else if (mt.isImplicitMethod) new untpd.FunctionWithMods(params, body, Modifiers(Implicit))
221221
else untpd.Function(params, body)
222222
if (defs.nonEmpty) untpd.Block(defs.toList map (untpd.TypedSplice(_)), fn) else fn

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,8 @@ object ProtoTypes {
224224
*
225225
* [](args): resultType
226226
*/
227-
case class FunProto(args: List[untpd.Tree], resType: Type)(typer: Typer, state: FunProtoState = new FunProtoState)(implicit ctx: Context)
227+
case class FunProto(args: List[untpd.Tree], resType: Type)(typer: Typer,
228+
override val isContextual: Boolean, state: FunProtoState = new FunProtoState)(implicit ctx: Context)
228229
extends UncachedGroundType with ApplyingProto with FunOrPolyProto {
229230
override def resultType(implicit ctx: Context): Type = resType
230231

@@ -233,7 +234,7 @@ object ProtoTypes {
233234

234235
def derivedFunProto(args: List[untpd.Tree] = this.args, resultType: Type, typer: Typer = this.typer): FunProto =
235236
if ((args eq this.args) && (resultType eq this.resultType) && (typer eq this.typer)) this
236-
else new FunProto(args, resultType)(typer)
237+
else new FunProto(args, resultType)(typer, isContextual)
237238

238239
override def notApplied: Type = WildcardType
239240

@@ -317,7 +318,7 @@ object ProtoTypes {
317318
case pt: FunProto =>
318319
pt
319320
case _ =>
320-
state.tupled = new FunProto(untpd.Tuple(args) :: Nil, resultType)(typer)
321+
state.tupled = new FunProto(untpd.Tuple(args) :: Nil, resultType)(typer, isContextual)
321322
tupled
322323
}
323324

@@ -352,14 +353,14 @@ object ProtoTypes {
352353

353354
override def withContext(newCtx: Context): ProtoType =
354355
if (newCtx `eq` ctx) this
355-
else new FunProto(args, resType)(typer, state)(newCtx)
356+
else new FunProto(args, resType)(typer, isContextual, state)(newCtx)
356357
}
357358

358359
/** A prototype for expressions that appear in function position
359360
*
360361
* [](args): resultType, where args are known to be typed
361362
*/
362-
class FunProtoTyped(args: List[tpd.Tree], resultType: Type)(typer: Typer)(implicit ctx: Context) extends FunProto(args, resultType)(typer)(ctx) {
363+
class FunProtoTyped(args: List[tpd.Tree], resultType: Type)(typer: Typer, isContextual: Boolean)(implicit ctx: Context) extends FunProto(args, resultType)(typer, isContextual)(ctx) {
363364
override def typedArgs: List[tpd.Tree] = args
364365
override def withContext(ctx: Context): FunProtoTyped = this
365366
}
@@ -406,7 +407,7 @@ object ProtoTypes {
406407
}
407408

408409
class UnapplyFunProto(argType: Type, typer: Typer)(implicit ctx: Context) extends FunProto(
409-
untpd.TypedSplice(dummyTreeOfType(argType))(ctx) :: Nil, WildcardType)(typer)
410+
untpd.TypedSplice(dummyTreeOfType(argType))(ctx) :: Nil, WildcardType)(typer, isContextual = false)
410411

411412
/** A prototype for expressions [] that are type-parameterized:
412413
*

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

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -857,7 +857,7 @@ class Typer extends Namer
857857
* every parameter in `params`.
858858
*/
859859
def calleeType: Type = fnBody match {
860-
case Apply(expr, args) =>
860+
case app @ Apply(expr, args) =>
861861
paramIndex = {
862862
for (param <- params; idx <- paramIndices(param, args))
863863
yield param.name -> idx
@@ -868,7 +868,7 @@ class Typer extends Namer
868868
expr1.tpe
869869
case _ =>
870870
val protoArgs = args map (_ withType WildcardType)
871-
val callProto = FunProto(protoArgs, WildcardType)(this)
871+
val callProto = FunProto(protoArgs, WildcardType)(this, isContextual = app.isContextual)
872872
val expr1 = typedExpr(expr, callProto)
873873
fnBody = cpy.Apply(fnBody)(untpd.TypedSplice(expr1), args)
874874
expr1.tpe
@@ -2239,7 +2239,8 @@ class Typer extends Namer
22392239
errorTree(tree, NoMatchingOverload(altDenots, pt)(err))
22402240
def hasEmptyParams(denot: SingleDenotation) = denot.info.paramInfoss == ListOfNil
22412241
pt match {
2242-
case pt: FunProto =>
2242+
case pt: FunProto if !pt.isContextual =>
2243+
// insert apply or convert qualifier only for a regular application
22432244
tryInsertApplyOrImplicit(tree, pt, locked)(noMatches)
22442245
case _ =>
22452246
if (altDenots exists (_.info.paramInfoss == ListOfNil))
@@ -2269,11 +2270,16 @@ class Typer extends Namer
22692270
}
22702271

22712272
def adaptToArgs(wtp: Type, pt: FunProto): Tree = wtp match {
2272-
case _: MethodOrPoly =>
2273-
if (pt.args.lengthCompare(1) > 0 && isUnary(wtp) && ctx.canAutoTuple)
2274-
adapt(tree, pt.tupled, locked)
2273+
case wtp: MethodOrPoly =>
2274+
if (wtp.isContextual == pt.isContextual)
2275+
if (pt.args.lengthCompare(1) > 0 && isUnary(wtp) && ctx.canAutoTuple)
2276+
adapt(tree, pt.tupled, locked)
2277+
else
2278+
tree
2279+
else if (wtp.isContextual)
2280+
adaptNoArgs(wtp) // insert arguments implicitly
22752281
else
2276-
tree
2282+
errorTree(tree, em"Missing arguments for ${methPart(tree).symbol.showLocated}")
22772283
case _ => tryInsertApplyOrImplicit(tree, pt, locked) {
22782284
errorTree(tree, MethodDoesNotTakeParameters(tree))
22792285
}

0 commit comments

Comments
 (0)