Skip to content

Commit

Permalink
Prefer extensions over conversions and implicits for member selection
Browse files Browse the repository at this point in the history
Before the changes, if
`isAsGoodValueType` was called with an extension and a given conversion,
it would prefer the conversion over the extension,
because only the former yielded true in `isGiven`.

Which contradicted the logic from searchImplicit which
preferred extension over conversions for member selection.
  • Loading branch information
EugeneFlesselle authored and WojciechMazur committed Jul 24, 2024
1 parent a11acbd commit cdf3dec
Show file tree
Hide file tree
Showing 2 changed files with 8 additions and 9 deletions.
14 changes: 6 additions & 8 deletions compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1816,10 +1816,8 @@ trait Applications extends Compatibility {
isAsGood(alt1, tp1.instantiate(tparams.map(_.typeRef)), alt2, tp2)
}
case _ => // (3)
def isGiven(alt: TermRef) =
alt.symbol.is(Given) && alt.symbol != defn.NotGivenClass
def compareValues(tp1: Type, tp2: Type)(using Context) =
isAsGoodValueType(tp1, tp2, isGiven(alt1), isGiven(alt2))
isAsGoodValueType(tp1, tp2, alt1.symbol.is(Implicit), alt2.symbol.is(Implicit))
tp2 match
case tp2: MethodType => true // (3a)
case tp2: PolyType if tp2.resultType.isInstanceOf[MethodType] => true // (3a)
Expand Down Expand Up @@ -1856,15 +1854,15 @@ trait Applications extends Compatibility {
* for overloading resolution (when `preferGeneral is false), and the opposite relation
* `U <: T` or `U convertible to `T` for implicit disambiguation between givens
* (when `preferGeneral` is true). For old-style implicit values, the 3.4 behavior is kept.
* If one of the alternatives is a given and the other is an implicit, the given wins.
* If one of the alternatives is an implicit and the other is a given (or an extension), the implicit loses.
*
* - In Scala 3.5 and Scala 3.6-migration, we issue a warning if the result under
* Scala 3.6 differ wrt to the old behavior up to 3.5.
*
* Also and only for given resolution: If a compared type refers to a given or its module class, use
* the intersection of its parent classes instead.
*/
def isAsGoodValueType(tp1: Type, tp2: Type, alt1isGiven: Boolean, alt2isGiven: Boolean)(using Context): Boolean =
def isAsGoodValueType(tp1: Type, tp2: Type, alt1IsImplicit: Boolean, alt2IsImplicit: Boolean)(using Context): Boolean =
val oldResolution = ctx.mode.is(Mode.OldImplicitResolution)
if !preferGeneral || Feature.migrateTo3 && oldResolution then
// Normal specificity test for overloading resolution (where `preferGeneral` is false)
Expand All @@ -1882,7 +1880,7 @@ trait Applications extends Compatibility {

if Feature.sourceVersion.isAtMost(SourceVersion.`3.4`)
|| oldResolution
|| !alt1isGiven && !alt2isGiven
|| alt1IsImplicit && alt2IsImplicit
then
// Intermediate rules: better means specialize, but map all type arguments downwards
// These are enabled for 3.0-3.5, and for all comparisons between old-style implicits,
Expand All @@ -1897,8 +1895,8 @@ trait Applications extends Compatibility {
case _ => mapOver(t)
(flip(tp1p) relaxed_<:< flip(tp2p)) || viewExists(tp1, tp2)
else
// New rules: better means generalize, givens always beat implicits
if alt1isGiven != alt2isGiven then alt1isGiven
// New rules: better means generalize, givens (and extensions) always beat implicits
if alt1IsImplicit != alt2IsImplicit then alt2IsImplicit
else (tp2p relaxed_<:< tp1p) || viewExists(tp2, tp1)
end isAsGoodValueType

Expand Down
3 changes: 2 additions & 1 deletion tests/pos/i19715.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ class NT(t: Tup):
object NT:
extension (x: NT)
def app(n: Int): Boolean = true
given Conversion[NT, Tup] = _.toTup
given c1: Conversion[NT, Tup] = _.toTup
implicit def c2(t: NT): Tup = c1(t)

def test =
val nt = new NT(Tup())
Expand Down

0 comments on commit cdf3dec

Please sign in to comment.