@@ -2062,7 +2062,7 @@ trait Applications extends Compatibility {
20622062 * Two trials: First, without implicits or SAM conversions enabled. Then,
20632063 * if the first finds no eligible candidates, with implicits and SAM conversions enabled.
20642064 */
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 ] =
20662066 record(" resolveOverloaded" )
20672067
20682068 /** Is `alt` a method or polytype whose result type after the first value parameter
@@ -2100,7 +2100,7 @@ trait Applications extends Compatibility {
21002100 case Nil => chosen
21012101 case alt2 :: Nil => alt2
21022102 case alts2 =>
2103- resolveOverloaded(alts2, pt) match {
2103+ resolveOverloaded(alts2, pt, srcPos ) match {
21042104 case alt2 :: Nil => alt2
21052105 case _ => chosen
21062106 }
@@ -2115,12 +2115,12 @@ trait Applications extends Compatibility {
21152115 val alts0 = alts.filterConserve(_.widen.stripPoly.isImplicitMethod)
21162116 if alts0 ne alts then return resolve(alts0)
21172117 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 )
21192119 case _ =>
21202120
2121- var found = withoutMode(Mode .ImplicitsEnabled )(resolveOverloaded1(alts, pt))
2121+ var found = withoutMode(Mode .ImplicitsEnabled )(resolveOverloaded1(alts, pt, srcPos ))
21222122 if found.isEmpty && ctx.mode.is(Mode .ImplicitsEnabled ) then
2123- found = resolveOverloaded1(alts, pt)
2123+ found = resolveOverloaded1(alts, pt, srcPos )
21242124 found match
21252125 case alt :: Nil => adaptByResult(alt, alts) :: Nil
21262126 case _ => found
@@ -2167,10 +2167,44 @@ trait Applications extends Compatibility {
21672167 * It might be called twice from the public `resolveOverloaded` method, once with
21682168 * implicits and SAM conversions enabled, and once without.
21692169 */
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 ] =
21712171 trace(i " resolve over $alts%, %, pt = $pt" , typr, show = true ) {
21722172 record(s " resolveOverloaded1 " , alts.length)
21732173
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+
21742208 def isDetermined (alts : List [TermRef ]) = alts.isEmpty || alts.tail.isEmpty
21752209
21762210 /** The shape of given tree as a type; cannot handle named arguments. */
@@ -2299,7 +2333,7 @@ trait Applications extends Compatibility {
22992333 TypeOps .boundsViolations(targs1, tp.paramInfos, _.substParams(tp, _), NoType ).isEmpty
23002334 val alts2 = alts1.filter(withinBounds)
23012335 if isDetermined(alts2) then alts2
2302- else resolveMapped(alts1, _.widen.appliedTo(targs1.tpes), pt1)
2336+ else resolveMapped(alts1, _.widen.appliedTo(targs1.tpes), pt1, srcPos )
23032337
23042338 case pt =>
23052339 val compat = alts.filterConserve(normalizedCompatible(_, pt, keepConstraint = false ))
@@ -2357,37 +2391,37 @@ trait Applications extends Compatibility {
23572391 candidates
23582392 else
23592393 val found = narrowMostSpecific(candidates)
2360- if found.length <= 1 then found
2394+ if isDetermined( found) then found
23612395 else
23622396 val deepPt = pt.deepenProto
23632397 deepPt match
23642398 case pt @ FunProto (_, PolyProto (targs, resType)) =>
23652399 // 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 )
23682402 case pt @ FunProto (_, resType : FunOrPolyProto ) =>
23692403 // 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 )
23722406 case _ =>
23732407 // prefer alternatives that need no eta expansion
23742408 val noCurried = alts.filterConserve(! resultIsMethod(_))
23752409 val noCurriedCount = noCurried.length
23762410 if noCurriedCount == 1 then
23772411 noCurried
23782412 else if noCurriedCount > 1 && noCurriedCount < alts.length then
2379- resolveOverloaded1(noCurried, pt)
2413+ resolveOverloaded1(noCurried, pt, srcPos )
23802414 else
23812415 // prefer alternatves that match without default parameters
23822416 val noDefaults = alts.filterConserve(! _.symbol.hasDefaultParams)
23832417 val noDefaultsCount = noDefaults.length
23842418 if noDefaultsCount == 1 then
23852419 noDefaults
23862420 else if noDefaultsCount > 1 && noDefaultsCount < alts.length then
2387- resolveOverloaded1(noDefaults, pt)
2421+ resolveOverloaded1(noDefaults, pt, srcPos )
23882422 else if deepPt ne pt then
23892423 // try again with a deeper known expected type
2390- resolveOverloaded1(alts, deepPt)
2424+ resolveOverloaded1(alts, deepPt, srcPos )
23912425 else
23922426 candidates
23932427 }
@@ -2414,7 +2448,7 @@ trait Applications extends Compatibility {
24142448 * type is mapped with `f`, alternatives with non-existing types or symbols are dropped, and the
24152449 * expected type is `pt`. Map the results back to the original alternatives.
24162450 */
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 ] =
24182452 val reverseMapping = alts.flatMap { alt =>
24192453 val t = f(alt)
24202454 if t.exists && alt.symbol.exists then
@@ -2437,7 +2471,7 @@ trait Applications extends Compatibility {
24372471 }
24382472 val mapped = reverseMapping.map(_._1)
24392473 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 ))
24412475 .map(reverseMapping.toMap)
24422476
24432477 /** Try to typecheck any arguments in `pt` that are function values missing a
0 commit comments