Skip to content

Fix spurious shadowing for higher-order implicits #2739

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -752,10 +752,12 @@ trait Implicits { self: Typer =>
lazy val shadowing =
typed(untpd.Ident(ref.name) withPos pos.toSynthetic, funProto)(
nestedContext.addMode(Mode.ImplicitShadowing).setExploreTyperState)
def refMatches(shadowing: Tree): Boolean =
def refSameAs(shadowing: Tree): Boolean =
ref.symbol == closureBody(shadowing).symbol || {
shadowing match {
case Trees.Select(qual, nme.apply) => refMatches(qual)
case Trees.Select(qual, nme.apply) => refSameAs(qual)
case Trees.Apply(fn, _) => refSameAs(fn)
case Trees.TypeApply(fn, _) => refSameAs(fn)
case _ => false
}
}
Expand All @@ -782,7 +784,7 @@ trait Implicits { self: Typer =>
if (ctx.reporter.hasErrors)
nonMatchingImplicit(ref, ctx.reporter.removeBufferedMessages)
else if (contextual && !ctx.mode.is(Mode.ImplicitShadowing) &&
!shadowing.tpe.isError && !refMatches(shadowing)) {
!shadowing.tpe.isError && !refSameAs(shadowing)) {
implicits.println(i"SHADOWING $ref in ${ref.termSymbol.owner} is shadowed by $shadowing in ${shadowing.symbol.owner}")
shadowedImplicit(ref, methPart(shadowing).tpe)
}
Expand Down
9 changes: 9 additions & 0 deletions tests/pos/ho-implicits.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
object Test2 {

implicit def __1: implicit Int => String = s"implicit: ${implicitly[Int]}"
implicit def __2: Int = 42

def f: implicit String => Int = implicitly[String].length

f: Int
}
69 changes: 69 additions & 0 deletions tests/pos/implicitFuns.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package cm2

case class Person(name: String)
case class Paper(title: String, authors: List[Person], body: String)

class Viewers(val persons: Set[Person])

class ConfManagement(papers: List[Paper], realScore: Map[Paper, Int]) extends App {

private def hasConflict(ps1: Set[Person], ps2: Iterable[Person]) =
ps2.exists(ps1 contains _)

type Viewable[T] = implicit Viewers => T

def vs: Viewable[Viewers] = implicitly

def viewers: Viewable[Set[Person]] = vs.persons

def score: Paper => Viewable[Int] =
paper =>
if hasConflict(viewers, paper.authors) then -100
else realScore(paper)

def viewRankings: Viewable[List[(String, Int)]] =
papers.sortBy(-score(_)).map(p => (p.title, score(p)))

def delegate[T]: (Viewers => T) => Person => Viewable[T] =
query => p => query(new Viewers(viewers + p))
}

object Test extends App {
def bob = Person("Bob")
def peter = Person("Peter")
def p1 = Paper("Bob's paper", List(bob), "")
def p2 = Paper("Peter's paper", List(peter), "")

implicit def __1: Viewers = new Viewers(Set(bob))

val cm = new ConfManagement(List(p1, p2), Map(p1 -> 2, p2 -> 3))

println(cm.viewRankings)
println(cm.score(p1))
println(Orderings.isLess(Nil)(List(1, 2, 3)))
}

object Orderings extends App {

trait Ord[T] { def less: T => T => Boolean }

implicit def __1: Ord[Int] = new Ord[Int] {
def less: Int => Int => Boolean =
x => y => x < y
}

implicit def __2[T]: implicit Ord[T] => Ord[List[T]] = new Ord[List[T]] {
def less: List[T] => List[T] => Boolean =
xs => ys =>
if ys.isEmpty then false
else if xs.isEmpty then true
else if xs.head == ys.head then less(xs.tail)(ys.tail)
else isLess(xs.head)(ys.head)
}

def isLess[T]: T => T => implicit Ord[T] => Boolean =
x => y => implicitly[Ord[T]].less(x)(y)

println(isLess(Nil)(List(1, 2, 3)))
println(isLess(List(List(1)))(List(List(1))))
}
15 changes: 15 additions & 0 deletions tests/run/cochis.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Predef.{$conforms => _, _}

trait A {

implicit def id[A] : A => A = x => x // (1)
def trans[A] (x : A) (implicit f : A => A) = f(x)
}
object Test extends A with App {
implicit def succ : Int ⇒ Int = x ⇒ x + 1 // (3)
def bad [A] (x : A) : A = trans[A](x) // (4) incoherent de€nition !
val v1 = bad [Int] (3) // (5) evaluates to 3
val v2 = trans [Int] (3) // (6) substituting bad by trans is rejected
assert(v1 == 3)
assert(v2 == 4)
}