Skip to content

Fix #5224: Make implicit shadowing conform to the rules in the spec #5227

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 4 commits into from
Oct 26, 2018
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
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/Driver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ class Driver {
case ex: FatalError =>
ctx.error(ex.getMessage) // signals that we should fail compilation.
ctx.reporter
case ex: Throwable =>
println(s"$ex while compiling ${fileNames.mkString(", ")}")
throw ex
}
else ctx.reporter

Expand Down
38 changes: 23 additions & 15 deletions compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import Mode.ImplicitsEnabled
import NameOps._
import NameKinds.LazyImplicitName
import Symbols._
import Denotations._
import Types._
import Decorators._
import Names._
Expand Down Expand Up @@ -907,12 +908,6 @@ trait Implicits { self: Typer =>
/** The expected type for the searched implicit */
lazy val fullProto: Type = implicitProto(pt, identity)

lazy val funProto: Type = fullProto match {
case proto: ViewProto =>
FunProto(untpd.TypedSplice(dummyTreeOfType(proto.argType)) :: Nil, proto.resultType)(self)
case proto => proto
}

/** The expected type where parameters and uninstantiated typevars are replaced by wildcard types */
val wildProto: Type = implicitProto(pt, wildApprox(_))

Expand All @@ -930,18 +925,31 @@ trait Implicits { self: Typer =>
untpd.Apply(untpd.TypedSplice(generated), untpd.TypedSplice(argument) :: Nil),
pt, locked)
val generated1 = adapt(generated, pt, locked)

lazy val shadowing =
typed(untpd.Ident(cand.implicitRef.implicitName) withPos pos.toSynthetic, funProto)(
typedUnadapted(untpd.Ident(cand.implicitRef.implicitName) withPos pos.toSynthetic)(
nestedContext().addMode(Mode.ImplicitShadowing).setExploreTyperState())
def refSameAs(shadowing: Tree): Boolean =
ref.symbol == closureBody(shadowing).symbol || {
shadowing match {
case Trees.Select(qual, nme.apply) => refSameAs(qual)
case Trees.Apply(fn, _) => refSameAs(fn)
case Trees.TypeApply(fn, _) => refSameAs(fn)
case _ => false
}

/** Is candidate reference the same as the `shadowing` reference? (i.e.
* no actual shadowing occured). This is the case if the
* underlying symbol of the shadowing reference is the same as the
* symbol of the candidate reference, or if they have a common type owner.
*
* The second condition (same owner) is needed because the candidate reference
* and the potential shadowing reference are typechecked with different prototypes.
* so might yield different overloaded symbols. E.g. if the candidate reference
* is to an implicit conversion generated from an implicit class, the shadowing
* reference could go to the companion object of that class instead.
*/
def refSameAs(shadowing: Tree): Boolean = {
def symMatches(sym: Symbol): Boolean =
sym == ref.symbol || sym.owner.isType && sym.owner == ref.symbol.owner
def denotMatches(d: Denotation): Boolean = d match {
case d: SingleDenotation => symMatches(d.symbol)
case d => d.hasAltWith(denotMatches(_))
}
denotMatches(closureBody(shadowing).denot)
}

if (ctx.reporter.hasErrors) {
ctx.reporter.removeBufferedMessages
Expand Down
19 changes: 18 additions & 1 deletion tests/neg/implicit-shadowing.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,25 @@ object Test {
def outer(implicit c: C) = {

def f(c: C) = implicitly[C] // error: shadowing
def g(c: Int) = implicitly[C] // ok since type is different
def g(c: Int) = implicitly[C] // error: shadowing (even though type is different)

f(new C)
}

class C1[X]
class C2[X]

def f[T: C1] = {
def g[U: C2] = {
implicitly[C1[T]] // OK: no shadowing for evidence parameters
implicitly[C2[U]]
}
}

def h[T]: implicit C1[T] => Unit = {
def g[U]: implicit C2[U] => Unit = {
implicitly[C1[T]] // OK: no shadowing for evidence parameters
implicitly[C2[U]]
}
}
}
2 changes: 2 additions & 0 deletions tests/run/i5224.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
barInt
bar
20 changes: 20 additions & 0 deletions tests/run/i5224.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
object Test extends App {
class Bar[T]

implicit def barInt: Bar[Int] = {
println("barInt")
new Bar[Int]
}
implicit def bar[T]: Bar[T] = {
println("bar")
new Bar[T]
}

implicitly[Bar[Int]]

locally {
def barInt: Unit = ???

implicitly[Bar[Int]]
}
}