From 210dbc7887bc42eed4154de65d0ff5f46ca5ee58 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Sun, 11 Aug 2013 21:54:11 +0200 Subject: 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. --- test/files/neg/divergent-implicit.check | 16 ++++----- test/files/neg/t3346b.check | 4 +++ test/files/neg/t3346b.scala | 15 ++++++++ test/files/neg/t3346c.check | 4 +++ test/files/neg/t3346c.scala | 61 +++++++++++++++++++++++++++++++++ test/files/neg/t3346i.check | 7 ++++ test/files/neg/t3346i.scala | 30 ++++++++++++++++ test/files/neg/t5578.check | 5 ++- test/files/neg/t5845.check | 7 ---- test/files/neg/t5845.scala | 16 --------- test/files/neg/t7519.check | 8 +++-- 11 files changed, 138 insertions(+), 35 deletions(-) create mode 100644 test/files/neg/t3346b.check create mode 100644 test/files/neg/t3346b.scala create mode 100644 test/files/neg/t3346c.check create mode 100644 test/files/neg/t3346c.scala create mode 100644 test/files/neg/t3346i.check create mode 100644 test/files/neg/t3346i.scala delete mode 100644 test/files/neg/t5845.check delete mode 100644 test/files/neg/t5845.scala (limited to 'test/files/neg') 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/t5845.scala b/test/files/neg/t5845.scala deleted file mode 100644 index 823c722c14..0000000000 --- a/test/files/neg/t5845.scala +++ /dev/null @@ -1,16 +0,0 @@ -class Num[T] { - def mkOps = new Ops - class Ops { def +++(rhs: T) = () } -} - -class A { - implicit def infixOps[T, CC[X] <: Num[X]](lhs: T)(implicit num: CC[T]) = num.mkOps - implicit val n1 = new Num[Int] { } - println(5 +++ 5) -} - -class B { - implicit def infixOps[T, CC[X] <: Num[X]](lhs: T)(implicit num: CC[T]) : CC[T]#Ops = num.mkOps - implicit val n1 = new Num[Int] {} - println(5 +++ 5) -} 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 -- cgit v1.2.3