Skip to content

Commit 1d56bb1

Browse files
authored
Merge pull request #2739 from dotty-staging/fix-ho-implicits
Fix spurious shadowing for higher-order implicits
2 parents c9e2e6a + d4c237e commit 1d56bb1

File tree

4 files changed

+98
-3
lines changed

4 files changed

+98
-3
lines changed

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -752,10 +752,12 @@ trait Implicits { self: Typer =>
752752
lazy val shadowing =
753753
typed(untpd.Ident(ref.name) withPos pos.toSynthetic, funProto)(
754754
nestedContext.addMode(Mode.ImplicitShadowing).setExploreTyperState)
755-
def refMatches(shadowing: Tree): Boolean =
755+
def refSameAs(shadowing: Tree): Boolean =
756756
ref.symbol == closureBody(shadowing).symbol || {
757757
shadowing match {
758-
case Trees.Select(qual, nme.apply) => refMatches(qual)
758+
case Trees.Select(qual, nme.apply) => refSameAs(qual)
759+
case Trees.Apply(fn, _) => refSameAs(fn)
760+
case Trees.TypeApply(fn, _) => refSameAs(fn)
759761
case _ => false
760762
}
761763
}
@@ -782,7 +784,7 @@ trait Implicits { self: Typer =>
782784
if (ctx.reporter.hasErrors)
783785
nonMatchingImplicit(ref, ctx.reporter.removeBufferedMessages)
784786
else if (contextual && !ctx.mode.is(Mode.ImplicitShadowing) &&
785-
!shadowing.tpe.isError && !refMatches(shadowing)) {
787+
!shadowing.tpe.isError && !refSameAs(shadowing)) {
786788
implicits.println(i"SHADOWING $ref in ${ref.termSymbol.owner} is shadowed by $shadowing in ${shadowing.symbol.owner}")
787789
shadowedImplicit(ref, methPart(shadowing).tpe)
788790
}

tests/pos/ho-implicits.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
object Test2 {
2+
3+
implicit def __1: implicit Int => String = s"implicit: ${implicitly[Int]}"
4+
implicit def __2: Int = 42
5+
6+
def f: implicit String => Int = implicitly[String].length
7+
8+
f: Int
9+
}

tests/pos/implicitFuns.scala

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package cm2
2+
3+
case class Person(name: String)
4+
case class Paper(title: String, authors: List[Person], body: String)
5+
6+
class Viewers(val persons: Set[Person])
7+
8+
class ConfManagement(papers: List[Paper], realScore: Map[Paper, Int]) extends App {
9+
10+
private def hasConflict(ps1: Set[Person], ps2: Iterable[Person]) =
11+
ps2.exists(ps1 contains _)
12+
13+
type Viewable[T] = implicit Viewers => T
14+
15+
def vs: Viewable[Viewers] = implicitly
16+
17+
def viewers: Viewable[Set[Person]] = vs.persons
18+
19+
def score: Paper => Viewable[Int] =
20+
paper =>
21+
if hasConflict(viewers, paper.authors) then -100
22+
else realScore(paper)
23+
24+
def viewRankings: Viewable[List[(String, Int)]] =
25+
papers.sortBy(-score(_)).map(p => (p.title, score(p)))
26+
27+
def delegate[T]: (Viewers => T) => Person => Viewable[T] =
28+
query => p => query(new Viewers(viewers + p))
29+
}
30+
31+
object Test extends App {
32+
def bob = Person("Bob")
33+
def peter = Person("Peter")
34+
def p1 = Paper("Bob's paper", List(bob), "")
35+
def p2 = Paper("Peter's paper", List(peter), "")
36+
37+
implicit def __1: Viewers = new Viewers(Set(bob))
38+
39+
val cm = new ConfManagement(List(p1, p2), Map(p1 -> 2, p2 -> 3))
40+
41+
println(cm.viewRankings)
42+
println(cm.score(p1))
43+
println(Orderings.isLess(Nil)(List(1, 2, 3)))
44+
}
45+
46+
object Orderings extends App {
47+
48+
trait Ord[T] { def less: T => T => Boolean }
49+
50+
implicit def __1: Ord[Int] = new Ord[Int] {
51+
def less: Int => Int => Boolean =
52+
x => y => x < y
53+
}
54+
55+
implicit def __2[T]: implicit Ord[T] => Ord[List[T]] = new Ord[List[T]] {
56+
def less: List[T] => List[T] => Boolean =
57+
xs => ys =>
58+
if ys.isEmpty then false
59+
else if xs.isEmpty then true
60+
else if xs.head == ys.head then less(xs.tail)(ys.tail)
61+
else isLess(xs.head)(ys.head)
62+
}
63+
64+
def isLess[T]: T => T => implicit Ord[T] => Boolean =
65+
x => y => implicitly[Ord[T]].less(x)(y)
66+
67+
println(isLess(Nil)(List(1, 2, 3)))
68+
println(isLess(List(List(1)))(List(List(1))))
69+
}

tests/run/cochis.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import Predef.{$conforms => _, _}
2+
3+
trait A {
4+
5+
implicit def id[A] : A => A = x => x // (1)
6+
def trans[A] (x : A) (implicit f : A => A) = f(x)
7+
}
8+
object Test extends A with App {
9+
implicit def succ : Int Int = x x + 1 // (3)
10+
def bad [A] (x : A) : A = trans[A](x) // (4) incoherent de€nition !
11+
val v1 = bad [Int] (3) // (5) evaluates to 3
12+
val v2 = trans [Int] (3) // (6) substituting bad by trans is rejected
13+
assert(v1 == 3)
14+
assert(v2 == 4)
15+
}

0 commit comments

Comments
 (0)