Skip to content

Commit

Permalink
Final versioning scheme
Browse files Browse the repository at this point in the history
  • Loading branch information
odersky committed Dec 20, 2023
1 parent 87803b0 commit 8159725
Show file tree
Hide file tree
Showing 8 changed files with 43 additions and 22 deletions.
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1749,12 +1749,13 @@ trait Applications extends Compatibility {
val tp1p = prepare(tp1)
val tp2p = prepare(tp2)

if Feature.sourceVersion.isAtMost(SourceVersion.`3.3`) // !!! change to 3.4
if Feature.sourceVersion.isAtMost(SourceVersion.`3.4`)
|| oldResolution
|| !compareGivens
then
// Intermediate rules: better means specialize, but map all type arguments downwards
// These are enabled for 3.0-3.4, and in 3.5-migration when we compare with previous rules
// These are enabled for 3.0-3.4, and for all comparisons between old-style implicits,
// and in 3.5-migration when we compare with previous rules.
val flip = new TypeMap:
def apply(t: Type) = t match
case t @ AppliedType(tycon, args) =>
Expand Down
33 changes: 14 additions & 19 deletions compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1290,27 +1290,22 @@ trait Implicits:
*/
def compareAlternatives(alt1: RefAndLevel, alt2: RefAndLevel): Int =
def comp(using Context) = explore(compare(alt1.ref, alt2.ref, preferGeneral = true))
def oldCompare() = comp(using searchContext().addMode(Mode.OldOverloadingResolution))
def newCompare() = comp(using searchContext())

if alt1.ref eq alt2.ref then 0
else if alt1.level != alt2.level then alt1.level - alt2.level
else if Feature.sourceVersion.isAtMost(SourceVersion.`3.3`) then // !!! change to 3.4
oldCompare()
else if Feature.sourceVersion == SourceVersion.`3.5-migration` || true then // !!! drop
val was = oldCompare()
val now = newCompare()
if was != now then
def choice(cmp: Int) = cmp match
case -1 => "the second alternative"
case 1 => "the first alternative"
case _ => "none - it's ambiguous"
report.warning(
em"""Change in given search preference for $pt between alternatives ${alt1.ref} and ${alt2.ref}
|Previous choice: ${choice(was)}
|New choice : ${choice(now)}""", srcPos)
now
else newCompare()
else
val cmp = comp(using searchContext())
if Feature.sourceVersion == SourceVersion.`3.5-migration` then
val prev = comp(using searchContext().addMode(Mode.OldOverloadingResolution))
if cmp != prev then
def choice(c: Int) = c match
case -1 => "the second alternative"
case 1 => "the first alternative"
case _ => "none - it's ambiguous"
report.warning(
em"""Change in given search preference for $pt between alternatives ${alt1.ref} and ${alt2.ref}
|Previous choice: ${choice(prev)}
|New choice : ${choice(cmp)}""", srcPos)
cmp
end compareAlternatives

/** If `alt1` is also a search success, try to disambiguate as follows:
Expand Down
14 changes: 13 additions & 1 deletion docs/_docs/reference/changed-features/implicit-resolution.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,20 @@ The new rules are as follows: An implicit `a` defined in `A` is more specific th

Condition (*) is new. It is necessary to ensure that the defined relation is transitive.

[//]: # todo: expand with precise rules


**9.** Given disambiguation has changed. When comparing two givens that both match an expected type, we used to pick the most specific one, in alignment with
overloading resolution. From Scala 3.5 on, we pick the most general one instead. Compiling with Scala 3.5-migration will print a warning in all cases where the preference has changed. Example:
```scala
class A
class B extends A
class C extends A

given A = A()
given B = B()
given C = C()

summon[A] // was ambiguous, will now return `given_A`
```

[//]: # todo: expand with precise rules
1 change: 1 addition & 0 deletions tests/neg/i15264.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import language.`3.5`
object priority:
// lower number = higher priority
class Prio0 extends Prio1
Expand Down
7 changes: 7 additions & 0 deletions tests/neg/implicit-arg.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class C

def foo(using C) = ()

def Test =
val c = C()
foo(c)
2 changes: 2 additions & 0 deletions tests/run/given-triangle.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import language.`3.5`

class A
class B extends A
class C extends A
Expand Down
2 changes: 2 additions & 0 deletions tests/run/implicit-specifity.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import language.`3.5`

case class Show[T](val i: Int)
object Show {
def apply[T](implicit st: Show[T]): Int = st.i
Expand Down
1 change: 1 addition & 0 deletions tests/run/implied-priority.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* These tests show various mechanisms available for implicit prioritization.
*/
import language.`3.5`

class E[T](val str: String) // The type for which we infer terms below

Expand Down

0 comments on commit 8159725

Please sign in to comment.