summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2012-06-04 19:59:51 +0200
committerJason Zaugg <jzaugg@gmail.com>2012-06-10 09:38:32 +0200
commit9129cfe9117e41d44cda30222ffef22b70767cfb (patch)
tree0c1681dfd15ff66a19b895a0f36707750d4b870b /src/compiler/scala/tools/nsc/typechecker/Implicits.scala
parent0dea3d5a05d838c9ad710cbcb14fbc6d83035851 (diff)
downloadscala-9129cfe9117e41d44cda30222ffef22b70767cfb.tar.gz
scala-9129cfe9117e41d44cda30222ffef22b70767cfb.tar.bz2
scala-9129cfe9117e41d44cda30222ffef22b70767cfb.zip
SI-4270 Disqualify in scope implicits that are shadowed.
If an expression wouldn't type check explicitly, it shouldn't be allowed implicitly. Employs typedIdent, which already does this sort of thing rather well, instead of continuing to reimplement it in Implicits. Remove check for non-implicit synonym, which is subsumed by typing an Ident. Workaround Scaladoc oddity, by using an attributed select when the context is deficient.
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker/Implicits.scala')
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala87
1 files changed, 43 insertions, 44 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index aa63f3ec31..97a229a18d 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -390,9 +390,10 @@ trait Implicits {
* Detect infinite search trees for implicits.
*
* @param info The given implicit info describing the implicit definition
+ * @param isLocal Is the implicit in the local scope of the call site?
* @pre `info.tpe` does not contain an error
*/
- private def typedImplicit(info: ImplicitInfo, ptChecked: Boolean): SearchResult = {
+ private def typedImplicit(info: ImplicitInfo, ptChecked: Boolean, isLocal: Boolean): SearchResult = {
(context.openImplicits find { case (tp, tree1) => tree1.symbol == tree.symbol && dominates(pt, tp)}) match {
case Some(pending) =>
//println("Pending implicit "+pending+" dominates "+pt+"/"+undetParams) //@MDEBUG
@@ -401,7 +402,7 @@ trait Implicits {
try {
context.openImplicits = (pt, tree) :: context.openImplicits
// println(" "*context.openImplicits.length+"typed implicit "+info+" for "+pt) //@MDEBUG
- typedImplicit0(info, ptChecked)
+ typedImplicit0(info, ptChecked, isLocal)
} catch {
case ex: DivergentImplicit =>
//println("DivergentImplicit for pt:"+ pt +", open implicits:"+context.openImplicits) //@MDEBUG
@@ -534,7 +535,7 @@ trait Implicits {
case _ => false
}
- private def typedImplicit0(info: ImplicitInfo, ptChecked: Boolean): SearchResult = {
+ private def typedImplicit0(info: ImplicitInfo, ptChecked: Boolean, isLocal: Boolean): SearchResult = {
incCounter(plausiblyCompatibleImplicits)
printTyping (
ptBlock("typedImplicit0",
@@ -549,17 +550,24 @@ trait Implicits {
)
if (ptChecked || matchesPt(info))
- typedImplicit1(info)
+ typedImplicit1(info, isLocal)
else
SearchFailure
}
- private def typedImplicit1(info: ImplicitInfo): SearchResult = {
+ private def typedImplicit1(info: ImplicitInfo, isLocal: Boolean): SearchResult = {
incCounter(matchingImplicits)
val itree = atPos(pos.focus) {
- if (info.pre == NoPrefix) Ident(info.name)
- else {
+ // workaround for deficient context provided by ModelFactoryImplicitSupport#makeImplicitConstraints
+ val isScalaDoc = context.tree == EmptyTree
+
+ if (isLocal && !isScalaDoc) {
+ // SI-4270 SI-5376 Always use an unattributed Ident for implicits in the local scope,
+ // rather than an attributed Select, to detect shadowing.
+ Ident(info.name)
+ } else {
+ assert(info.pre != NoPrefix, info)
// SI-2405 Not info.name, which might be an aliased import
val implicitMemberName = info.sym.name
Select(gen.mkAttributedQualifier(info.pre), implicitMemberName)
@@ -607,8 +615,8 @@ trait Implicits {
if (context.hasErrors)
fail("hasMatchingSymbol reported threw error(s)")
- else if (!hasMatchingSymbol(itree1))
- fail("candidate implicit %s is shadowed by other implicit %s".format(
+ else if (isLocal && !hasMatchingSymbol(itree1))
+ fail("candidate implicit %s is shadowed by %s".format(
info.sym.fullLocationString, itree1.symbol.fullLocationString))
else {
val tvars = undetParams map freshVar
@@ -683,17 +691,6 @@ trait Implicits {
}
}
- // #3453: in addition to the implicit symbols that may shadow the implicit with
- // name `name`, this method tests whether there's a non-implicit symbol with name
- // `name` in scope. Inspired by logic in typedIdent.
- private def nonImplicitSynonymInScope(name: Name) = {
- // the implicit ones are handled by the `shadowed` set above
- context.scope.lookupEntry(name) match {
- case x: ScopeEntry => reallyExists(x.sym) && !x.sym.isImplicit
- case _ => false
- }
- }
-
/** Should implicit definition symbol `sym` be considered for applicability testing?
* This is the case if one of the following holds:
* - the symbol's type is initialized
@@ -737,14 +734,15 @@ trait Implicits {
/** Prune ImplicitInfos down to either all the eligible ones or the best one.
*
* @param iss list of list of infos
- * @param shadowed set in which to record names that are shadowed by implicit infos
- * If it is null, no shadowing.
+ * @param isLocal if true, `iss` represents in-scope implicits, which must respect the normal rules of
+ * shadowing. The head of the list `iss` must represent implicits from the closest
+ * enclosing scope, and so on.
*/
- class ImplicitComputation(iss: Infoss, shadowed: util.HashSet[Name]) {
+ class ImplicitComputation(iss: Infoss, isLocal: Boolean) {
+ private val shadowed = util.HashSet[Name](512)
private var best: SearchResult = SearchFailure
private def isShadowed(name: Name) = (
- (shadowed != null)
- && (shadowed(name) || nonImplicitSynonymInScope(name))
+ isLocal && shadowed(name)
)
private def isIneligible(info: ImplicitInfo) = (
info.isCyclicOrErroneous
@@ -788,9 +786,7 @@ trait Implicits {
val eligible = {
val matches = iss flatMap { is =>
val result = is filter (info => checkValid(info.sym) && survives(info))
- if (shadowed ne null)
- shadowed addEntries (is map (_.name))
-
+ if (isLocal) shadowed addEntries (is map (_.name))
result
}
@@ -812,7 +808,7 @@ trait Implicits {
case Nil => acc
case i :: is =>
def tryImplicitInfo(i: ImplicitInfo) =
- try typedImplicit(i, true)
+ try typedImplicit(i, ptChecked = true, isLocal)
catch divergenceHandler
tryImplicitInfo(i) match {
@@ -842,7 +838,7 @@ trait Implicits {
/** Returns all eligible ImplicitInfos and their SearchResults in a map.
*/
- def findAll() = mapFrom(eligible)(typedImplicit(_, false))
+ def findAll() = mapFrom(eligible)(typedImplicit(_, ptChecked = false, isLocal))
/** Returns the SearchResult of the best match.
*/
@@ -891,7 +887,7 @@ trait Implicits {
*/
def applicableInfos(iss: Infoss, isLocal: Boolean): Map[ImplicitInfo, SearchResult] = {
val start = startCounter(subtypeAppInfos)
- val computation = new ImplicitComputation(iss, if (isLocal) util.HashSet[Name](512) else null) { }
+ val computation = new ImplicitComputation(iss, isLocal) { }
val applicable = computation.findAll()
stopCounter(subtypeAppInfos, start)
@@ -909,7 +905,7 @@ trait Implicits {
*/
def searchImplicit(implicitInfoss: Infoss, isLocal: Boolean): SearchResult =
if (implicitInfoss.forall(_.isEmpty)) SearchFailure
- else new ImplicitComputation(implicitInfoss, if (isLocal) util.HashSet[Name](128) else null) findBest()
+ else new ImplicitComputation(implicitInfoss, isLocal) findBest()
/** Produce an implicict info map, i.e. a map from the class symbols C of all parts of this type to
* the implicit infos in the companion objects of these class symbols C.
@@ -1397,19 +1393,22 @@ trait Implicits {
def allImplicitsPoly(tvars: List[TypeVar]): List[(SearchResult, List[TypeConstraint])] = {
def resetTVars() = tvars foreach { _.constr = new TypeConstraint }
- def eligibleInfos(iss: Infoss, isLocal: Boolean) = new ImplicitComputation(iss, if (isLocal) util.HashSet[Name](512) else null).eligible
- val allEligibleInfos = (eligibleInfos(context.implicitss, true) ++ eligibleInfos(implicitsOfExpectedType, false)).toList
-
- allEligibleInfos flatMap { ii =>
- // each ImplicitInfo contributes a distinct set of constraints (generated indirectly by typedImplicit)
- // thus, start each type var off with a fresh for every typedImplicit
- resetTVars()
- // any previous errors should not affect us now
- context.flushBuffer()
- val res = typedImplicit(ii, false)
- if (res.tree ne EmptyTree) List((res, tvars map (_.constr)))
- else Nil
+ def eligibleInfos(iss: Infoss, isLocal: Boolean) = {
+ val eligible = new ImplicitComputation(iss, isLocal).eligible
+ eligible.toList.flatMap {
+ (ii: ImplicitInfo) =>
+ // each ImplicitInfo contributes a distinct set of constraints (generated indirectly by typedImplicit)
+ // thus, start each type var off with a fresh for every typedImplicit
+ resetTVars()
+ // any previous errors should not affect us now
+ context.flushBuffer()
+
+ val res = typedImplicit(ii, ptChecked = false, isLocal)
+ if (res.tree ne EmptyTree) List((res, tvars map (_.constr)))
+ else Nil
+ }
}
+ eligibleInfos(context.implicitss, isLocal = true) ++ eligibleInfos(implicitsOfExpectedType, isLocal = false)
}
}