summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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