Skip to content

Commit ed9f427

Browse files
committed
Make the new behavior dependent on source >= 3.4
1 parent fab7147 commit ed9f427

File tree

7 files changed

+122
-69
lines changed

7 files changed

+122
-69
lines changed

compiler/src/dotty/tools/dotc/typer/Implicits.scala

Lines changed: 49 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1587,46 +1587,54 @@ trait Implicits:
15871587
&& candSucceedsGiven(ctx.owner)
15881588
end comesTooLate
15891589

1590-
val eligible = if contextual then preEligible.filterNot(comesTooLate) else preEligible
1590+
val eligible = // the eligible candidates that come before the search point
1591+
if contextual && sourceVersion.isAtLeast(SourceVersion.`3.4`)
1592+
then preEligible.filterNot(comesTooLate)
1593+
else preEligible
15911594

15921595
def checkResolutionChange(result: SearchResult) =
15931596
if (eligible ne preEligible)
15941597
&& !Feature.enabled(Feature.avoidLoopingGivens)
1595-
then searchImplicit(preEligible.diff(eligible), contextual) match
1596-
case prevResult: SearchSuccess =>
1597-
def remedy = pt match
1598-
case _: SelectionProto =>
1599-
"conversion,\n - use an import to get extension method into scope"
1600-
case _: ViewProto =>
1601-
"conversion"
1602-
case _ =>
1603-
"argument"
1604-
1605-
def showResult(r: SearchResult) = r match
1606-
case r: SearchSuccess => ctx.printer.toTextRef(r.ref).show
1607-
case r => r.show
1608-
1609-
result match
1610-
case result: SearchSuccess if prevResult.ref frozen_=:= result.ref =>
1611-
// OK
1612-
case _ =>
1613-
report.error(
1614-
em"""Warning: result of implicit search for $pt will change.
1615-
|Current result ${showResult(prevResult)} will be no longer eligible
1616-
| because it is not defined before the search position.
1617-
|Result with new rules: ${showResult(result)}.
1618-
|To opt into the new rules, use the `avoidLoopingGivens` language import,
1619-
|
1620-
|To fix the problem you could try one of the following:
1621-
| - rearrange definitions,
1622-
| - use an explicit $remedy.""",
1623-
srcPos)
1624-
case _ =>
1598+
then
1599+
val prevResult = searchImplicit(preEligible, contextual)
1600+
prevResult match
1601+
case prevResult: SearchSuccess =>
1602+
def remedy = pt match
1603+
case _: SelectionProto =>
1604+
"conversion,\n - use an import to get extension method into scope"
1605+
case _: ViewProto =>
1606+
"conversion"
1607+
case _ =>
1608+
"argument"
1609+
1610+
def showResult(r: SearchResult) = r match
1611+
case r: SearchSuccess => ctx.printer.toTextRef(r.ref).show
1612+
case r => r.show
1613+
1614+
result match
1615+
case result: SearchSuccess if prevResult.ref frozen_=:= result.ref =>
1616+
// OK
1617+
case _ =>
1618+
report.error(
1619+
em"""Warning: result of implicit search for $pt will change.
1620+
|Current result ${showResult(prevResult)} will be no longer eligible
1621+
| because it is not defined before the search position.
1622+
|Result with new rules: ${showResult(result)}.
1623+
|To opt into the new rules, use the `experimental.avoidLoopingGivens` language import.
1624+
|
1625+
|To fix the problem without the language import, you could try one of the following:
1626+
| - rearrange definitions so that ${showResult(prevResult)} comes earlier,
1627+
| - use an explicit $remedy.""",
1628+
srcPos)
1629+
case _ =>
1630+
prevResult
1631+
else result
16251632
end checkResolutionChange
16261633

1627-
searchImplicit(eligible, contextual) match
1634+
val result = searchImplicit(eligible, contextual)
1635+
result match
16281636
case result: SearchSuccess =>
1629-
result
1637+
checkResolutionChange(result)
16301638
case failure: SearchFailure =>
16311639
failure.reason match
16321640
case _: AmbiguousImplicits => failure
@@ -1641,15 +1649,14 @@ trait Implicits:
16411649
else ctxImplicits.nn.outerImplicits: ContextualImplicits | Null
16421650
// !!! Dotty problem: without the ContextualImplicits | Null type ascription
16431651
// we get a Ycheck failure after arrayConstructors due to "Types differ"
1644-
val result = searchImplicit(newCtxImplicits).recoverWith:
1645-
failure2 => failure2.reason match
1646-
case _: AmbiguousImplicits => failure2
1647-
case _ =>
1648-
reason match
1649-
case (_: DivergingImplicit) => failure
1650-
case _ => List(failure, failure2).maxBy(_.tree.treeSize)
1651-
checkResolutionChange(result)
1652-
result
1652+
checkResolutionChange:
1653+
searchImplicit(newCtxImplicits).recoverWith:
1654+
failure2 => failure2.reason match
1655+
case _: AmbiguousImplicits => failure2
1656+
case _ =>
1657+
reason match
1658+
case (_: DivergingImplicit) => failure
1659+
case _ => List(failure, failure2).maxBy(_.tree.treeSize)
16531660
else failure
16541661
end searchImplicit
16551662

tests/neg/i15474.check

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,38 @@
1+
-- Error: tests/neg/i15474.scala:7:35 ----------------------------------------------------------------------------------
2+
7 | def apply(from: String): Int = from.toInt // error
3+
| ^^^^
4+
| Warning: result of implicit search for ?{ toInt: ? } will change.
5+
| Current result Test1.c will be no longer eligible
6+
| because it is not defined before the search position.
7+
| Result with new rules: augmentString.
8+
| To opt into the new rules, use the `experimental.avoidLoopingGivens` language import.
9+
|
10+
| To fix the problem without the language import, you could try one of the following:
11+
| - rearrange definitions so that Test1.c comes earlier,
12+
| - use an explicit conversion,
13+
| - use an import to get extension method into scope.
14+
-- Error: tests/neg/i15474.scala:10:39 ---------------------------------------------------------------------------------
15+
10 | given c: Conversion[ String, Int ] = _.toInt // error
16+
| ^
17+
| Warning: result of implicit search for ?{ toInt: ? } will change.
18+
| Current result Test2.c will be no longer eligible
19+
| because it is not defined before the search position.
20+
| Result with new rules: augmentString.
21+
| To opt into the new rules, use the `experimental.avoidLoopingGivens` language import.
22+
|
23+
| To fix the problem without the language import, you could try one of the following:
24+
| - rearrange definitions so that Test2.c comes earlier,
25+
| - use an explicit conversion,
26+
| - use an import to get extension method into scope.
127
-- Error: tests/neg/i15474.scala:16:56 ---------------------------------------------------------------------------------
228
16 | given Ordering[Price] = summon[Ordering[BigDecimal]] // error
329
| ^
4-
| Warning: result of implicit search for Ordering[BigDecimal] will change.
5-
| Current result Prices.Price.given_Ordering_Price will be no longer eligible
6-
| because it is not defined before the search position.
7-
| Result with new rules: scala.math.Ordering.BigDecimal.
8-
| To opt into the new rules, use the `avoidLoopingGivens` language import,
30+
| Warning: result of implicit search for Ordering[BigDecimal] will change.
31+
| Current result Prices.Price.given_Ordering_Price will be no longer eligible
32+
| because it is not defined before the search position.
33+
| Result with new rules: scala.math.Ordering.BigDecimal.
34+
| To opt into the new rules, use the `experimental.avoidLoopingGivens` language import.
935
|
10-
| To fix the problem you could try one of the following:
11-
| - rearrange definitions,
12-
| - use an explicit argument.
36+
| To fix the problem without the language import, you could try one of the following:
37+
| - rearrange definitions so that Prices.Price.given_Ordering_Price comes earlier,
38+
| - use an explicit argument.

tests/neg/i15474.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import scala.language.implicitConversions
44

55
object Test1:
66
given c: Conversion[ String, Int ] with
7-
def apply(from: String): Int = from.toInt // was error, now avoided
7+
def apply(from: String): Int = from.toInt // error
88

99
object Test2:
10-
given c: Conversion[ String, Int ] = _.toInt // now avoided, was loop not detected, could be used as a fallback to avoid the warning.
10+
given c: Conversion[ String, Int ] = _.toInt // error
1111

1212
object Prices {
1313
opaque type Price = BigDecimal

tests/neg/i6716.check

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
-- Error: tests/neg/i6716.scala:12:39 ----------------------------------------------------------------------------------
22
12 | given Monad[Bar] = summon[Monad[Foo]] // error
33
| ^
4-
| Warning: result of implicit search for Monad[Foo] will change.
5-
| Current result Bar.given_Monad_Bar will be no longer eligible
6-
| because it is not defined before the search position.
7-
| Result with new rules: Foo.given_Monad_Foo.
8-
| To opt into the new rules, use the `avoidLoopingGivens` language import,
4+
| Warning: result of implicit search for Monad[Foo] will change.
5+
| Current result Bar.given_Monad_Bar will be no longer eligible
6+
| because it is not defined before the search position.
7+
| Result with new rules: Foo.given_Monad_Foo.
8+
| To opt into the new rules, use the `experimental.avoidLoopingGivens` language import.
99
|
10-
| To fix the problem you could try one of the following:
11-
| - rearrange definitions,
12-
| - use an explicit argument.
10+
| To fix the problem without the language import, you could try one of the following:
11+
| - rearrange definitions so that Bar.given_Monad_Bar comes earlier,
12+
| - use an explicit argument.

tests/neg/i7294-a.check

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,19 @@
55
| Current result foo.i7294-a$package.f will be no longer eligible
66
| because it is not defined before the search position.
77
| Result with new rules: No Matching Implicit.
8-
| To opt into the new rules, use the `avoidLoopingGivens` language import,
8+
| To opt into the new rules, use the `experimental.avoidLoopingGivens` language import.
99
|
10-
| To fix the problem you could try one of the following:
11-
| - rearrange definitions,
10+
| To fix the problem without the language import, you could try one of the following:
11+
| - rearrange definitions so that foo.i7294-a$package.f comes earlier,
1212
| - use an explicit argument.
1313
|
1414
| where: T is a type in given instance f with bounds <: foo.Foo
15-
-- [E007] Type Mismatch Error: tests/neg/i7294-a.scala:6:15 ------------------------------------------------------------
15+
-- [E007] Type Mismatch Error: tests/neg/i7294-a.scala:6:18 ------------------------------------------------------------
1616
6 | case x: T => x.g(10) // error // error
17-
| ^
18-
| Found: (x : Nothing)
19-
| Required: ?{ g: ? }
20-
| Note that implicit conversions were not tried because the result of an implicit conversion
21-
| must be more specific than ?{ g: [applied to (10) returning T] }
17+
| ^^^^^^^
18+
| Found: Any
19+
| Required: T
20+
|
21+
| where: T is a type in given instance f with bounds <: foo.Foo
2222
|
2323
| longer explanation available when compiling with `-explain`

tests/neg/looping-givens.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class A
2+
class B
3+
4+
given joint(using a: A, b: B): (A & B) = ???
5+
6+
def foo(using a: A, b: B) =
7+
given aa: A = summon // error
8+
given bb: B = summon // error
9+
given ab: (A & B) = summon // error

tests/pos/looping-givens.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import language.experimental.avoidLoopingGivens
2+
3+
class A
4+
class B
5+
6+
given joint(using a: A, b: B): (A & B) = ???
7+
8+
def foo(using a: A, b: B) =
9+
given aa: A = summon // error
10+
given bb: B = summon // error
11+
given ab: (A & B) = summon // error

0 commit comments

Comments
 (0)