Skip to content

Commit 3d3d147

Browse files
Merge pull request #8406 from dotty-staging/fix-#7793
Fix #7793: Handle context parameters in resolveOverloaded
2 parents 8043497 + d7a3629 commit 3d3d147

File tree

11 files changed

+72
-53
lines changed

11 files changed

+72
-53
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,7 @@ object desugar {
574574
val nu = vparamss.foldLeft(makeNew(classTypeRef)) { (nu, vparams) =>
575575
val app = Apply(nu, vparams.map(refOfDef))
576576
vparams match {
577-
case vparam :: _ if vparam.mods.is(Given) => app.setGivenApply()
577+
case vparam :: _ if vparam.mods.is(Given) => app.setUsingApply()
578578
case _ => app
579579
}
580580
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -439,8 +439,8 @@ object Trees {
439439
extends GenericApply[T] {
440440
type ThisTree[-T >: Untyped] = Apply[T]
441441

442-
def isGivenApply = hasAttachment(untpd.ApplyGiven)
443-
def setGivenApply() = { putAttachment(untpd.ApplyGiven, ()); this }
442+
def isUsingApply = hasAttachment(untpd.ApplyGiven)
443+
def setUsingApply() = { putAttachment(untpd.ApplyGiven, ()); this }
444444
}
445445

446446
/** fun[args] */

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3305,7 +3305,6 @@ object Types {
33053305
companion.eq(ContextualMethodType) ||
33063306
companion.eq(ErasedContextualMethodType)
33073307

3308-
33093308
def computeSignature(implicit ctx: Context): Signature = {
33103309
val params = if (isErasedMethod) Nil else paramInfos
33113310
resultSignature.prependTermParams(params, isJavaMethod)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2293,7 +2293,7 @@ object Parsers {
22932293

22942294
def mkApply(fn: Tree, args: (List[Tree], Boolean)): Tree =
22952295
val res = Apply(fn, args._1)
2296-
if args._2 then res.setGivenApply()
2296+
if args._2 then res.setUsingApply()
22972297
res
22982298

22992299
val argumentExpr: () => Tree = () => exprInParens() match {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
390390
else
391391
toTextLocal(fun)
392392
~ "("
393-
~ Str("using ").provided(app.isGivenApply && !homogenizedView)
393+
~ Str("using ").provided(app.isUsingApply && !homogenizedView)
394394
~ toTextGlobal(args, ", ")
395395
~ ")"
396396
case tree: TypeApply =>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,7 @@ object Erasure {
545545
val Apply(fun, args) = tree
546546
if (fun.symbol == defn.cbnArg)
547547
typedUnadapted(args.head, pt)
548-
else typedExpr(fun, FunProto(args, pt)(this, isGivenApply = false)) match {
548+
else typedExpr(fun, FunProto(args, pt)(this, isUsingApply = false)) match {
549549
case fun1: Apply => // arguments passed in prototype were already passed
550550
fun1
551551
case fun1 =>

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

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -833,7 +833,7 @@ trait Applications extends Compatibility {
833833
def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = {
834834

835835
def realApply(implicit ctx: Context): Tree = {
836-
val originalProto = new FunProto(tree.args, IgnoredProto(pt))(this, tree.isGivenApply)(argCtx(tree))
836+
val originalProto = new FunProto(tree.args, IgnoredProto(pt))(this, tree.isUsingApply)(argCtx(tree))
837837
record("typedApply")
838838
val fun1 = typedFunPart(tree.fun, originalProto)
839839

@@ -1299,6 +1299,16 @@ trait Applications extends Compatibility {
12991299
}
13001300
}
13011301

1302+
/** Drop any implicit parameter section */
1303+
def stripImplicit(tp: Type)(using Context): Type = tp match {
1304+
case mt: MethodType if mt.isImplicitMethod =>
1305+
stripImplicit(resultTypeApprox(mt))
1306+
case pt: PolyType =>
1307+
pt.derivedLambdaType(pt.paramNames, pt.paramInfos, stripImplicit(pt.resultType))
1308+
case _ =>
1309+
tp
1310+
}
1311+
13021312
/** Compare owner inheritance level.
13031313
* @param sym1 The first owner
13041314
* @param sym2 The second owner
@@ -1466,16 +1476,6 @@ trait Applications extends Compatibility {
14661476
else tp
14671477
}
14681478

1469-
/** Drop any implicit parameter section */
1470-
def stripImplicit(tp: Type): Type = tp match {
1471-
case mt: MethodType if mt.isImplicitMethod =>
1472-
stripImplicit(resultTypeApprox(mt))
1473-
case pt: PolyType =>
1474-
pt.derivedLambdaType(pt.paramNames, pt.paramInfos, stripImplicit(pt.resultType))
1475-
case _ =>
1476-
tp
1477-
}
1478-
14791479
def compareWithTypes(tp1: Type, tp2: Type) = {
14801480
val ownerScore = compareOwner(alt1.symbol.maybeOwner, alt2.symbol.maybeOwner)
14811481
def winsType1 = isAsSpecific(alt1, tp1, alt2, tp2)
@@ -1707,6 +1707,22 @@ trait Applications extends Compatibility {
17071707
case _ => arg
17081708
end normArg
17091709

1710+
/** Resolve overloading by mapping to a different problem where each alternative's
1711+
* type is mapped with `f`, alternatives with non-existing types are dropped, and the
1712+
* expected type is `pt`. Map the results back to the original alternatives.
1713+
*/
1714+
def resolveMapped(alts: List[TermRef], f: TermRef => Type, pt: Type): List[TermRef] =
1715+
val reverseMapping = alts.flatMap { alt =>
1716+
val t = f(alt)
1717+
if t.exists then
1718+
Some((TermRef(NoPrefix, alt.symbol.asTerm.copy(info = t)), alt))
1719+
else
1720+
None
1721+
}
1722+
val mapped = reverseMapping.map(_._1)
1723+
overload.println(i"resolve mapped: $mapped")
1724+
resolveOverloaded(mapped, pt, targs).map(reverseMapping.toMap)
1725+
17101726
val candidates = pt match {
17111727
case pt @ FunProto(args, resultType) =>
17121728
val numArgs = args.length
@@ -1747,6 +1763,15 @@ trait Applications extends Compatibility {
17471763
alts2
17481764
}
17491765

1766+
if pt.isUsingApply then
1767+
val alts0 = alts.filterConserve { alt =>
1768+
val mt = alt.widen.stripPoly
1769+
mt.isImplicitMethod || mt.isContextualMethod
1770+
}
1771+
if alts0 ne alts then return resolveOverloaded(alts0, pt, targs)
1772+
else if alts.exists(_.widen.stripPoly.isContextualMethod) then
1773+
return resolveMapped(alts, alt => stripImplicit(alt.widen), pt)
1774+
17501775
val alts1 = narrowBySize(alts)
17511776
//ctx.log(i"narrowed by size: ${alts1.map(_.symbol.showDcl)}%, %")
17521777
if isDetermined(alts1) then alts1
@@ -1783,32 +1808,20 @@ trait Applications extends Compatibility {
17831808
else compat
17841809
}
17851810

1786-
/** For each candidate `C`, a proxy termref paired with `C`.
1787-
* The proxy termref has as symbol a copy of the original candidate symbol,
1788-
* with an info that strips the first value parameter list away.
1789-
* @param argTypes The types of the arguments of the FunProto `pt`.
1811+
/** The type of alternative `alt` after instantiating its first parameter
1812+
* clause with `argTypes`.
17901813
*/
1791-
def advanceCandidates(argTypes: List[Type]): List[(TermRef, TermRef)] = {
1792-
def strippedType(tp: Type): Type = tp match {
1814+
def skipParamClause(argTypes: List[Type])(alt: TermRef): Type =
1815+
def skip(tp: Type): Type = tp match {
17931816
case tp: PolyType =>
1794-
val rt = strippedType(tp.resultType)
1817+
val rt = skip(tp.resultType)
17951818
if (rt.exists) tp.derivedLambdaType(resType = rt) else rt
17961819
case tp: MethodType =>
17971820
tp.instantiate(argTypes)
17981821
case _ =>
17991822
NoType
18001823
}
1801-
def cloneCandidate(cand: TermRef): List[(TermRef, TermRef)] = {
1802-
val strippedInfo = strippedType(cand.widen)
1803-
if (strippedInfo.exists) {
1804-
val sym = cand.symbol.asTerm.copy(info = strippedInfo)
1805-
(TermRef(NoPrefix, sym), cand) :: Nil
1806-
}
1807-
else Nil
1808-
}
1809-
overload.println(i"look at more params: ${candidates.head.symbol}: ${candidates.map(_.widen)}%, % with $pt, [$targs%, %]")
1810-
candidates.flatMap(cloneCandidate)
1811-
}
1824+
skip(alt.widen)
18121825

18131826
def resultIsMethod(tp: Type): Boolean = tp.widen.stripPoly match
18141827
case tp: MethodType => tp.resultType.isInstanceOf[MethodType]
@@ -1821,9 +1834,7 @@ trait Applications extends Compatibility {
18211834
deepPt match
18221835
case pt @ FunProto(_, resType: FunProto) =>
18231836
// try to narrow further with snd argument list
1824-
val advanced = advanceCandidates(pt.typedArgs().tpes)
1825-
resolveOverloaded(advanced.map(_._1), resType, Nil) // resolve with candidates where first params are stripped
1826-
.map(advanced.toMap) // map surviving result(s) back to original candidates
1837+
resolveMapped(candidates, skipParamClause(pt.typedArgs().tpes), resType)
18271838
case _ =>
18281839
// prefer alternatives that need no eta expansion
18291840
val noCurried = alts.filter(!resultIsMethod(_))

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ object EtaExpansion extends LiftImpure {
233233
if (mt.paramInfos.nonEmpty && mt.paramInfos.last.isRepeatedParam)
234234
ids = ids.init :+ repeated(ids.last)
235235
val app = Apply(lifted, ids)
236-
if (mt.isContextualMethod) app.setGivenApply()
236+
if (mt.isContextualMethod) app.setUsingApply()
237237
val body = if (isLastApplication) app else PostfixOp(app, Ident(nme.WILDCARD))
238238
val fn =
239239
if (mt.isContextualMethod) new untpd.FunctionWithMods(params, body, Modifiers(Given))

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ object ProtoTypes {
228228

229229
trait ApplyingProto extends ProtoType // common trait of ViewProto and FunProto
230230
trait FunOrPolyProto extends ProtoType { // common trait of PolyProto and FunProto
231-
def isGivenApply: Boolean = false
231+
def isUsingApply: Boolean = false
232232
}
233233

234234
class FunProtoState {
@@ -251,7 +251,7 @@ object ProtoTypes {
251251
* [](args): resultType
252252
*/
253253
case class FunProto(args: List[untpd.Tree], resType: Type)(typer: Typer,
254-
override val isGivenApply: Boolean, state: FunProtoState = new FunProtoState)(implicit val ctx: Context)
254+
override val isUsingApply: Boolean, state: FunProtoState = new FunProtoState)(implicit val ctx: Context)
255255
extends UncachedGroundType with ApplyingProto with FunOrPolyProto {
256256
override def resultType(implicit ctx: Context): Type = resType
257257

@@ -265,7 +265,7 @@ object ProtoTypes {
265265

266266
def derivedFunProto(args: List[untpd.Tree] = this.args, resultType: Type, typer: Typer = this.typer): FunProto =
267267
if ((args eq this.args) && (resultType eq this.resultType) && (typer eq this.typer)) this
268-
else new FunProto(args, resultType)(typer, isGivenApply)
268+
else new FunProto(args, resultType)(typer, isUsingApply)
269269

270270
/** @return True if all arguments have types.
271271
*/
@@ -355,7 +355,7 @@ object ProtoTypes {
355355
case pt: FunProto =>
356356
pt
357357
case _ =>
358-
state.tupled = new FunProto(untpd.Tuple(args) :: Nil, resultType)(typer, isGivenApply)
358+
state.tupled = new FunProto(untpd.Tuple(args) :: Nil, resultType)(typer, isUsingApply)
359359
tupled
360360
}
361361

@@ -390,14 +390,14 @@ object ProtoTypes {
390390

391391
override def withContext(newCtx: Context): ProtoType =
392392
if (newCtx `eq` ctx) this
393-
else new FunProto(args, resType)(typer, isGivenApply, state)(newCtx)
393+
else new FunProto(args, resType)(typer, isUsingApply, state)(newCtx)
394394
}
395395

396396
/** A prototype for expressions that appear in function position
397397
*
398398
* [](args): resultType, where args are known to be typed
399399
*/
400-
class FunProtoTyped(args: List[tpd.Tree], resultType: Type)(typer: Typer, isGivenApply: Boolean)(implicit ctx: Context) extends FunProto(args, resultType)(typer, isGivenApply)(ctx) {
400+
class FunProtoTyped(args: List[tpd.Tree], resultType: Type)(typer: Typer, isUsingApply: Boolean)(implicit ctx: Context) extends FunProto(args, resultType)(typer, isUsingApply)(ctx) {
401401
override def typedArgs(norm: (untpd.Tree, Int) => untpd.Tree)(implicit ctx: Context): List[tpd.Tree] = args
402402
override def withContext(ctx: Context): FunProtoTyped = this
403403
}
@@ -444,7 +444,7 @@ object ProtoTypes {
444444
}
445445

446446
class UnapplyFunProto(argType: Type, typer: Typer)(implicit ctx: Context) extends FunProto(
447-
untpd.TypedSplice(dummyTreeOfType(argType)(ctx.source))(ctx) :: Nil, WildcardType)(typer, isGivenApply = false)
447+
untpd.TypedSplice(dummyTreeOfType(argType)(ctx.source))(ctx) :: Nil, WildcardType)(typer, isUsingApply = false)
448448

449449
/** A prototype for expressions [] that are type-parameterized:
450450
*

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,7 +1015,7 @@ class Typer extends Namer
10151015
case _ =>
10161016
given nestedCtx as Context = ctx.fresh.setNewTyperState()
10171017
val protoArgs = args map (_ withType WildcardType)
1018-
val callProto = FunProto(protoArgs, WildcardType)(this, app.isGivenApply)
1018+
val callProto = FunProto(protoArgs, WildcardType)(this, app.isUsingApply)
10191019
val expr1 = typedExpr(expr, callProto)
10201020
if nestedCtx.reporter.hasErrors then NoType
10211021
else
@@ -2640,7 +2640,7 @@ class Typer extends Namer
26402640
errorTree(tree, NoMatchingOverload(altDenots, pt)(err))
26412641
def hasEmptyParams(denot: SingleDenotation) = denot.info.paramInfoss == ListOfNil
26422642
pt match {
2643-
case pt: FunOrPolyProto if !pt.isGivenApply =>
2643+
case pt: FunOrPolyProto if !pt.isUsingApply =>
26442644
// insert apply or convert qualifier, but only for a regular application
26452645
tryInsertApplyOrImplicit(tree, pt, locked)(noMatches)
26462646
case _ =>
@@ -2791,7 +2791,7 @@ class Typer extends Namer
27912791
}
27922792
tryEither {
27932793
val app = cpy.Apply(tree)(untpd.TypedSplice(tree), namedArgs)
2794-
if (wtp.isContextualMethod) app.setGivenApply()
2794+
if (wtp.isContextualMethod) app.setUsingApply()
27952795
typr.println(i"try with default implicit args $app")
27962796
typed(app, pt, locked)
27972797
} { (_, _) =>
@@ -2808,7 +2808,7 @@ class Typer extends Namer
28082808
}
28092809
}
28102810
pt.revealIgnored match {
2811-
case pt: FunProto if pt.isGivenApply =>
2811+
case pt: FunProto if pt.isUsingApply =>
28122812
// We can end up here if extension methods are called with explicit given arguments.
28132813
// See for instance #7119.
28142814
tree
@@ -3230,8 +3230,8 @@ class Typer extends Namer
32303230
* Overridden in `ReTyper`, where all applications are treated the same
32313231
*/
32323232
protected def matchingApply(methType: MethodOrPoly, pt: FunProto)(implicit ctx: Context): Boolean =
3233-
methType.isContextualMethod == pt.isGivenApply ||
3234-
methType.isImplicitMethod && pt.isGivenApply // for a transition allow `with` arguments for regular implicit parameters
3233+
methType.isContextualMethod == pt.isUsingApply ||
3234+
methType.isImplicitMethod && pt.isUsingApply // for a transition allow `with` arguments for regular implicit parameters
32353235

32363236
/** Check that `tree == x: pt` is typeable. Used when checking a pattern
32373237
* against a selector of type `pt`. This implementation accounts for

tests/pos/i7793.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
trait Foo:
2+
def g(f: Int => Int): Int = 1
3+
def g(using String)(f: Int => String): String = "2"
4+
5+
@main def Test =
6+
val m: Foo = ???
7+
given String = "foo"
8+
m.g(x => "2")
9+
m.g(using summon[String])(x => "2")

0 commit comments

Comments
 (0)