Skip to content

Commit 54707cb

Browse files
committed
Merge pull request scala#3030 from xeno-by/topic/fundep-views
SI-3346 implicit parameters can now guide implicit view inference
2 parents 5b2c464 + 210dbc7 commit 54707cb

29 files changed

+334
-24
lines changed

src/compiler/scala/tools/nsc/typechecker/Implicits.scala

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -579,10 +579,10 @@ trait Implicits {
579579
private def typedImplicit1(info: ImplicitInfo, isLocal: Boolean): SearchResult = {
580580
if (Statistics.canEnable) Statistics.incCounter(matchingImplicits)
581581

582-
val itree = atPos(pos.focus) {
583-
// workaround for deficient context provided by ModelFactoryImplicitSupport#makeImplicitConstraints
584-
val isScalaDoc = context.tree == EmptyTree
582+
// workaround for deficient context provided by ModelFactoryImplicitSupport#makeImplicitConstraints
583+
val isScalaDoc = context.tree == EmptyTree
585584

585+
val itree = atPos(pos.focus) {
586586
if (isLocal && !isScalaDoc) {
587587
// SI-4270 SI-5376 Always use an unattributed Ident for implicits in the local scope,
588588
// rather than an attributed Select, to detect shadowing.
@@ -605,7 +605,23 @@ trait Implicits {
605605
atPos(itree.pos)(Apply(itree, List(Ident("<argument>") setType approximate(arg1)))),
606606
EXPRmode,
607607
approximate(arg2)
608-
)
608+
) match {
609+
// try to infer implicit parameters immediately in order to:
610+
// 1) guide type inference for implicit views
611+
// 2) discard ineligible views right away instead of risking spurious ambiguous implicits
612+
//
613+
// this is an improvement of the state of the art that brings consistency to implicit resolution rules
614+
// (and also helps fundep materialization to be applicable to implicit views)
615+
//
616+
// there's one caveat though. we need to turn this behavior off for scaladoc
617+
// because scaladoc usually doesn't know the entire story
618+
// and is just interested in views that are potentially applicable
619+
// for instance, if we have `class C[T]` and `implicit def conv[T: Numeric](c: C[T]) = ???`
620+
// then Scaladoc will give us something of type `C[T]`, and it would like to know
621+
// that `conv` is potentially available under such and such conditions
622+
case tree if isImplicitMethodType(tree.tpe) && !isScalaDoc => applyImplicitArgs(tree)
623+
case tree => tree
624+
}
609625
case _ => fallback
610626
}
611627
context.firstError match { // using match rather than foreach to avoid non local return.
@@ -617,7 +633,7 @@ trait Implicits {
617633

618634
if (Statistics.canEnable) Statistics.incCounter(typedImplicits)
619635

620-
val itree2 = if (isView) (itree1: @unchecked) match { case Apply(fun, _) => fun }
636+
val itree2 = if (isView) treeInfo.dissectApplied(itree1).callee
621637
else adapt(itree1, EXPRmode, wildPt)
622638

623639
typingStack.showAdapt(itree, itree2, pt, context)

test/files/neg/divergent-implicit.check

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,14 @@ divergent-implicit.scala:4: error: type mismatch;
33
required: String
44
val x1: String = 1
55
^
6-
divergent-implicit.scala:5: error: diverging implicit expansion for type Int => String
7-
starting with method cast in object Test1
8-
val x2: String = cast[Int, String](1)
9-
^
10-
divergent-implicit.scala:14: error: diverging implicit expansion for type Test2.Baz => Test2.Bar
11-
starting with method baz2bar in object Test2
6+
divergent-implicit.scala:14: error: type mismatch;
7+
found : Test2.Foo
8+
required: Test2.Bar
129
val x: Bar = new Foo
1310
^
14-
divergent-implicit.scala:15: error: diverging implicit expansion for type Test2.Foo => Test2.Bar
15-
starting with method foo2bar in object Test2
11+
divergent-implicit.scala:15: error: type mismatch;
12+
found : Test2.Baz
13+
required: Test2.Bar
1614
val y: Bar = new Baz
1715
^
18-
four errors found
16+
three errors found

test/files/neg/t3346b.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
t3346b.scala:14: error: could not find implicit value for evidence parameter of type TC[Any]
2+
val y = foo(1)
3+
^
4+
one error found

test/files/neg/t3346b.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import scala.language.implicitConversions
2+
3+
trait T[X]
4+
trait U[X]
5+
trait TC[M[_]]
6+
7+
object Test extends App {
8+
def foo[M[_]: TC, A](ma: M[A]) = ()
9+
implicit val TCofT: TC[T] = new TC[T] {}
10+
implicit def any2T[A](a: A): T[A] = new T[A] {}
11+
implicit def any2U[A](a: A): U[A] = new U[A] {}
12+
13+
val x = foo[T, Int](1)
14+
val y = foo(1)
15+
}

test/files/neg/t3346c.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
t3346c.scala:60: error: value bar is not a member of Either[Int,String]
2+
eii.bar
3+
^
4+
one error found

test/files/neg/t3346c.scala

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
object Test extends App {
2+
//
3+
// An attempt to workaround SI-2712, foiled by SI-3346
4+
//
5+
trait TC[M[_]]
6+
7+
type EitherInt[A] = Either[Int, A]
8+
9+
implicit object EitherTC extends TC[EitherInt]
10+
11+
def foo[M[_]: TC, A](ma: M[A]) = ()
12+
13+
val eii: Either[Int, String] = Right("")
14+
15+
foo[EitherInt, String](eii)
16+
17+
// This one needs SI-2712 Higher Order Unification
18+
//foo(eii) // not inferred
19+
20+
// A workaround is to provide a set of implicit conversions that take values
21+
// based on type constructors of various shapes, and search for the
22+
// type class instances.
23+
//
24+
// This is the approach taken by scalaz7.
25+
26+
trait TCValue[M[_], A] {
27+
implicit def self: M[A]
28+
def M: TC[M]
29+
30+
// instead of `foo(eii)`, we'll try `eii.foo`
31+
def foo[M[_], A] = ()
32+
}
33+
34+
35+
implicit def ToTCValue[M[_], A](ma: M[A])(implicit M0: TC[M]) = new TCValue[M, A] {
36+
implicit val M = M0
37+
val self = ma
38+
}
39+
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] {
40+
implicit val M = M0
41+
val self = ma
42+
}
43+
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] {
44+
implicit val M = M0
45+
val self = ma
46+
}
47+
48+
49+
ToTCValueBin1(eii).foo
50+
51+
// as expected, could not find implicit parameter
52+
// ToTCValueBin2(eii).bar
53+
54+
// error: implicit conversions are not applicable because they are ambiguous, both method ToTCValueBin1 ... and method ToTCValueBin2
55+
// annoying!!
56+
// https://issues.scala-lang.org/browse/SI-3346
57+
//
58+
// Works if we remove ToTCValueBin2
59+
//
60+
eii.bar
61+
}

test/files/neg/t3346i.check

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
t3346i.scala:28: error: value a is not a member of Test.A[T]
2+
(new A).a
3+
^
4+
t3346i.scala:29: error: value a is not a member of Test.A[Nothing]
5+
(new A[Nothing]).a
6+
^
7+
two errors found

test/files/neg/t3346i.scala

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import scala.language.implicitConversions
2+
3+
// the classes involved
4+
case class Z[U](a: U)
5+
case class Intermediate[T, U](t: T, u: U)
6+
class Implicit1[T](b: Implicit2[T])
7+
class Implicit2[T](c: Implicit3[T])
8+
class Implicit3[T](/* and so on */)
9+
10+
object Test extends App {
11+
// the base conversion
12+
implicit def convertToZ[T](a: A[T])(implicit b: Implicit1[T]): Z[A[T]] = Z(a)
13+
14+
// and the implicit chaining, don't you just love it? :D
15+
// implicit1, with one alternative
16+
implicit def implicit1[T <: Intermediate[_, _]](implicit b: Implicit2[T]) = new Implicit1[T](b)
17+
// implicit2, with two alternatives
18+
implicit def implicit2alt1[T <: Intermediate[_ <: String, _]](implicit c: Implicit3[T]) = new Implicit2[T](c)
19+
implicit def implicit2alt2[T <: Intermediate[_ <: Double, _]](implicit c: Implicit3[T]) = new Implicit2[T](c)
20+
// implicit3, with two alternatives
21+
implicit def implicit3alt1[T <: Intermediate[_, _ <: Int]] = new Implicit3[T]()
22+
implicit def implicit3alt2[T <: Intermediate[_ <: Double, _ <: AnyRef],X] = new Implicit3[T]()
23+
24+
// and our targets
25+
/** conversion here, with constraints */
26+
class A[T]()
27+
28+
(new A).a
29+
(new A[Nothing]).a
30+
}

test/files/neg/t5578.check

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
t5578.scala:33: error: No Manifest available for T.
1+
t5578.scala:33: error: type mismatch;
2+
found : NumericOpsExp.this.Plus[T]
3+
required: NumericOpsExp.this.Rep[T]
4+
(which expands to) NumericOpsExp.this.Exp[T]
25
def plus[T: Numeric](x: Rep[T], y: Rep[T]): Rep[T] = Plus[T](x,y)
36
^
47
one error found

test/files/neg/t5845.check

Lines changed: 0 additions & 7 deletions
This file was deleted.

0 commit comments

Comments
 (0)