diff options
author | Paul Phillips <paulp@improving.org> | 2013-04-30 05:18:20 -0700 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2013-04-30 05:18:20 -0700 |
commit | bf0db36d8bbb0ac7faa0652b98458cfebca8e78a (patch) | |
tree | d3746ffa180541b3b7c6a60c5b1cd3bf272a7c37 | |
parent | fc14edde4cd0dde064b78b0bde35adbfd561597d (diff) | |
parent | 71581425746e9d3239998569d6f85c710efbf17b (diff) | |
download | scala-bf0db36d8bbb0ac7faa0652b98458cfebca8e78a.tar.gz scala-bf0db36d8bbb0ac7faa0652b98458cfebca8e78a.tar.bz2 scala-bf0db36d8bbb0ac7faa0652b98458cfebca8e78a.zip |
Merge pull request #2428 from hubertp/issue/7291
SI-7291: Don't throw exceptions while encountering diverging expansion.
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala | 72 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Contexts.scala | 8 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Implicits.scala | 82 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 60 | ||||
-rw-r--r-- | src/compiler/scala/tools/reflect/ToolBoxFactory.scala | 17 | ||||
-rw-r--r-- | src/interactive/scala/tools/nsc/interactive/Global.scala | 5 | ||||
-rw-r--r-- | test/files/neg/t696.check | 10 | ||||
-rw-r--r-- | test/files/neg/t696.scala | 7 | ||||
-rw-r--r-- | test/files/run/t7291.check | 2 | ||||
-rw-r--r-- | test/files/run/t7291.scala | 19 |
10 files changed, 165 insertions, 117 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 89fc55bc2c..a0660ce71d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -21,47 +21,65 @@ trait ContextErrors { import global._ import definitions._ - object ErrorKinds extends Enumeration { - type ErrorKind = Value - val Normal, Access, Ambiguous, Divergent = Value - } - - import ErrorKinds.ErrorKind - - trait AbsTypeError extends Throwable { + abstract class AbsTypeError extends Throwable { def errPos: Position def errMsg: String - def kind: ErrorKind + override def toString() = "[Type error at:" + errPos + "] " + errMsg } - case class NormalTypeError(underlyingTree: Tree, errMsg: String, kind: ErrorKind = ErrorKinds.Normal) - extends AbsTypeError { - - def errPos:Position = underlyingTree.pos - override def toString() = "[Type error at:" + underlyingTree.pos + "] " + errMsg + abstract class TreeTypeError extends AbsTypeError { + def underlyingTree: Tree + def errPos = underlyingTree.pos } - case class SymbolTypeError(underlyingSym: Symbol, errMsg: String, kind: ErrorKind = ErrorKinds.Normal) + case class NormalTypeError(underlyingTree: Tree, errMsg: String) + extends TreeTypeError + + case class AccessTypeError(underlyingTree: Tree, errMsg: String) + extends TreeTypeError + + case class AmbiguousTypeError(errPos: Position, errMsg: String) + extends AbsTypeError + + case class SymbolTypeError(underlyingSym: Symbol, errMsg: String) extends AbsTypeError { def errPos = underlyingSym.pos } - case class TypeErrorWrapper(ex: TypeError, kind: ErrorKind = ErrorKinds.Normal) + case class TypeErrorWrapper(ex: TypeError) extends AbsTypeError { def errMsg = ex.msg def errPos = ex.pos } - case class TypeErrorWithUnderlyingTree(tree: Tree, ex: TypeError, kind: ErrorKind = ErrorKinds.Normal) + case class TypeErrorWithUnderlyingTree(tree: Tree, ex: TypeError) extends AbsTypeError { def errMsg = ex.msg def errPos = tree.pos } - case class AmbiguousTypeError(underlyingTree: Tree, errPos: Position, errMsg: String, kind: ErrorKind = ErrorKinds.Ambiguous) extends AbsTypeError + // Unlike other type errors diverging implicit expansion + // will be re-issued explicitly on failed implicit argument search. + // This is because we want to: + // 1) provide better error message than just "implicit not found" + // 2) provide the type of the implicit parameter for which we got diverging expansion + // (pt at the point of divergence gives less information to the user) + // Note: it is safe to delay error message generation in this case + // becasue we don't modify implicits' infos. + case class DivergentImplicitTypeError(underlyingTree: Tree, pt0: Type, sym: Symbol) + extends TreeTypeError { + def errMsg: String = errMsgForPt(pt0) + def withPt(pt: Type): AbsTypeError = this.copy(pt0 = pt) + private def errMsgForPt(pt: Type) = + s"diverging implicit expansion for type ${pt}\nstarting with ${sym.fullLocationString}" + } + + case class AmbiguousImplicitTypeError(underlyingTree: Tree, errMsg: String) + extends TreeTypeError - case class PosAndMsgTypeError(errPos: Position, errMsg: String, kind: ErrorKind = ErrorKinds.Normal) extends AbsTypeError + case class PosAndMsgTypeError(errPos: Position, errMsg: String) + extends AbsTypeError object ErrorUtils { def issueNormalTypeError(tree: Tree, msg: String)(implicit context: Context) { @@ -72,10 +90,6 @@ trait ContextErrors { issueTypeError(SymbolTypeError(sym, msg)) } - def issueDivergentImplicitsError(tree: Tree, msg: String)(implicit context: Context) { - issueTypeError(NormalTypeError(tree, msg, ErrorKinds.Divergent)) - } - def issueAmbiguousTypeError(pre: Type, sym1: Symbol, sym2: Symbol, err: AmbiguousTypeError)(implicit context: Context) { context.issueAmbiguousError(pre, sym1, sym2, err) } @@ -830,7 +844,7 @@ trait ContextErrors { underlyingSymbol(sym).fullLocationString + " cannot be accessed in " + location + explanation } - NormalTypeError(tree, errMsg, ErrorKinds.Access) + AccessTypeError(tree, errMsg) } def NoMethodInstanceError(fn: Tree, args: List[Tree], msg: String) = @@ -875,7 +889,7 @@ trait ContextErrors { "argument types " + argtpes.mkString("(", ",", ")") + (if (pt == WildcardType) "" else " and expected result type " + pt) val (pos, msg) = ambiguousErrorMsgPos(tree.pos, pre, best, firstCompeting, msg0) - issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(tree, pos, msg)) + issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(pos, msg)) setErrorOnLastTry(lastTry, tree) } else setError(tree) // do not even try further attempts because they should all fail // even if this is not the last attempt (because of the SO's possibility on the horizon) @@ -889,7 +903,7 @@ trait ContextErrors { def AmbiguousExprAlternativeError(tree: Tree, pre: Type, best: Symbol, firstCompeting: Symbol, pt: Type, lastTry: Boolean) = { val (pos, msg) = ambiguousErrorMsgPos(tree.pos, pre, best, firstCompeting, "expected type " + pt) - issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(tree, pos, msg)) + issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(pos, msg)) setErrorOnLastTry(lastTry, tree) } @@ -1174,7 +1188,7 @@ trait ContextErrors { if (explanation == "") "" else "\n" + explanation ) } - context.issueAmbiguousError(AmbiguousTypeError(tree, tree.pos, + context.issueAmbiguousError(AmbiguousImplicitTypeError(tree, if (isView) viewMsg else s"ambiguous implicit values:\n${coreMsg}match expected type $pt") ) @@ -1182,9 +1196,7 @@ trait ContextErrors { } def DivergingImplicitExpansionError(tree: Tree, pt: Type, sym: Symbol)(implicit context0: Context) = - issueDivergentImplicitsError(tree, - "diverging implicit expansion for type "+pt+"\nstarting with "+ - sym.fullLocationString) + issueTypeError(DivergentImplicitTypeError(tree, pt, sym)) } object NamesDefaultsErrorsGen { diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index c26ad3e61d..e3bb595bd7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -1189,12 +1189,12 @@ trait Contexts { self: Analyzer => errorBuffer.clear() this } - def clearErrors(kind: ErrorKinds.ErrorKind): this.type = { - errorBuffer.retain(_.kind != kind) + def clearErrors(removeF: PartialFunction[AbsTypeError, Boolean]): this.type = { + errorBuffer.retain(!PartialFunction.cond(_)(removeF)) this } - def retainErrors(kind: ErrorKinds.ErrorKind): this.type = { - errorBuffer.retain(_.kind == kind) + def retainErrors(leaveF: PartialFunction[AbsTypeError, Boolean]): this.type = { + errorBuffer.retain(PartialFunction.cond(_)(leaveF)) this } def clearAllWarnings(): this.type = { diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index b0c8baae20..875aa5a9d3 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -80,8 +80,11 @@ trait Implicits { printTyping("typing implicit: %s %s".format(tree, context.undetparamsString)) val implicitSearchContext = context.makeImplicit(reportAmbiguous) val result = new ImplicitSearch(tree, pt, isView, implicitSearchContext, pos).bestImplicit - if (saveAmbiguousDivergent && implicitSearchContext.hasErrors) { - context.updateBuffer(implicitSearchContext.reportBuffer.errors.filter(err => err.kind == ErrorKinds.Ambiguous || err.kind == ErrorKinds.Divergent)) + if (result.isFailure && saveAmbiguousDivergent && implicitSearchContext.hasErrors) { + context.updateBuffer(implicitSearchContext.reportBuffer.errors.collect { + case dte: DivergentImplicitTypeError => dte + case ate: AmbiguousImplicitTypeError => ate + }) debuglog("update buffer: " + implicitSearchContext.reportBuffer.errors) } printInference("[infer implicit] inferred " + result) @@ -152,6 +155,7 @@ trait Implicits { def isFailure = false def isAmbiguousFailure = false + def isDivergent = false final def isSuccess = !isFailure } @@ -159,6 +163,11 @@ trait Implicits { override def isFailure = true } + lazy val DivergentSearchFailure = new SearchResult(EmptyTree, EmptyTreeTypeSubstituter) { + override def isFailure = true + override def isDivergent = true + } + lazy val AmbiguousSearchFailure = new SearchResult(EmptyTree, EmptyTreeTypeSubstituter) { override def isFailure = true override def isAmbiguousFailure = true @@ -397,22 +406,18 @@ trait Implicits { (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 - throw DivergentImplicit + DivergentSearchFailure case None => try { context.openImplicits = (pt, tree) :: context.openImplicits // println(" "*context.openImplicits.length+"typed implicit "+info+" for "+pt) //@MDEBUG - typedImplicit0(info, ptChecked, isLocal) - } catch { - case ex: DivergentImplicit => + val result = typedImplicit0(info, ptChecked, isLocal) + if (result.isDivergent) { //println("DivergentImplicit for pt:"+ pt +", open implicits:"+context.openImplicits) //@MDEBUG - if (context.openImplicits.tail.isEmpty) { - if (!(pt.isErroneous)) - DivergingImplicitExpansionError(tree, pt, info.sym)(context) - SearchFailure - } else { - throw DivergentImplicit - } + if (context.openImplicits.tail.isEmpty && !pt.isErroneous) + DivergingImplicitExpansionError(tree, pt, info.sym)(context) + } + result } finally { context.openImplicits = context.openImplicits.tail } @@ -588,7 +593,7 @@ trait Implicits { case Some(err) => log("implicit adapt failed: " + err.errMsg) return fail(err.errMsg) - case None => + case None => } if (Statistics.canEnable) Statistics.incCounter(typedImplicits) @@ -637,7 +642,7 @@ trait Implicits { context.firstError match { case Some(err) => return fail("type parameters weren't correctly instantiated outside of the implicit tree: " + err.errMsg) - case None => + case None => } // filter out failures from type inference, don't want to remove them from undetParams! @@ -676,7 +681,7 @@ trait Implicits { context.firstError match { case Some(err) => fail("typing TypeApply reported errors for the implicit tree: " + err.errMsg) - case None => + case None => val result = new SearchResult(itree2, subst) if (Statistics.canEnable) Statistics.incCounter(foundImplicits) printInference("[success] found %s for pt %s".format(result, ptInstantiated)) @@ -789,16 +794,21 @@ trait Implicits { /** Preventing a divergent implicit from terminating implicit search, * so that if there is a best candidate it can still be selected. */ - private var divergence = false - private val divergenceHandler: PartialFunction[Throwable, SearchResult] = { - var remaining = 1; - { case x: DivergentImplicit if remaining > 0 => - remaining -= 1 - divergence = true - log("discarding divergent implicit during implicit search") + object DivergentImplicitRecovery { + // symbol of the implicit that caused the divergence. + // Initially null, will be saved on first diverging expansion. + private var implicitSym: Symbol = _ + private var countdown: Int = 1 + + def sym: Symbol = implicitSym + def apply(search: SearchResult, i: ImplicitInfo): SearchResult = + if (search.isDivergent && countdown > 0) { + countdown -= 1 + implicitSym = i.sym + log("discarding divergent implicit ${implicitSym} during implicit search") SearchFailure - } - } + } else search + } /** Sorted list of eligible implicits. */ @@ -826,15 +836,15 @@ trait Implicits { @tailrec private def rankImplicits(pending: Infos, acc: Infos): Infos = pending match { case Nil => acc case i :: is => - def tryImplicitInfo(i: ImplicitInfo) = - try typedImplicit(i, ptChecked = true, isLocal) - catch divergenceHandler - - tryImplicitInfo(i) match { + DivergentImplicitRecovery(typedImplicit(i, ptChecked = true, isLocal), i) match { + case sr if sr.isDivergent => + Nil case sr if sr.isFailure => // We don't want errors that occur during checking implicit info // to influence the check of further infos. - context.reportBuffer.retainErrors(ErrorKinds.Divergent) + context.reportBuffer.retainErrors { + case err: DivergentImplicitTypeError => true + } rankImplicits(is, acc) case newBest => best = newBest @@ -882,10 +892,9 @@ trait Implicits { /* If there is no winner, and we witnessed and caught divergence, * now we can throw it for the error message. */ - if (divergence) - throw DivergentImplicit - - if (invalidImplicits.nonEmpty) + if (DivergentImplicitRecovery.sym != null) { + DivergingImplicitExpansionError(tree, pt, DivergentImplicitRecovery.sym)(context) + } else if (invalidImplicits.nonEmpty) setAddendum(pos, () => "\n Note: implicit "+invalidImplicits.head+" is not applicable here"+ " because it comes after the application point and it lacks an explicit result type") @@ -1438,6 +1447,3 @@ object ImplicitsStats { val implicitCacheAccs = Statistics.newCounter ("implicit cache accesses", "typer") val implicitCacheHits = Statistics.newSubCounter("implicit cache hits", implicitCacheAccs) } - -class DivergentImplicit extends Exception -object DivergentImplicit extends DivergentImplicit diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 95a9cea066..4b7260c798 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -150,15 +150,19 @@ trait Typers extends Adaptations with Tags { } else { mkArg = gen.mkNamedArg // don't pass the default argument (if any) here, but start emitting named arguments for the following args if (!param.hasDefault && !paramFailed) { - context.reportBuffer.errors.find(_.kind == ErrorKinds.Divergent) match { - case Some(divergentImplicit) => + context.reportBuffer.errors.collectFirst { + case dte: DivergentImplicitTypeError => dte + } match { + case Some(divergent) => // DivergentImplicit error has higher priority than "no implicit found" // no need to issue the problem again if we are still in silent mode if (context.reportErrors) { - context.issue(divergentImplicit) - context.reportBuffer.clearErrors(ErrorKinds.Divergent) + context.issue(divergent.withPt(paramTp)) + context.reportBuffer.clearErrors { + case dte: DivergentImplicitTypeError => true + } } - case None => + case _ => NoImplicitFoundError(fun, param) } paramFailed = true @@ -1627,22 +1631,27 @@ trait Typers extends Adaptations with Tags { /** Makes sure that the first type tree in the list of parent types is always a class. * If the first parent is a trait, prepend its supertype to the list until it's a class. -*/ - private def normalizeFirstParent(parents: List[Tree]): List[Tree] = parents match { - case first :: rest if treeInfo.isTraitRef(first) => - def explode(supertpt: Tree, acc: List[Tree]): List[Tree] = { - if (treeInfo.isTraitRef(supertpt)) { - val supertpt1 = typedType(supertpt) - if (!supertpt1.isErrorTyped) { - val supersupertpt = TypeTree(supertpt1.tpe.firstParent) setPos supertpt.pos.focus - return explode(supersupertpt, supertpt1 :: acc) - } - } - if (supertpt.tpe.typeSymbol == AnyClass) supertpt setType AnyRefClass.tpe - supertpt :: acc - } - explode(first, Nil) ++ rest - case _ => parents + */ + private def normalizeFirstParent(parents: List[Tree]): List[Tree] = { + @annotation.tailrec + def explode0(parents: List[Tree]): List[Tree] = { + val supertpt :: rest = parents // parents is always non-empty here - it only grows + if (supertpt.tpe.typeSymbol == AnyClass) { + supertpt setType AnyRefTpe + parents + } else if (treeInfo isTraitRef supertpt) { + val supertpt1 = typedType(supertpt) + def supersuper = TypeTree(supertpt1.tpe.firstParent) setPos supertpt.pos.focus + if (supertpt1.isErrorTyped) rest + else explode0(supersuper :: supertpt1 :: rest) + } else parents + } + + def explode(parents: List[Tree]) = + if (treeInfo isTraitRef parents.head) explode0(parents) + else parents + + if (parents.isEmpty) Nil else explode(parents) } /** Certain parents are added in the parser before it is known whether @@ -4683,12 +4692,11 @@ trait Typers extends Adaptations with Tags { case SelectFromTypeTree(_, _) => treeCopy.SelectFromTypeTree(tree, qual, name) } val (result, accessibleError) = silent(_.makeAccessible(tree1, sym, qual.tpe, qual)) match { + case SilentTypeError(err: AccessTypeError) => + (tree1, Some(err)) case SilentTypeError(err) => - if (err.kind != ErrorKinds.Access) { - context issue err - return setError(tree) - } - else (tree1, Some(err)) + context issue err + return setError(tree) case SilentResultValue(treeAndPre) => (stabilize(treeAndPre._1, treeAndPre._2, mode, pt), None) } diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index 66dda2b530..6fb8b4ea37 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -181,16 +181,15 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => (currentTyper, tree) => { trace("inferring implicit %s (macros = %s): ".format(if (isView) "view" else "value", !withMacrosDisabled))(showAttributed(pt, true, true, settings.Yshowsymkinds.value)) val context = currentTyper.context - analyzer.inferImplicit(tree, pt, reportAmbiguous = true, isView = isView, context = context, saveAmbiguousDivergent = !silent, pos = pos) match { - case failure if failure.tree.isEmpty => - trace("implicit search has failed. to find out the reason, turn on -Xlog-implicits: ")(failure.tree) - context.firstError foreach { err => - throw ToolBoxError("reflective implicit search has failed: %s".format(err.errMsg)) - } - EmptyTree - case success => - success.tree + val result = analyzer.inferImplicit(tree, pt, reportAmbiguous = true, isView = isView, context = context, saveAmbiguousDivergent = !silent, pos = pos) + if (result.isFailure) { + // @H: what's the point of tracing an empty tree? + trace("implicit search has failed. to find out the reason, turn on -Xlog-implicits: ")(result.tree) + context.firstError foreach { err => + throw ToolBoxError("reflective implicit search has failed: %s".format(err.errMsg)) + } } + result.tree }) def compile(expr0: Tree): () => Any = { diff --git a/src/interactive/scala/tools/nsc/interactive/Global.scala b/src/interactive/scala/tools/nsc/interactive/Global.scala index 43b8bd2738..792ca6efa6 100644 --- a/src/interactive/scala/tools/nsc/interactive/Global.scala +++ b/src/interactive/scala/tools/nsc/interactive/Global.scala @@ -15,7 +15,7 @@ import scala.reflect.internal.util.{ SourceFile, BatchSourceFile, Position, NoPo import scala.tools.nsc.reporters._ import scala.tools.nsc.symtab._ import scala.tools.nsc.doc.ScaladocAnalyzer -import scala.tools.nsc.typechecker.{ Analyzer, DivergentImplicit } +import scala.tools.nsc.typechecker.Analyzer import symtab.Flags.{ACCESSOR, PARAMACCESSOR} import scala.annotation.{ elidable, tailrec } import scala.language.implicitConversions @@ -1210,9 +1210,6 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") case ex: TypeError => debugLog("type error caught: "+ex) alt - case ex: DivergentImplicit => - debugLog("divergent implicit caught: "+ex) - alt } } diff --git a/test/files/neg/t696.check b/test/files/neg/t696.check index ac26a864a5..b7bc5cdf98 100644 --- a/test/files/neg/t696.check +++ b/test/files/neg/t696.check @@ -1,5 +1,9 @@ -t696.scala:4: error: diverging implicit expansion for type TypeUtil0.Type[Any] +t696.scala:5: error: diverging implicit expansion for type TypeUtil0.Type[Any] starting with method WithType in object TypeUtil0 - as[Any](null); + as[Any](null) ^ -one error found +t696.scala:6: error: diverging implicit expansion for type TypeUtil0.Type[X] +starting with method WithType in object TypeUtil0 + def foo[X]() = as[X](null) + ^ +two errors found diff --git a/test/files/neg/t696.scala b/test/files/neg/t696.scala index a06a32141a..ca76f7ef6c 100644 --- a/test/files/neg/t696.scala +++ b/test/files/neg/t696.scala @@ -1,6 +1,7 @@ object TypeUtil0 { - trait Type[+T]; + trait Type[+T] implicit def WithType[S,T](implicit tpeS : Type[S], tpeT : Type[T]) : Type[S with T] = null - as[Any](null); - def as[T](x : Any)(implicit tpe : Type[T]) = null; + def as[T](x : Any)(implicit tpe : Type[T]) = null + as[Any](null) + def foo[X]() = as[X](null) } diff --git a/test/files/run/t7291.check b/test/files/run/t7291.check new file mode 100644 index 0000000000..c07ba986a3 --- /dev/null +++ b/test/files/run/t7291.check @@ -0,0 +1,2 @@ +conjure +traversable diff --git a/test/files/run/t7291.scala b/test/files/run/t7291.scala new file mode 100644 index 0000000000..30c4261a81 --- /dev/null +++ b/test/files/run/t7291.scala @@ -0,0 +1,19 @@ +trait Fooable[T] +object Fooable { + implicit def conjure[T]: Fooable[T] = { + println("conjure") + new Fooable[T]{} + } + +} + +object Test { + implicit def traversable[T, Coll[_] <: Traversable[_]](implicit +elem: Fooable[T]): Fooable[Coll[T]] = { + println("traversable") + new Fooable[Coll[T]]{} + } + def main(args: Array[String]) { + implicitly[Fooable[List[Any]]] + } +} |