@@ -2116,7 +2116,7 @@ trait Applications extends Compatibility {
2116
2116
* Two trials: First, without implicits or SAM conversions enabled. Then,
2117
2117
* if the first finds no eligible candidates, with implicits and SAM conversions enabled.
2118
2118
*/
2119
- def resolveOverloaded (alts : List [TermRef ], pt : Type )(using Context ): List [TermRef ] =
2119
+ def resolveOverloaded (alts : List [TermRef ], pt : Type , srcPos : SrcPos )(using Context ): List [TermRef ] =
2120
2120
record(" resolveOverloaded" )
2121
2121
2122
2122
/** Is `alt` a method or polytype whose result type after the first value parameter
@@ -2154,7 +2154,7 @@ trait Applications extends Compatibility {
2154
2154
case Nil => chosen
2155
2155
case alt2 :: Nil => alt2
2156
2156
case alts2 =>
2157
- resolveOverloaded(alts2, pt) match {
2157
+ resolveOverloaded(alts2, pt, srcPos ) match {
2158
2158
case alt2 :: Nil => alt2
2159
2159
case _ => chosen
2160
2160
}
@@ -2169,12 +2169,12 @@ trait Applications extends Compatibility {
2169
2169
val alts0 = alts.filterConserve(_.widen.stripPoly.isImplicitMethod)
2170
2170
if alts0 ne alts then return resolve(alts0)
2171
2171
else if alts.exists(_.widen.stripPoly.isContextualMethod) then
2172
- return resolveMapped(alts, alt => stripImplicit(alt.widen), pt)
2172
+ return resolveMapped(alts, alt => stripImplicit(alt.widen), pt, srcPos )
2173
2173
case _ =>
2174
2174
2175
- var found = withoutMode(Mode .ImplicitsEnabled )(resolveOverloaded1(alts, pt))
2175
+ var found = withoutMode(Mode .ImplicitsEnabled )(resolveOverloaded1(alts, pt, srcPos ))
2176
2176
if found.isEmpty && ctx.mode.is(Mode .ImplicitsEnabled ) then
2177
- found = resolveOverloaded1(alts, pt)
2177
+ found = resolveOverloaded1(alts, pt, srcPos )
2178
2178
found match
2179
2179
case alt :: Nil => adaptByResult(alt, alts) :: Nil
2180
2180
case _ => found
@@ -2221,10 +2221,44 @@ trait Applications extends Compatibility {
2221
2221
* It might be called twice from the public `resolveOverloaded` method, once with
2222
2222
* implicits and SAM conversions enabled, and once without.
2223
2223
*/
2224
- private def resolveOverloaded1 (alts : List [TermRef ], pt : Type )(using Context ): List [TermRef ] =
2224
+ private def resolveOverloaded1 (alts : List [TermRef ], pt : Type , srcPos : SrcPos )(using Context ): List [TermRef ] =
2225
2225
trace(i " resolve over $alts%, %, pt = $pt" , typr, show = true ) {
2226
2226
record(s " resolveOverloaded1 " , alts.length)
2227
2227
2228
+ val sv = Feature .sourceVersion
2229
+ val isOldPriorityVersion : Boolean = sv.isAtMost(SourceVersion .`3.6`)
2230
+ val isWarnPriorityChangeVersion = sv == SourceVersion .`3.6` || sv == SourceVersion .`3.7-migration`
2231
+
2232
+ inline def warnOnPriorityChange (oldCands : List [TermRef ], newCands : List [TermRef ])(f : List [TermRef ] => List [TermRef ]): List [TermRef ] =
2233
+
2234
+ def doWarn (oldChoice : String , newChoice : String ): Unit =
2235
+ val (change, whichChoice) =
2236
+ if isOldPriorityVersion
2237
+ then (" will change" , " Current choice " )
2238
+ else (" has changed" , " Previous choice" )
2239
+
2240
+ val msg = // uses oldCands as the list of alternatives since they should be a superset of newCands
2241
+ em """ Overloading resolution for ${err.expectedTypeStr(pt)} between alternatives
2242
+ | ${oldCands map (_.info)}%\n %
2243
+ | $change.
2244
+ | $whichChoice : $oldChoice
2245
+ |New choice from Scala 3.7: $newChoice"""
2246
+
2247
+ report.warning(msg, srcPos)
2248
+ end doWarn
2249
+
2250
+ lazy val oldRes = f(oldCands)
2251
+ val newRes = f(newCands)
2252
+
2253
+ if isWarnPriorityChangeVersion then (oldRes, newRes) match
2254
+ case (oldAlt :: Nil , newAlt :: Nil ) if oldAlt != newAlt => doWarn(oldAlt.info.show, newAlt.info.show)
2255
+ case (oldAlt :: Nil , Nil ) => doWarn(oldAlt.info.show, " none" )
2256
+ case (Nil , newAlt :: Nil ) => doWarn(" none" , newAlt.info.show)
2257
+ case _ => // neither scheme has determined an alternative
2258
+
2259
+ if isOldPriorityVersion then oldRes else newRes
2260
+ end warnOnPriorityChange
2261
+
2228
2262
def isDetermined (alts : List [TermRef ]) = alts.isEmpty || alts.tail.isEmpty
2229
2263
2230
2264
/** The shape of given tree as a type; cannot handle named arguments. */
@@ -2372,7 +2406,7 @@ trait Applications extends Compatibility {
2372
2406
TypeOps .boundsViolations(targs1, tp.paramInfos, _.substParams(tp, _), NoType ).isEmpty
2373
2407
val alts2 = alts1.filter(withinBounds)
2374
2408
if isDetermined(alts2) then alts2
2375
- else resolveMapped(alts1, _.widen.appliedTo(targs1.tpes), pt1)
2409
+ else resolveMapped(alts1, _.widen.appliedTo(targs1.tpes), pt1, srcPos )
2376
2410
2377
2411
case pt =>
2378
2412
val compat = alts.filterConserve(normalizedCompatible(_, pt, keepConstraint = false ))
@@ -2430,37 +2464,37 @@ trait Applications extends Compatibility {
2430
2464
candidates
2431
2465
else
2432
2466
val found = narrowMostSpecific(candidates)
2433
- if found.length <= 1 then found
2467
+ if isDetermined( found) then found
2434
2468
else
2435
2469
val deepPt = pt.deepenProto
2436
2470
deepPt match
2437
2471
case pt @ FunProto (_, PolyProto (targs, resType)) =>
2438
2472
// try to narrow further with snd argument list and following type params
2439
- resolveMapped(found,
2440
- skipParamClause(pt.typedArgs().tpes, targs.tpes), resType)
2473
+ warnOnPriorityChange(candidates, found) :
2474
+ resolveMapped(_, skipParamClause(pt.typedArgs().tpes, targs.tpes), resType, srcPos )
2441
2475
case pt @ FunProto (_, resType : FunOrPolyProto ) =>
2442
2476
// try to narrow further with snd argument list
2443
- resolveMapped(found,
2444
- skipParamClause(pt.typedArgs().tpes, Nil ), resType)
2477
+ warnOnPriorityChange(candidates, found) :
2478
+ resolveMapped(_, skipParamClause(pt.typedArgs().tpes, Nil ), resType, srcPos )
2445
2479
case _ =>
2446
2480
// prefer alternatives that need no eta expansion
2447
2481
val noCurried = alts.filterConserve(! resultIsMethod(_))
2448
2482
val noCurriedCount = noCurried.length
2449
2483
if noCurriedCount == 1 then
2450
2484
noCurried
2451
2485
else if noCurriedCount > 1 && noCurriedCount < alts.length then
2452
- resolveOverloaded1(noCurried, pt)
2486
+ resolveOverloaded1(noCurried, pt, srcPos )
2453
2487
else
2454
2488
// prefer alternatves that match without default parameters
2455
2489
val noDefaults = alts.filterConserve(! _.symbol.hasDefaultParams)
2456
2490
val noDefaultsCount = noDefaults.length
2457
2491
if noDefaultsCount == 1 then
2458
2492
noDefaults
2459
2493
else if noDefaultsCount > 1 && noDefaultsCount < alts.length then
2460
- resolveOverloaded1(noDefaults, pt)
2494
+ resolveOverloaded1(noDefaults, pt, srcPos )
2461
2495
else if deepPt ne pt then
2462
2496
// try again with a deeper known expected type
2463
- resolveOverloaded1(alts, deepPt)
2497
+ resolveOverloaded1(alts, deepPt, srcPos )
2464
2498
else
2465
2499
candidates
2466
2500
}
@@ -2494,7 +2528,7 @@ trait Applications extends Compatibility {
2494
2528
* type is mapped with `f`, alternatives with non-existing types or symbols are dropped, and the
2495
2529
* expected type is `pt`. Map the results back to the original alternatives.
2496
2530
*/
2497
- def resolveMapped (alts : List [TermRef ], f : TermRef => Type , pt : Type )(using Context ): List [TermRef ] =
2531
+ def resolveMapped (alts : List [TermRef ], f : TermRef => Type , pt : Type , srcPos : SrcPos )(using Context ): List [TermRef ] =
2498
2532
val reverseMapping = alts.flatMap { alt =>
2499
2533
val t = f(alt)
2500
2534
if t.exists && alt.symbol.exists then
@@ -2517,7 +2551,7 @@ trait Applications extends Compatibility {
2517
2551
}
2518
2552
val mapped = reverseMapping.map(_._1)
2519
2553
overload.println(i " resolve mapped: ${mapped.map(_.widen)}%, % with $pt" )
2520
- resolveOverloaded(mapped, pt)(using ctx.retractMode(Mode .SynthesizeExtMethodReceiver ))
2554
+ resolveOverloaded(mapped, pt, srcPos )(using ctx.retractMode(Mode .SynthesizeExtMethodReceiver ))
2521
2555
.map(reverseMapping.toMap)
2522
2556
2523
2557
/** Try to typecheck any arguments in `pt` that are function values missing a
0 commit comments