summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala17
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala87
-rw-r--r--test/files/neg/t4270.check4
-rw-r--r--test/files/neg/t4270.scala6
-rw-r--r--test/files/neg/t5376.check11
-rw-r--r--test/files/neg/t5376.scala24
6 files changed, 100 insertions, 49 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index ac3c94c47a..bcf529ecd2 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -100,6 +100,12 @@ trait Contexts { self: Analyzer =>
var outer: Context = _ // The next outer context
var enclClass: Context = _ // The next outer context whose tree is a
// template or package definition
+ @inline final def savingEnclClass[A](c: Context)(a: => A): A = {
+ val saved = enclClass
+ enclClass = c
+ try a finally enclClass = saved
+ }
+
var enclMethod: Context = _ // The next outer context whose tree is a method
var variance: Int = _ // Variance relative to enclosing class
private var _undetparams: List[Symbol] = List() // Undetermined type parameters,
@@ -638,11 +644,12 @@ trait Contexts { self: Analyzer =>
if (owner != nextOuter.owner && owner.isClass && !owner.isPackageClass && !inSelfSuperCall) {
if (!owner.isInitialized) return nextOuter.implicitss
// debuglog("collect member implicits " + owner + ", implicit members = " + owner.thisType.implicitMembers)//DEBUG
- val savedEnclClass = enclClass
- this.enclClass = this
- val res = collectImplicits(owner.thisType.implicitMembers, owner.thisType)
- this.enclClass = savedEnclClass
- res
+ savingEnclClass(this) {
+ // !!! In the body of `class C(implicit a: A) { }`, `implicitss` returns `List(List(a), List(a), List(<predef..)))`
+ // it handled correctly by implicit search, which considers the second `a` to be shadowed, but should be
+ // remedied nonetheless.
+ collectImplicits(owner.thisType.implicitMembers, owner.thisType)
+ }
} else if (scope != nextOuter.scope && !owner.isPackageClass) {
debuglog("collect local implicits " + scope.toList)//DEBUG
collectImplicits(scope.toList, NoPrefix)
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)
}
}
diff --git a/test/files/neg/t4270.check b/test/files/neg/t4270.check
new file mode 100644
index 0000000000..cfe0a93e00
--- /dev/null
+++ b/test/files/neg/t4270.check
@@ -0,0 +1,4 @@
+t4270.scala:5: error: could not find implicit value for parameter e: Int
+ implicitly[Int]
+ ^
+one error found
diff --git a/test/files/neg/t4270.scala b/test/files/neg/t4270.scala
new file mode 100644
index 0000000000..2c7c71d8c2
--- /dev/null
+++ b/test/files/neg/t4270.scala
@@ -0,0 +1,6 @@
+object Test1 {
+ object A { implicit val x: Int = 1 }
+ import A.x
+ def x: Int = 0
+ implicitly[Int]
+}
diff --git a/test/files/neg/t5376.check b/test/files/neg/t5376.check
new file mode 100644
index 0000000000..0376163c35
--- /dev/null
+++ b/test/files/neg/t5376.check
@@ -0,0 +1,11 @@
+t5376.scala:12: error: type mismatch;
+ found : String("a")
+ required: Int
+ "a": Int
+ ^
+t5376.scala:22: error: type mismatch;
+ found : String("a")
+ required: Int
+ "a": Int
+ ^
+two errors found
diff --git a/test/files/neg/t5376.scala b/test/files/neg/t5376.scala
new file mode 100644
index 0000000000..8da3868566
--- /dev/null
+++ b/test/files/neg/t5376.scala
@@ -0,0 +1,24 @@
+object Test {
+ object O1 { implicit def f(s: String): Int = 1 }
+ object O2 { implicit def f(s: String): Int = 2 }
+ object O3 { def f(s: String): Int = 3 }
+
+ // Import two implicits with the same name in the same scope.
+ def m1 = {
+ import O1._
+ import O2._
+
+ // Implicit usage compiles.
+ "a": Int
+ }
+
+ // Import one implict and one non-implicit method with the
+ // same name in the same scope.
+ def m2 = {
+ import O1._
+ import O3._
+
+ // Implicit usage compiles.
+ "a": Int
+ }
+} \ No newline at end of file