summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugene Burmako <xeno.by@gmail.com>2013-08-11 21:54:11 +0200
committerEugene Burmako <xeno.by@gmail.com>2013-10-11 13:03:49 +0200
commit210dbc7887bc42eed4154de65d0ff5f46ca5ee58 (patch)
tree535e273511e0503a743a14fad7099d9fa73ac04c
parentacd77803f7da7c369f4ffdc70b5eeec4a23e35ae (diff)
downloadscala-210dbc7887bc42eed4154de65d0ff5f46ca5ee58.tar.gz
scala-210dbc7887bc42eed4154de65d0ff5f46ca5ee58.tar.bz2
scala-210dbc7887bc42eed4154de65d0ff5f46ca5ee58.zip
SI-3346 implicit parameters can now guide implicit view inference
This simple patch makes it possible for implicit views to benefit from fundep-guided type inference, eliminating a nasty special case in implicit inference. Here are the changes that I had to apply to the tests (they exposed quite a number of interesting questions that I was happy to answer): 1) neg/t5845.scala now works, so I moved it to pos. That easily makes sense, because the test was just a canary to track previous state of things. 2) neg/divergent_implicit.scala, neg/t5578.scala and neg/t7519.scala changed their messages to less informative ones, because inapplicable implicit views now get disqualified early and therefore don't display their error messages to the user. This is an unfortunate but necessary byproduct of this change, and one can argue that the behavior is now completely consistent with implicit vals (that also don't explain why this or that implicit val got disqualified, but rather display a generic implicit value not found message). 3) scaladoc/implicits-chaining.scala and scaladoc/implicits-base.scala. Immediate culling of apriori inapplicable implicit views messes things up for Scaladoc, because it's interested in potentially applicable views, having infrastructure to track various constraints (type bounds, requirements for implicit parameters, etc) that guide applicability of views and then present it to the user. Therefore, when scaladoc is detected, implicit search reverts to the old behavior. 4) We still cannot have Jason's workaround to type constructor inference mentioned in comments to SI-3346, because it's not really about implicit parameters of implicit views, but about type inference flowing from the implicit parameter list to a preceding parameter list in order to affect inference of an implicit view. This is something that's still too ambitious.
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala26
-rw-r--r--test/files/neg/divergent-implicit.check16
-rw-r--r--test/files/neg/t3346b.check4
-rw-r--r--test/files/neg/t3346b.scala15
-rw-r--r--test/files/neg/t3346c.check4
-rw-r--r--test/files/neg/t3346c.scala61
-rw-r--r--test/files/neg/t3346i.check7
-rw-r--r--test/files/neg/t3346i.scala30
-rw-r--r--test/files/neg/t5578.check5
-rw-r--r--test/files/neg/t5845.check7
-rw-r--r--test/files/neg/t7519.check8
-rw-r--r--test/files/pos/t5845.check0
-rw-r--r--test/files/pos/t5845.scala (renamed from test/files/neg/t5845.scala)0
-rw-r--r--test/files/run/t3346a.check1
-rw-r--r--test/files/run/t3346a.scala11
-rw-r--r--test/files/run/t3346b.check0
-rw-r--r--test/files/run/t3346c.check0
-rw-r--r--test/files/run/t3346d.check0
-rw-r--r--test/files/run/t3346d.scala21
-rw-r--r--test/files/run/t3346e.check12
-rw-r--r--test/files/run/t3346e.scala81
-rw-r--r--test/files/run/t3346f.check2
-rw-r--r--test/files/run/t3346f.scala15
-rw-r--r--test/files/run/t3346g.check1
-rw-r--r--test/files/run/t3346g.scala9
-rw-r--r--test/files/run/t3346h.check1
-rw-r--r--test/files/run/t3346h.scala9
-rw-r--r--test/files/run/t3346j.check1
-rw-r--r--test/files/run/t3346j.scala11
29 files changed, 334 insertions, 24 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index b30ae917d9..c19d861d23 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -579,10 +579,10 @@ trait Implicits {
private def typedImplicit1(info: ImplicitInfo, isLocal: Boolean): SearchResult = {
if (Statistics.canEnable) Statistics.incCounter(matchingImplicits)
- val itree = atPos(pos.focus) {
- // workaround for deficient context provided by ModelFactoryImplicitSupport#makeImplicitConstraints
- val isScalaDoc = context.tree == EmptyTree
+ // workaround for deficient context provided by ModelFactoryImplicitSupport#makeImplicitConstraints
+ val isScalaDoc = context.tree == EmptyTree
+ val itree = atPos(pos.focus) {
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.
@@ -605,7 +605,23 @@ trait Implicits {
atPos(itree.pos)(Apply(itree, List(Ident("<argument>") setType approximate(arg1)))),
EXPRmode,
approximate(arg2)
- )
+ ) match {
+ // try to infer implicit parameters immediately in order to:
+ // 1) guide type inference for implicit views
+ // 2) discard ineligible views right away instead of risking spurious ambiguous implicits
+ //
+ // this is an improvement of the state of the art that brings consistency to implicit resolution rules
+ // (and also helps fundep materialization to be applicable to implicit views)
+ //
+ // there's one caveat though. we need to turn this behavior off for scaladoc
+ // because scaladoc usually doesn't know the entire story
+ // and is just interested in views that are potentially applicable
+ // for instance, if we have `class C[T]` and `implicit def conv[T: Numeric](c: C[T]) = ???`
+ // then Scaladoc will give us something of type `C[T]`, and it would like to know
+ // that `conv` is potentially available under such and such conditions
+ case tree if isImplicitMethodType(tree.tpe) && !isScalaDoc => applyImplicitArgs(tree)
+ case tree => tree
+ }
case _ => fallback
}
context.firstError match { // using match rather than foreach to avoid non local return.
@@ -617,7 +633,7 @@ trait Implicits {
if (Statistics.canEnable) Statistics.incCounter(typedImplicits)
- val itree2 = if (isView) (itree1: @unchecked) match { case Apply(fun, _) => fun }
+ val itree2 = if (isView) treeInfo.dissectApplied(itree1).callee
else adapt(itree1, EXPRmode, wildPt)
typingStack.showAdapt(itree, itree2, pt, context)
diff --git a/test/files/neg/divergent-implicit.check b/test/files/neg/divergent-implicit.check
index 5f20df1b91..60d876409f 100644
--- a/test/files/neg/divergent-implicit.check
+++ b/test/files/neg/divergent-implicit.check
@@ -3,16 +3,14 @@ divergent-implicit.scala:4: error: type mismatch;
required: String
val x1: String = 1
^
-divergent-implicit.scala:5: error: diverging implicit expansion for type Int => String
-starting with method cast in object Test1
- val x2: String = cast[Int, String](1)
- ^
-divergent-implicit.scala:14: error: diverging implicit expansion for type Test2.Baz => Test2.Bar
-starting with method baz2bar in object Test2
+divergent-implicit.scala:14: error: type mismatch;
+ found : Test2.Foo
+ required: Test2.Bar
val x: Bar = new Foo
^
-divergent-implicit.scala:15: error: diverging implicit expansion for type Test2.Foo => Test2.Bar
-starting with method foo2bar in object Test2
+divergent-implicit.scala:15: error: type mismatch;
+ found : Test2.Baz
+ required: Test2.Bar
val y: Bar = new Baz
^
-four errors found
+three errors found
diff --git a/test/files/neg/t3346b.check b/test/files/neg/t3346b.check
new file mode 100644
index 0000000000..bcde6d90e4
--- /dev/null
+++ b/test/files/neg/t3346b.check
@@ -0,0 +1,4 @@
+t3346b.scala:14: error: could not find implicit value for evidence parameter of type TC[Any]
+ val y = foo(1)
+ ^
+one error found
diff --git a/test/files/neg/t3346b.scala b/test/files/neg/t3346b.scala
new file mode 100644
index 0000000000..8ea8970298
--- /dev/null
+++ b/test/files/neg/t3346b.scala
@@ -0,0 +1,15 @@
+import scala.language.implicitConversions
+
+trait T[X]
+trait U[X]
+trait TC[M[_]]
+
+object Test extends App {
+ def foo[M[_]: TC, A](ma: M[A]) = ()
+ implicit val TCofT: TC[T] = new TC[T] {}
+ implicit def any2T[A](a: A): T[A] = new T[A] {}
+ implicit def any2U[A](a: A): U[A] = new U[A] {}
+
+ val x = foo[T, Int](1)
+ val y = foo(1)
+} \ No newline at end of file
diff --git a/test/files/neg/t3346c.check b/test/files/neg/t3346c.check
new file mode 100644
index 0000000000..575379d009
--- /dev/null
+++ b/test/files/neg/t3346c.check
@@ -0,0 +1,4 @@
+t3346c.scala:60: error: value bar is not a member of Either[Int,String]
+ eii.bar
+ ^
+one error found
diff --git a/test/files/neg/t3346c.scala b/test/files/neg/t3346c.scala
new file mode 100644
index 0000000000..a5ac166b2d
--- /dev/null
+++ b/test/files/neg/t3346c.scala
@@ -0,0 +1,61 @@
+object Test extends App {
+ //
+ // An attempt to workaround SI-2712, foiled by SI-3346
+ //
+ trait TC[M[_]]
+
+ type EitherInt[A] = Either[Int, A]
+
+ implicit object EitherTC extends TC[EitherInt]
+
+ def foo[M[_]: TC, A](ma: M[A]) = ()
+
+ val eii: Either[Int, String] = Right("")
+
+ foo[EitherInt, String](eii)
+
+ // This one needs SI-2712 Higher Order Unification
+ //foo(eii) // not inferred
+
+ // A workaround is to provide a set of implicit conversions that take values
+ // based on type constructors of various shapes, and search for the
+ // type class instances.
+ //
+ // This is the approach taken by scalaz7.
+
+ trait TCValue[M[_], A] {
+ implicit def self: M[A]
+ def M: TC[M]
+
+ // instead of `foo(eii)`, we'll try `eii.foo`
+ def foo[M[_], A] = ()
+ }
+
+
+ implicit def ToTCValue[M[_], A](ma: M[A])(implicit M0: TC[M]) = new TCValue[M, A] {
+ implicit val M = M0
+ val self = ma
+ }
+ implicit def ToTCValueBin1[M[_, _], A, B](ma: M[A, B])(implicit M0: TC[({type λ[α]=M[A, α]})#λ]): TCValue[({type λ[α] = M[A, α]})#λ, B] = new TCValue[({type λ[α]=M[A, α]})#λ, B] {
+ implicit val M = M0
+ val self = ma
+ }
+ implicit def ToTCValueBin2[M[_, _], A, B](ma: M[A, B])(implicit M0: TC[({type λ[α]=M[α, B]})#λ]): TCValue[({type λ[α]=M[α, B]})#λ, A] = new TCValue[({type λ[α]=M[α, B]})#λ, A] {
+ implicit val M = M0
+ val self = ma
+ }
+
+
+ ToTCValueBin1(eii).foo
+
+ // as expected, could not find implicit parameter
+ // ToTCValueBin2(eii).bar
+
+ // error: implicit conversions are not applicable because they are ambiguous, both method ToTCValueBin1 ... and method ToTCValueBin2
+ // annoying!!
+ // https://issues.scala-lang.org/browse/SI-3346
+ //
+ // Works if we remove ToTCValueBin2
+ //
+ eii.bar
+}
diff --git a/test/files/neg/t3346i.check b/test/files/neg/t3346i.check
new file mode 100644
index 0000000000..cc17ab7ce4
--- /dev/null
+++ b/test/files/neg/t3346i.check
@@ -0,0 +1,7 @@
+t3346i.scala:28: error: value a is not a member of Test.A[T]
+ (new A).a
+ ^
+t3346i.scala:29: error: value a is not a member of Test.A[Nothing]
+ (new A[Nothing]).a
+ ^
+two errors found
diff --git a/test/files/neg/t3346i.scala b/test/files/neg/t3346i.scala
new file mode 100644
index 0000000000..9ad2544537
--- /dev/null
+++ b/test/files/neg/t3346i.scala
@@ -0,0 +1,30 @@
+import scala.language.implicitConversions
+
+// the classes involved
+case class Z[U](a: U)
+case class Intermediate[T, U](t: T, u: U)
+class Implicit1[T](b: Implicit2[T])
+class Implicit2[T](c: Implicit3[T])
+class Implicit3[T](/* and so on */)
+
+object Test extends App {
+ // the base conversion
+ implicit def convertToZ[T](a: A[T])(implicit b: Implicit1[T]): Z[A[T]] = Z(a)
+
+ // and the implicit chaining, don't you just love it? :D
+ // implicit1, with one alternative
+ implicit def implicit1[T <: Intermediate[_, _]](implicit b: Implicit2[T]) = new Implicit1[T](b)
+ // implicit2, with two alternatives
+ implicit def implicit2alt1[T <: Intermediate[_ <: String, _]](implicit c: Implicit3[T]) = new Implicit2[T](c)
+ implicit def implicit2alt2[T <: Intermediate[_ <: Double, _]](implicit c: Implicit3[T]) = new Implicit2[T](c)
+ // implicit3, with two alternatives
+ implicit def implicit3alt1[T <: Intermediate[_, _ <: Int]] = new Implicit3[T]()
+ implicit def implicit3alt2[T <: Intermediate[_ <: Double, _ <: AnyRef],X] = new Implicit3[T]()
+
+ // and our targets
+ /** conversion here, with constraints */
+ class A[T]()
+
+ (new A).a
+ (new A[Nothing]).a
+}
diff --git a/test/files/neg/t5578.check b/test/files/neg/t5578.check
index d803adb223..56123d2e0f 100644
--- a/test/files/neg/t5578.check
+++ b/test/files/neg/t5578.check
@@ -1,4 +1,7 @@
-t5578.scala:33: error: No Manifest available for T.
+t5578.scala:33: error: type mismatch;
+ found : NumericOpsExp.this.Plus[T]
+ required: NumericOpsExp.this.Rep[T]
+ (which expands to) NumericOpsExp.this.Exp[T]
def plus[T: Numeric](x: Rep[T], y: Rep[T]): Rep[T] = Plus[T](x,y)
^
one error found
diff --git a/test/files/neg/t5845.check b/test/files/neg/t5845.check
deleted file mode 100644
index 8c6100d6de..0000000000
--- a/test/files/neg/t5845.check
+++ /dev/null
@@ -1,7 +0,0 @@
-t5845.scala:9: error: value +++ is not a member of Int
- println(5 +++ 5)
- ^
-t5845.scala:15: error: value +++ is not a member of Int
- println(5 +++ 5)
- ^
-two errors found
diff --git a/test/files/neg/t7519.check b/test/files/neg/t7519.check
index 164d67f595..df54abaa3e 100644
--- a/test/files/neg/t7519.check
+++ b/test/files/neg/t7519.check
@@ -1,7 +1,11 @@
-t7519.scala:5: error: could not find implicit value for parameter nada: Nothing
+t7519.scala:5: error: type mismatch;
+ found : Int(0)
+ required: String
locally(0 : String) // was: "value conversion is not a member of C.this.C"
^
-t7519.scala:15: error: could not find implicit value for parameter nada: Nothing
+t7519.scala:15: error: type mismatch;
+ found : Int(0)
+ required: String
locally(0 : String) // was: "value conversion is not a member of U"
^
two errors found
diff --git a/test/files/pos/t5845.check b/test/files/pos/t5845.check
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/files/pos/t5845.check
diff --git a/test/files/neg/t5845.scala b/test/files/pos/t5845.scala
index 823c722c14..823c722c14 100644
--- a/test/files/neg/t5845.scala
+++ b/test/files/pos/t5845.scala
diff --git a/test/files/run/t3346a.check b/test/files/run/t3346a.check
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/test/files/run/t3346a.check
@@ -0,0 +1 @@
+1
diff --git a/test/files/run/t3346a.scala b/test/files/run/t3346a.scala
new file mode 100644
index 0000000000..c0a90b011b
--- /dev/null
+++ b/test/files/run/t3346a.scala
@@ -0,0 +1,11 @@
+import scala.language.implicitConversions
+
+object Test extends App {
+ class Rep[T](x : T)
+
+ class SomeOps[T](x : Rep[T]) { def foo = 1 }
+ implicit def mkOps[X, T](x : X)(implicit conv: X => Rep[T]) : SomeOps[T] = new SomeOps(conv(x))
+
+ val a: Rep[Int] = new Rep(42)
+ println(a.foo)
+} \ No newline at end of file
diff --git a/test/files/run/t3346b.check b/test/files/run/t3346b.check
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/files/run/t3346b.check
diff --git a/test/files/run/t3346c.check b/test/files/run/t3346c.check
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/files/run/t3346c.check
diff --git a/test/files/run/t3346d.check b/test/files/run/t3346d.check
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/files/run/t3346d.check
diff --git a/test/files/run/t3346d.scala b/test/files/run/t3346d.scala
new file mode 100644
index 0000000000..3f79896210
--- /dev/null
+++ b/test/files/run/t3346d.scala
@@ -0,0 +1,21 @@
+import scala.language.implicitConversions
+
+object Test extends App {
+ trait TARInt
+
+ trait Basket[A,B] {
+ def iAmABasket = {}
+ }
+
+ trait BasketFactory[A,B] {
+ def create(v: A): Basket[A,B]
+ }
+
+ implicit val bf = new BasketFactory[Int,TARInt] {
+ def create(v: Int): Basket[Int,TARInt] = new Basket[Int, TARInt]{}
+ }
+
+ implicit def i2[A,B](a: A)(implicit bf: BasketFactory[A,B]): Basket[A,B] = bf.create(a)
+
+ 1.iAmABasket // <-- i2 conversion not applicable
+} \ No newline at end of file
diff --git a/test/files/run/t3346e.check b/test/files/run/t3346e.check
new file mode 100644
index 0000000000..71a57ffa70
--- /dev/null
+++ b/test/files/run/t3346e.check
@@ -0,0 +1,12 @@
+eqw
+List(0, 2)
+List(0, 2)
+BitSet(0, 2)
+Vector(113, 119, 101)
+qwe
+List(2, 0)
+List(0!)
+BitSet(0, 2)
+qwe
+List(2, 0)
+qwe
diff --git a/test/files/run/t3346e.scala b/test/files/run/t3346e.scala
new file mode 100644
index 0000000000..ac0de564d4
--- /dev/null
+++ b/test/files/run/t3346e.scala
@@ -0,0 +1,81 @@
+import scala.language.implicitConversions
+import scala.collection.generic.CanBuildFrom
+import scala.math.Ordering
+import collection.{TraversableLike, SeqLike}
+import collection.immutable.BitSet
+
+class QuickSort[Coll](a: Coll) {
+ //should be able to sort only something with defined order (someting like a Seq)
+ def quickSort[T](implicit ev0: Coll => SeqLike[T, Coll],
+ cbf: CanBuildFrom[Coll, T, Coll],
+ n: Ordering[T]): Coll = {
+ quickSortAnything(ev0, cbf, n)
+ }
+
+ //we can even sort a Set, if we really want to
+ def quickSortAnything[T](implicit ev0: Coll => TraversableLike[T, Coll],
+ cbf: CanBuildFrom[Coll, T, Coll],
+ n: Ordering[T]): Coll = {
+ import n._
+ if (a.size < 2) {
+ a
+ } else {
+ // We pick the first value for the pivot.
+ val pivot = a.head
+ val (lower, tmp) = a.partition(_ < pivot)
+ val (upper, same) = tmp.partition(_ > pivot)
+ val b = cbf()
+ b.sizeHint(a.size)
+ b ++= new QuickSort(lower).quickSortAnything
+ b ++= same
+ b ++= new QuickSort(upper).quickSortAnything
+ b.result
+ }
+ }
+}
+
+class FilterMap[Repr](a: Repr) {
+ def filterMap[A, B, That](f: A => Option[B])(implicit ev0: Repr => TraversableLike[A, Repr],
+ cbf: CanBuildFrom[Repr, B, That]): That = {
+ a.flatMap(e => f(e).toSeq)
+ }
+}
+
+class FilterMapFixed[A, Repr <% TraversableLike[A, Repr]](a: Repr) {
+ def filterMap2[B, That](f: A => Option[B])(implicit cbf: CanBuildFrom[Repr, B, That]): That = {
+ a.flatMap(e => f(e).toSeq)
+ }
+}
+
+object MyEnhancements {
+ implicit def toQS[Coll](a: Coll) = new QuickSort(a)
+ implicit def toFM[Coll](a: Coll) = new FilterMap(a)
+ implicit def toFM2[A, Repr <% TraversableLike[A, Repr]](a: Repr) = new FilterMapFixed(a)
+}
+
+object Test extends App {
+
+ import MyEnhancements._
+
+ println("qwe".quickSort)
+ println(Array(2, 0).quickSort.toList)
+ println(Seq(2, 0).quickSort)
+ //not very useful to sort a set, but just as a demonstration
+ println(BitSet(2, 0).quickSortAnything)
+
+ //need to hint type inferencer,
+ //probably will be able to overcome after https://issues.scala-lang.org/browse/SI-4699 and
+ // related issues are fixed (by moving ev0 parameter from filterMap to toFM), see toFM2
+ println("qwe".filterMap((c: Char) => Some(c.toInt)))
+ println("qwe".filterMap((c: Char) => Some(c)))
+ println(Array(2, 0).filterMap((c: Int) => Some(c.toInt)).toList)
+ println(Seq(2, 0).filterMap((c: Int) => if (c < 2) Some(c + "!") else None))
+ def test(i:Int) = Option(i)
+ println(BitSet(2,0).filterMap(test))
+
+ println(toFM2("qwe").filterMap2(c => Some(c)))
+ println(toFM2(Array(2, 0)).filterMap2(c => Some(c.toInt)).toList)
+ //No implicit view available from java.lang.String => scala.collection.TraversableLike[A,java.lang.String]. :(
+ //Not anymore :)
+ println("qwe".filterMap2(c => Some(c)))
+}
diff --git a/test/files/run/t3346f.check b/test/files/run/t3346f.check
new file mode 100644
index 0000000000..fd3c81a4d7
--- /dev/null
+++ b/test/files/run/t3346f.check
@@ -0,0 +1,2 @@
+5
+5
diff --git a/test/files/run/t3346f.scala b/test/files/run/t3346f.scala
new file mode 100644
index 0000000000..4799ca2ca9
--- /dev/null
+++ b/test/files/run/t3346f.scala
@@ -0,0 +1,15 @@
+import scala.language.implicitConversions
+import scala.language.reflectiveCalls
+
+object Test extends App {
+ trait Foo[A]
+ implicit def fooString: Foo[String] = null
+ implicit def value[A](implicit foo: Foo[A]) = 5
+
+ println(implicitly[Int])
+
+ implicit def conversion[A](x: Int)(implicit foo: Foo[A]) = new {
+ def aMethod = 5
+ }
+ println(1.aMethod)
+}
diff --git a/test/files/run/t3346g.check b/test/files/run/t3346g.check
new file mode 100644
index 0000000000..ce894825e0
--- /dev/null
+++ b/test/files/run/t3346g.check
@@ -0,0 +1 @@
+A(3,asdf)
diff --git a/test/files/run/t3346g.scala b/test/files/run/t3346g.scala
new file mode 100644
index 0000000000..d7c9d79c7f
--- /dev/null
+++ b/test/files/run/t3346g.scala
@@ -0,0 +1,9 @@
+import scala.language.implicitConversions
+
+case class A(b: Int, c: String)
+
+object Test extends App {
+ implicit def s2i(s: String): Int = s.length
+ implicit def toA[T](t: T)(implicit f: T => Int): A = A(f(t), t.toString)
+ println("asdf".copy(b = 3))
+} \ No newline at end of file
diff --git a/test/files/run/t3346h.check b/test/files/run/t3346h.check
new file mode 100644
index 0000000000..587be6b4c3
--- /dev/null
+++ b/test/files/run/t3346h.check
@@ -0,0 +1 @@
+x
diff --git a/test/files/run/t3346h.scala b/test/files/run/t3346h.scala
new file mode 100644
index 0000000000..97ebc9380c
--- /dev/null
+++ b/test/files/run/t3346h.scala
@@ -0,0 +1,9 @@
+import scala.language.implicitConversions
+
+object Test extends App {
+ trait Fundep[T, U] { def u(t: T): U }
+ class C { def y = "x" }
+ implicit val FundepStringC = new Fundep[String, C]{ def u(t: String) = new C }
+ implicit def foo[T, U](x: T)(implicit y: Fundep[T, U]): U = y.u(x)
+ println("x".y)
+} \ No newline at end of file
diff --git a/test/files/run/t3346j.check b/test/files/run/t3346j.check
new file mode 100644
index 0000000000..59e8626fc5
--- /dev/null
+++ b/test/files/run/t3346j.check
@@ -0,0 +1 @@
+Int
diff --git a/test/files/run/t3346j.scala b/test/files/run/t3346j.scala
new file mode 100644
index 0000000000..98b5a870a7
--- /dev/null
+++ b/test/files/run/t3346j.scala
@@ -0,0 +1,11 @@
+import scala.language.implicitConversions
+import scala.language.reflectiveCalls
+import scala.reflect.runtime.universe._
+
+object Test extends App {
+ class A[T]
+ class B[T]
+ implicit def foo[T: TypeTag](a: A[T])(implicit b: B[T]) = new { def baz = typeOf[T] }
+ implicit def bar[T <: Int]: B[T] = new B[T]()
+ println(new A[Int]().baz)
+} \ No newline at end of file