@@ -2062,7 +2062,7 @@ trait Applications extends Compatibility {
2062
2062
* Two trials: First, without implicits or SAM conversions enabled. Then,
2063
2063
* if the first finds no eligible candidates, with implicits and SAM conversions enabled.
2064
2064
*/
2065
- def resolveOverloaded (alts : List [TermRef ], pt : Type )(using Context ): List [TermRef ] =
2065
+ def resolveOverloaded (alts : List [TermRef ], pt : Type , srcPos : SrcPos )(using Context ): List [TermRef ] =
2066
2066
record(" resolveOverloaded" )
2067
2067
2068
2068
/** Is `alt` a method or polytype whose result type after the first value parameter
@@ -2100,7 +2100,7 @@ trait Applications extends Compatibility {
2100
2100
case Nil => chosen
2101
2101
case alt2 :: Nil => alt2
2102
2102
case alts2 =>
2103
- resolveOverloaded(alts2, pt) match {
2103
+ resolveOverloaded(alts2, pt, srcPos ) match {
2104
2104
case alt2 :: Nil => alt2
2105
2105
case _ => chosen
2106
2106
}
@@ -2115,12 +2115,12 @@ trait Applications extends Compatibility {
2115
2115
val alts0 = alts.filterConserve(_.widen.stripPoly.isImplicitMethod)
2116
2116
if alts0 ne alts then return resolve(alts0)
2117
2117
else if alts.exists(_.widen.stripPoly.isContextualMethod) then
2118
- return resolveMapped(alts, alt => stripImplicit(alt.widen), pt)
2118
+ return resolveMapped(alts, alt => stripImplicit(alt.widen), pt, srcPos )
2119
2119
case _ =>
2120
2120
2121
- var found = withoutMode(Mode .ImplicitsEnabled )(resolveOverloaded1(alts, pt))
2121
+ var found = withoutMode(Mode .ImplicitsEnabled )(resolveOverloaded1(alts, pt, srcPos ))
2122
2122
if found.isEmpty && ctx.mode.is(Mode .ImplicitsEnabled ) then
2123
- found = resolveOverloaded1(alts, pt)
2123
+ found = resolveOverloaded1(alts, pt, srcPos )
2124
2124
found match
2125
2125
case alt :: Nil => adaptByResult(alt, alts) :: Nil
2126
2126
case _ => found
@@ -2167,10 +2167,44 @@ trait Applications extends Compatibility {
2167
2167
* It might be called twice from the public `resolveOverloaded` method, once with
2168
2168
* implicits and SAM conversions enabled, and once without.
2169
2169
*/
2170
- private def resolveOverloaded1 (alts : List [TermRef ], pt : Type )(using Context ): List [TermRef ] =
2170
+ private def resolveOverloaded1 (alts : List [TermRef ], pt : Type , srcPos : SrcPos )(using Context ): List [TermRef ] =
2171
2171
trace(i " resolve over $alts%, %, pt = $pt" , typr, show = true ) {
2172
2172
record(s " resolveOverloaded1 " , alts.length)
2173
2173
2174
+ val sv = Feature .sourceVersion
2175
+ val isOldPriorityVersion : Boolean = sv.isAtMost(SourceVersion .`3.6`)
2176
+ val isWarnPriorityChangeVersion = sv == SourceVersion .`3.6` || sv == SourceVersion .`3.7-migration`
2177
+
2178
+ inline def warnOnPriorityChange (oldCands : List [TermRef ], newCands : List [TermRef ])(f : List [TermRef ] => List [TermRef ]): List [TermRef ] =
2179
+
2180
+ def doWarn (oldChoice : String , newChoice : String ): Unit =
2181
+ val (change, whichChoice) =
2182
+ if isOldPriorityVersion
2183
+ then (" will change" , " Current choice " )
2184
+ else (" has changed" , " Previous choice" )
2185
+
2186
+ val msg = // uses oldCands as the list of alternatives since they should be a superset of newCands
2187
+ em """ Overloading resolution for ${err.expectedTypeStr(pt)} between alternatives
2188
+ | ${oldCands map (_.info)}%\n %
2189
+ | $change.
2190
+ | $whichChoice : $oldChoice
2191
+ |New choice from Scala 3.7: $newChoice"""
2192
+
2193
+ report.warning(msg, srcPos)
2194
+ end doWarn
2195
+
2196
+ lazy val oldRes = f(oldCands)
2197
+ val newRes = f(newCands)
2198
+
2199
+ if isWarnPriorityChangeVersion then (oldRes, newRes) match
2200
+ case (oldAlt :: Nil , newAlt :: Nil ) if oldAlt != newAlt => doWarn(oldAlt.info.show, newAlt.info.show)
2201
+ case (oldAlt :: Nil , Nil ) => doWarn(oldAlt.info.show, " none" )
2202
+ case (Nil , newAlt :: Nil ) => doWarn(" none" , newAlt.info.show)
2203
+ case _ => // neither scheme has determined an alternative
2204
+
2205
+ if isOldPriorityVersion then oldRes else newRes
2206
+ end warnOnPriorityChange
2207
+
2174
2208
def isDetermined (alts : List [TermRef ]) = alts.isEmpty || alts.tail.isEmpty
2175
2209
2176
2210
/** The shape of given tree as a type; cannot handle named arguments. */
@@ -2299,7 +2333,7 @@ trait Applications extends Compatibility {
2299
2333
TypeOps .boundsViolations(targs1, tp.paramInfos, _.substParams(tp, _), NoType ).isEmpty
2300
2334
val alts2 = alts1.filter(withinBounds)
2301
2335
if isDetermined(alts2) then alts2
2302
- else resolveMapped(alts1, _.widen.appliedTo(targs1.tpes), pt1)
2336
+ else resolveMapped(alts1, _.widen.appliedTo(targs1.tpes), pt1, srcPos )
2303
2337
2304
2338
case pt =>
2305
2339
val compat = alts.filterConserve(normalizedCompatible(_, pt, keepConstraint = false ))
@@ -2357,37 +2391,37 @@ trait Applications extends Compatibility {
2357
2391
candidates
2358
2392
else
2359
2393
val found = narrowMostSpecific(candidates)
2360
- if found.length <= 1 then found
2394
+ if isDetermined( found) then found
2361
2395
else
2362
2396
val deepPt = pt.deepenProto
2363
2397
deepPt match
2364
2398
case pt @ FunProto (_, PolyProto (targs, resType)) =>
2365
2399
// try to narrow further with snd argument list and following type params
2366
- resolveMapped(found,
2367
- skipParamClause(pt.typedArgs().tpes, targs.tpes), resType)
2400
+ warnOnPriorityChange(candidates, found) :
2401
+ resolveMapped(_, skipParamClause(pt.typedArgs().tpes, targs.tpes), resType, srcPos )
2368
2402
case pt @ FunProto (_, resType : FunOrPolyProto ) =>
2369
2403
// try to narrow further with snd argument list
2370
- resolveMapped(found,
2371
- skipParamClause(pt.typedArgs().tpes, Nil ), resType)
2404
+ warnOnPriorityChange(candidates, found) :
2405
+ resolveMapped(_, skipParamClause(pt.typedArgs().tpes, Nil ), resType, srcPos )
2372
2406
case _ =>
2373
2407
// prefer alternatives that need no eta expansion
2374
2408
val noCurried = alts.filterConserve(! resultIsMethod(_))
2375
2409
val noCurriedCount = noCurried.length
2376
2410
if noCurriedCount == 1 then
2377
2411
noCurried
2378
2412
else if noCurriedCount > 1 && noCurriedCount < alts.length then
2379
- resolveOverloaded1(noCurried, pt)
2413
+ resolveOverloaded1(noCurried, pt, srcPos )
2380
2414
else
2381
2415
// prefer alternatves that match without default parameters
2382
2416
val noDefaults = alts.filterConserve(! _.symbol.hasDefaultParams)
2383
2417
val noDefaultsCount = noDefaults.length
2384
2418
if noDefaultsCount == 1 then
2385
2419
noDefaults
2386
2420
else if noDefaultsCount > 1 && noDefaultsCount < alts.length then
2387
- resolveOverloaded1(noDefaults, pt)
2421
+ resolveOverloaded1(noDefaults, pt, srcPos )
2388
2422
else if deepPt ne pt then
2389
2423
// try again with a deeper known expected type
2390
- resolveOverloaded1(alts, deepPt)
2424
+ resolveOverloaded1(alts, deepPt, srcPos )
2391
2425
else
2392
2426
candidates
2393
2427
}
@@ -2414,7 +2448,7 @@ trait Applications extends Compatibility {
2414
2448
* type is mapped with `f`, alternatives with non-existing types or symbols are dropped, and the
2415
2449
* expected type is `pt`. Map the results back to the original alternatives.
2416
2450
*/
2417
- def resolveMapped (alts : List [TermRef ], f : TermRef => Type , pt : Type )(using Context ): List [TermRef ] =
2451
+ def resolveMapped (alts : List [TermRef ], f : TermRef => Type , pt : Type , srcPos : SrcPos )(using Context ): List [TermRef ] =
2418
2452
val reverseMapping = alts.flatMap { alt =>
2419
2453
val t = f(alt)
2420
2454
if t.exists && alt.symbol.exists then
@@ -2437,7 +2471,7 @@ trait Applications extends Compatibility {
2437
2471
}
2438
2472
val mapped = reverseMapping.map(_._1)
2439
2473
overload.println(i " resolve mapped: ${mapped.map(_.widen)}%, % with $pt" )
2440
- resolveOverloaded(mapped, pt)(using ctx.retractMode(Mode .SynthesizeExtMethodReceiver ))
2474
+ resolveOverloaded(mapped, pt, srcPos )(using ctx.retractMode(Mode .SynthesizeExtMethodReceiver ))
2441
2475
.map(reverseMapping.toMap)
2442
2476
2443
2477
/** Try to typecheck any arguments in `pt` that are function values missing a
0 commit comments