Skip to content

Commit

Permalink
Handle import aliases of implicit definitions
Browse files Browse the repository at this point in the history
This makes scala#2405 work again.
  • Loading branch information
odersky authored and smarter committed Dec 3, 2017
1 parent 9e65352 commit 78e036b
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 14 deletions.
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ object Contexts {
def implicits: ContextualImplicits = {
if (implicitsCache == null )
implicitsCache = {
val implicitRefs: List[TermRef] =
val implicitRefs: List[ImplicitDef] =
if (isClassDefContext)
try owner.thisType.implicitMembers
catch {
Expand Down
14 changes: 13 additions & 1 deletion compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1969,7 +1969,16 @@ object Types {
override def eql(that: Type) = this eq that // safe because named types are hash-consed separately
}

abstract case class TermRef(override val prefix: Type, var designator: Designator) extends NamedType with SingletonType {
/** A reference to an implicit definition. This can be either a TermRef or a
* Implicits.RenamedImplicitDef.
*/
trait ImplicitDef {
def implicitName(implicit ctx: Context): TermName
def implicitRef: TermRef
}

abstract case class TermRef(override val prefix: Type, var designator: Designator)
extends NamedType with SingletonType with ImplicitDef {

type ThisType = TermRef
type ThisName = TermName
Expand All @@ -1987,6 +1996,9 @@ object Types {

def altsWith(p: Symbol => Boolean)(implicit ctx: Context): List[TermRef] =
denot.altsWith(p).map(withDenot(_))

def implicitName(implicit ctx: Context): TermName = name
def implicitRef = this
}

abstract case class TypeRef(override val prefix: Type, var designator: Designator) extends NamedType {
Expand Down
29 changes: 19 additions & 10 deletions compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,20 @@ object Implicits {
*/
val DelayedImplicit = new Property.Key[TermRef]

/** An implicit definition `implicitRef` that is visible under a different name, `alias`.
* Gets generated if an implicit ref is imported via a renaming import.
*/
class RenamedImplicitDef(val implicitRef: TermRef, val alias: TermName) extends ImplicitDef {
def implicitName(implicit ctx: Context): TermName = alias
}

/** An eligible implicit candidate, consisting of an implicit reference and a nesting level */
case class Candidate(ref: TermRef, level: Int)
case class Candidate(implicitDef: ImplicitDef, level: Int) {
def ref: TermRef = implicitDef.implicitRef
}

/** A common base class of contextual implicits and of-type implicits which
* represents a set of implicit references.
* represents a set of references to implicit definitions.
*/
abstract class ImplicitRefs(initctx: Context) {
implicit val ctx: Context =
Expand All @@ -59,7 +68,7 @@ object Implicits {
def level: Int = 0

/** The implicit references */
def refs: List[TermRef]
def refs: List[ImplicitDef]

/** Return those references in `refs` that are compatible with type `pt`. */
protected def filterMatching(pt: Type)(implicit ctx: Context): List[Candidate] = track("filterMatching") {
Expand Down Expand Up @@ -138,7 +147,7 @@ object Implicits {
else {
val nestedCtx = ctx.fresh.addMode(Mode.TypevarsMissContext)
refs
.filter(ref => nestedCtx.typerState.test(refMatches(ref)(nestedCtx)))
.filter(ref => nestedCtx.typerState.test(refMatches(ref.implicitRef)(nestedCtx)))
.map(Candidate(_, level))
}
}
Expand All @@ -150,7 +159,7 @@ object Implicits {
*/
class OfTypeImplicits(tp: Type, val companionRefs: TermRefSet)(initctx: Context) extends ImplicitRefs(initctx) {
assert(initctx.typer != null)
lazy val refs: List[TermRef] = {
lazy val refs: List[ImplicitDef] = {
val buf = new mutable.ListBuffer[TermRef]
for (companion <- companionRefs) buf ++= companion.implicitMembers
buf.toList
Expand All @@ -176,7 +185,7 @@ object Implicits {
* name, b, whereas the name of the symbol is the original name, a.
* @param outerCtx the next outer context that makes visible further implicits
*/
class ContextualImplicits(val refs: List[TermRef], val outerImplicits: ContextualImplicits)(initctx: Context) extends ImplicitRefs(initctx) {
class ContextualImplicits(val refs: List[ImplicitDef], val outerImplicits: ContextualImplicits)(initctx: Context) extends ImplicitRefs(initctx) {
private val eligibleCache = new mutable.AnyRefMap[Type, List[Candidate]]

/** The level increases if current context has a different owner or scope than
Expand All @@ -188,7 +197,7 @@ object Implicits {
else if (ctx.scala2Mode ||
(ctx.owner eq outerImplicits.ctx.owner) &&
(ctx.scope eq outerImplicits.ctx.scope) &&
!refs.head.name.is(LazyImplicitName)) outerImplicits.level
!refs.head.implicitName.is(LazyImplicitName)) outerImplicits.level
else outerImplicits.level + 1

/** Is this the outermost implicits? This is the case if it either the implicits
Expand Down Expand Up @@ -231,8 +240,8 @@ object Implicits {
val ownEligible = filterMatching(tp)
if (isOuterMost) ownEligible
else ownEligible ::: {
val shadowed = ownEligible.map(_.ref.name).toSet
outerImplicits.eligible(tp).filterNot(cand => shadowed.contains(cand.ref.name))
val shadowed = ownEligible.map(_.ref.implicitName).toSet
outerImplicits.eligible(tp).filterNot(cand => shadowed.contains(cand.ref.implicitName))
}
}

Expand Down Expand Up @@ -818,7 +827,7 @@ trait Implicits { self: Typer =>
pt)
val generated1 = adapt(generated, pt)
lazy val shadowing =
typed(untpd.Ident(ref.name) withPos pos.toSynthetic, funProto)(
typed(untpd.Ident(cand.implicitDef.implicitName) withPos pos.toSynthetic, funProto)(
nestedContext().addMode(Mode.ImplicitShadowing).setExploreTyperState())
def refSameAs(shadowing: Tree): Boolean =
ref.symbol == closureBody(shadowing).symbol || {
Expand Down
10 changes: 8 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/ImportInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import printing.{Printer, Showable}
import util.SimpleIdentityMap
import Symbols._, Names._, Denotations._, Types._, Contexts._, StdNames._, Flags._
import Decorators.StringInterpolators
import Implicits.RenamedImplicitDef

object ImportInfo {
/** The import info for a root import from given symbol `sym` */
Expand Down Expand Up @@ -92,7 +93,7 @@ class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree],
}

/** The implicit references imported by this import clause */
def importedImplicits(implicit ctx: Context): List[TermRef] = {
def importedImplicits(implicit ctx: Context): List[ImplicitDef] = {
val pre = site
if (isWildcardImport) {
val refs = pre.implicitMembers
Expand All @@ -102,7 +103,12 @@ class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree],
for {
renamed <- reverseMapping.keys
denot <- pre.member(reverseMapping(renamed)).altsWith(_ is Implicit)
} yield TermRef(pre, renamed, denot)
} yield {
val original = reverseMapping(renamed)
val ref = TermRef(pre, original, denot)
if (renamed == original) ref
else new RenamedImplicitDef(ref, renamed)
}
}

/** The root import symbol hidden by this symbol, or NoSymbol if no such symbol is hidden.
Expand Down
23 changes: 23 additions & 0 deletions tests/pos/t2405.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
object A { implicit val x: Int = 1 }

// Problem as stated in the ticket.
object Test1 {
import A.{x => y}
implicitly[Int]
}

// Testing for the absense of shadowing #1.
object Test2 {
import A.{x => y}
val x = 2
implicitly[Int]
}

// Testing for the absense of shadowing #2.
object Test3 {
{
import A.{x => y}
def x: Int = 0
implicitly[Int]
}
}

0 comments on commit 78e036b

Please sign in to comment.