summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala86
-rw-r--r--test/files/buildmanager/t2653/t2653.check2
-rw-r--r--test/files/neg/bug3683b.check2
-rw-r--r--test/files/neg/found-req-variance.check185
-rw-r--r--test/files/neg/found-req-variance.scala106
5 files changed, 378 insertions, 3 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
index 3de2f38d5d..b09ad34c1c 100644
--- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
@@ -216,11 +216,91 @@ trait TypeDiagnostics {
case xs => " where " + (disambiguate(xs map (_.existentialToString)) mkString ", ")
}
- def foundReqMsg(found: Type, req: Type): String =
- withDisambiguation(found, req) {
+ def varianceWord(sym: Symbol): String =
+ if (sym.variance == 1) "covariant"
+ else if (sym.variance == -1) "contravariant"
+ else "invariant"
+
+ /** Look through the base types of the found type for any which
+ * might have been valid subtypes if given conformant type arguments.
+ * Examine those for situations where the type error would have been
+ * eliminated if the variance were different. In such cases, append
+ * an additional explanatory message.
+ *
+ * TODO: handle type aliases better.
+ */
+ def explainVariance(found: Type, req: Type): String = {
+ found.baseTypeSeq.toList foreach { tp =>
+ if (tp.typeSymbol isSubClass req.typeSymbol) {
+ val foundArgs = tp.typeArgs
+ val reqArgs = req.typeArgs
+ val params = req.typeConstructor.typeParams
+
+ if (foundArgs.nonEmpty && foundArgs.length == reqArgs.length) {
+ val relationships = (foundArgs, reqArgs, params).zipped map {
+ case (arg, reqArg, param) =>
+ def mkMsg(isSubtype: Boolean) = {
+ val op = if (isSubtype) "<:" else ">:"
+ val suggest = if (isSubtype) "+" else "-"
+ val reqsym = req.typeSymbol
+ def isJava = reqsym.isJavaDefined
+ def isScala = reqsym hasTransOwner ScalaPackageClass
+
+ val explainFound = "%s %s %s%s, but ".format(
+ arg, op, reqArg,
+ // If the message involves a type from the base type sequence rather than the
+ // actual found type, we need to explain why we're talking about it. Less brute
+ // force measures than comparing normalized Strings were producing error messages
+ // like "and java.util.ArrayList[String] <: java.util.ArrayList[String]" but there
+ // should be a cleaner way to do this.
+ if (found.normalize.toString == tp.normalize.toString) ""
+ else " (and %s <: %s)".format(found, tp)
+ )
+ val explainDef = {
+ val prepend = if (isJava) "Java-defined " else ""
+ "%s%s is %s in %s.".format(prepend, reqsym, varianceWord(param), param)
+ }
+ // Don't suggest they change the class declaration if it's somewhere
+ // under scala.* or defined in a java class, because attempting either
+ // would be fruitless.
+ val suggestChange = "\nYou may wish to " + (
+ if (isScala || isJava)
+ "investigate a wildcard type such as `_ %s %s`. (SLS 3.2.10)".format(op, reqArg)
+ else
+ "define %s as %s%s instead. (SLS 4.5)".format(param.name, suggest, param.name)
+ )
+
+ Some("Note: " + explainFound + explainDef + suggestChange)
+ }
+ // In these cases the arg is OK and needs no explanation.
+ val conforms = (
+ (arg =:= reqArg)
+ || ((arg <:< reqArg) && param.isCovariant)
+ || ((reqArg <:< arg) && param.isContravariant)
+ )
+ val invariant = param.variance == 0
+
+ if (conforms) Some("")
+ else if ((arg <:< reqArg) && invariant) mkMsg(true) // covariant relationship
+ else if ((reqArg <:< arg) && invariant) mkMsg(false) // contravariant relationship
+ else None // we assume in other cases our ham-fisted advice will merely serve to confuse
+ }
+ val messages = relationships.flatten
+ // the condition verifies no type argument came back None
+ if (messages.size == foundArgs.size)
+ return messages filterNot (_ == "") mkString ("\n", "\n", "")
+ }
+ }
+ }
+ "" // no elaborable variance situation found
+ }
+
+ def foundReqMsg(found: Type, req: Type): String = {
+ (withDisambiguation(found, req) {
";\n found : " + found.toLongString + existentialContext(found) +
"\n required: " + req + existentialContext(req)
- }
+ }) + explainVariance(found, req)
+ }
case class TypeDiag(tp: Type, sym: Symbol) extends Ordered[TypeDiag] {
// save the name because it will be mutated until it has been
diff --git a/test/files/buildmanager/t2653/t2653.check b/test/files/buildmanager/t2653/t2653.check
index 0d40601962..6a4fc0e982 100644
--- a/test/files/buildmanager/t2653/t2653.check
+++ b/test/files/buildmanager/t2653/t2653.check
@@ -9,5 +9,7 @@ compiling Set(B.scala)
B.scala:2: error: type mismatch;
found : A[Int]
required: A[Any]
+Note: Int <: Any, but class A is invariant in type T.
+You may wish to define T as +T instead. (SLS 4.5)
val a: A[Any] = new A[Int]
^
diff --git a/test/files/neg/bug3683b.check b/test/files/neg/bug3683b.check
index 3952633d1d..2637f62630 100644
--- a/test/files/neg/bug3683b.check
+++ b/test/files/neg/bug3683b.check
@@ -1,6 +1,8 @@
bug3683b.scala:15: error: constructor cannot be instantiated to expected type;
found : X
required: W[Bar]
+Note: Foo >: Bar (and X <: W[Foo]), but trait W is invariant in type T.
+You may wish to define T as -T instead. (SLS 4.5)
case X() => 1
^
one error found
diff --git a/test/files/neg/found-req-variance.check b/test/files/neg/found-req-variance.check
new file mode 100644
index 0000000000..828e40a48b
--- /dev/null
+++ b/test/files/neg/found-req-variance.check
@@ -0,0 +1,185 @@
+found-req-variance.scala:22: error: type mismatch;
+ found : Inv[B]
+ required: Inv[A]
+Note: B <: A, but class Inv is invariant in type T.
+You may wish to define T as +T instead. (SLS 4.5)
+ def f2 = Set[Inv[A]]() + new Inv[B]
+ ^
+found-req-variance.scala:23: error: type mismatch;
+ found : Inv[C]
+ required: Inv[A]
+Note: C <: A, but class Inv is invariant in type T.
+You may wish to define T as +T instead. (SLS 4.5)
+ def f3 = Set[Inv[A]]() + new Inv[C]
+ ^
+found-req-variance.scala:24: error: type mismatch;
+ found : Inv[A]
+ required: Inv[B]
+Note: A >: B, but class Inv is invariant in type T.
+You may wish to define T as -T instead. (SLS 4.5)
+ def f4 = Set[Inv[B]]() + new Inv[A]
+ ^
+found-req-variance.scala:26: error: type mismatch;
+ found : Inv[C]
+ required: Inv[B]
+Note: C <: B, but class Inv is invariant in type T.
+You may wish to define T as +T instead. (SLS 4.5)
+ def f6 = Set[Inv[B]]() + new Inv[C]
+ ^
+found-req-variance.scala:27: error: type mismatch;
+ found : Inv[A]
+ required: Inv[C]
+Note: A >: C, but class Inv is invariant in type T.
+You may wish to define T as -T instead. (SLS 4.5)
+ def f7 = Set[Inv[C]]() + new Inv[A]
+ ^
+found-req-variance.scala:28: error: type mismatch;
+ found : Inv[B]
+ required: Inv[C]
+Note: B >: C, but class Inv is invariant in type T.
+You may wish to define T as -T instead. (SLS 4.5)
+ def f8 = Set[Inv[C]]() + new Inv[B]
+ ^
+found-req-variance.scala:34: error: type mismatch;
+ found : MultiInv[A]
+ required: Multi[A,B,C]
+Note: A >: B (and MultiInv[A] <: Multi[A,A,C]), but class Multi is invariant in type Inv.
+You may wish to define Inv as -Inv instead. (SLS 4.5)
+ def g4 = Set[Multi[A, B, C]]() + new MultiInv[A]
+ ^
+found-req-variance.scala:36: error: type mismatch;
+ found : MultiInv[C]
+ required: Multi[A,B,C]
+Note: C <: B (and MultiInv[C] <: Multi[A,C,C]), but class Multi is invariant in type Inv.
+You may wish to define Inv as +Inv instead. (SLS 4.5)
+ def g6 = Set[Multi[A, B, C]]() + new MultiInv[C]
+ ^
+found-req-variance.scala:47: error: type mismatch;
+ found : FF1[A,A]
+ required: FF1[B,B]
+ def ff1 = f[B, B](h[A, A]) // fail
+ ^
+found-req-variance.scala:48: error: type mismatch;
+ found : FF1[B,A]
+ required: FF1[B,B]
+ def ff2 = f[B, B](h[B, A]) // fail
+ ^
+found-req-variance.scala:49: error: type mismatch;
+ found : FF1[C,A]
+ required: FF1[B,B]
+ def ff3 = f[B, B](h[C, A]) // fail
+ ^
+found-req-variance.scala:50: error: type mismatch;
+ found : FF1[A,B]
+ required: FF1[B,B]
+Note: A >: B, but trait FF1 is invariant in type T.
+You may wish to define T as -T instead. (SLS 4.5)
+ def ff4 = f[B, B](h[A, B]) // suggest
+ ^
+found-req-variance.scala:52: error: type mismatch;
+ found : FF1[C,B]
+ required: FF1[B,B]
+Note: C <: B, but trait FF1 is invariant in type T.
+You may wish to define T as +T instead. (SLS 4.5)
+ def ff6 = f[B, B](h[C, B]) // suggest
+ ^
+found-req-variance.scala:53: error: type mismatch;
+ found : FF1[A,C]
+ required: FF1[B,B]
+Note: A >: B, but trait FF1 is invariant in type T.
+You may wish to define T as -T instead. (SLS 4.5)
+ def ff7 = f[B, B](h[A, C]) // suggest
+ ^
+found-req-variance.scala:55: error: type mismatch;
+ found : FF1[C,C]
+ required: FF1[B,B]
+Note: C <: B, but trait FF1 is invariant in type T.
+You may wish to define T as +T instead. (SLS 4.5)
+ def ff9 = f[B, B](h[C, C]) // suggest
+ ^
+found-req-variance.scala:61: error: type mismatch;
+ found : FF2[A,A]
+ required: FF2[B,B]
+Note: A >: B, but trait FF2 is invariant in type R.
+You may wish to define R as -R instead. (SLS 4.5)
+ def ff1 = f[B, B](h[A, A]) // suggest
+ ^
+found-req-variance.scala:62: error: type mismatch;
+ found : FF2[B,A]
+ required: FF2[B,B]
+Note: A >: B, but trait FF2 is invariant in type R.
+You may wish to define R as -R instead. (SLS 4.5)
+ def ff2 = f[B, B](h[B, A]) // suggest
+ ^
+found-req-variance.scala:63: error: type mismatch;
+ found : FF2[C,A]
+ required: FF2[B,B]
+ def ff3 = f[B, B](h[C, A]) // fail
+ ^
+found-req-variance.scala:66: error: type mismatch;
+ found : FF2[C,B]
+ required: FF2[B,B]
+ def ff6 = f[B, B](h[C, B]) // fail
+ ^
+found-req-variance.scala:67: error: type mismatch;
+ found : FF2[A,C]
+ required: FF2[B,B]
+Note: C <: B, but trait FF2 is invariant in type R.
+You may wish to define R as +R instead. (SLS 4.5)
+ def ff7 = f[B, B](h[A, C]) // suggest
+ ^
+found-req-variance.scala:68: error: type mismatch;
+ found : FF2[B,C]
+ required: FF2[B,B]
+Note: C <: B, but trait FF2 is invariant in type R.
+You may wish to define R as +R instead. (SLS 4.5)
+ def ff8 = f[B, B](h[B, C]) // suggest
+ ^
+found-req-variance.scala:69: error: type mismatch;
+ found : FF2[C,C]
+ required: FF2[B,B]
+ def ff9 = f[B, B](h[C, C]) // fail
+ ^
+found-req-variance.scala:86: error: type mismatch;
+ found : java.util.ArrayList[String]
+ required: java.util.List[AnyRef]
+Note: String <: AnyRef, but Java-defined trait List is invariant in type E.
+You may wish to investigate a wildcard type such as `_ <: AnyRef`. (SLS 3.2.10)
+ def g1 = f[AnyRef](new java.util.ArrayList[String] { })
+ ^
+found-req-variance.scala:87: error: type mismatch;
+ found : scala.math.Ordering[AnyRef]
+ required: java.util.Comparator[String]
+Note: AnyRef >: String, but Java-defined trait Comparator is invariant in type T.
+You may wish to investigate a wildcard type such as `_ >: String`. (SLS 3.2.10)
+ def g2 = g[String](Ordering.fromLessThan[AnyRef](_.toString < _.toString))
+ ^
+found-req-variance.scala:94: error: type mismatch;
+ found : Misc.MyData
+ required: Misc.Data[AnyVal]
+Note: Int <: AnyVal (and Misc.MyData <: Misc.Data[Int]), but class Data is invariant in type A.
+You may wish to define A as +A instead. (SLS 4.5)
+ def f1 = Set[Data[AnyVal]]() + new MyData
+ ^
+found-req-variance.scala:100: error: type mismatch;
+ found : Set[String]
+ required: Set[java.lang.CharSequence]
+Note: String <: java.lang.CharSequence, but trait Set is invariant in type A.
+You may wish to investigate a wildcard type such as `_ <: java.lang.CharSequence`. (SLS 3.2.10)
+ foo(s)
+ ^
+found-req-variance.scala:104: error: type mismatch;
+ found : Misc.Trippy[String,String,String]
+ required: Misc.Trippy[java.lang.Object,java.lang.Object,java.lang.Object]
+Note: String <: java.lang.Object, but class Trippy is invariant in type T2.
+You may wish to define T2 as +T2 instead. (SLS 4.5)
+ def g1 = Set[Trippy[AnyRef, AnyRef, AnyRef]]() + new Trippy[String, String, String]
+ ^
+found-req-variance.scala:105: error: type mismatch;
+ found : scala.collection.immutable.Map[AnyRef,String]
+ required: Map[String,String]
+Note: AnyRef >: String, but trait Map is invariant in type A.
+You may wish to investigate a wildcard type such as `_ >: String`. (SLS 3.2.10)
+ def g2 = Set[Map[String, String]]() + Map[AnyRef, String]()
+ ^
+28 errors found
diff --git a/test/files/neg/found-req-variance.scala b/test/files/neg/found-req-variance.scala
new file mode 100644
index 0000000000..c44d021fac
--- /dev/null
+++ b/test/files/neg/found-req-variance.scala
@@ -0,0 +1,106 @@
+import scala.collection.mutable.ListBuffer
+
+class A
+class B extends A
+class C extends B
+
+trait FF1[T, +R]
+trait FF2[-T, R]
+
+class Inv[T]
+class InvA extends Inv[A]
+class InvB extends Inv[B]
+class InvC extends Inv[C]
+
+class Multi[+Cov, Inv, -Con]
+class MultiCov[+T <: A] extends Multi[T, B, C]
+class MultiInv[T] extends Multi[A, T, C]
+class MultiCon[-T >: C] extends Multi[A, B, T]
+
+object Test {
+ def f1 = Set[Inv[A]]() + new Inv[A]
+ def f2 = Set[Inv[A]]() + new Inv[B]
+ def f3 = Set[Inv[A]]() + new Inv[C]
+ def f4 = Set[Inv[B]]() + new Inv[A]
+ def f5 = Set[Inv[B]]() + new Inv[B]
+ def f6 = Set[Inv[B]]() + new Inv[C]
+ def f7 = Set[Inv[C]]() + new Inv[A]
+ def f8 = Set[Inv[C]]() + new Inv[B]
+ def f9 = Set[Inv[C]]() + new Inv[C]
+
+ def g1 = Set[Multi[A, B, C]]() + new MultiCov[A]
+ def g2 = Set[Multi[A, B, C]]() + new MultiCov[B]
+ def g3 = Set[Multi[A, B, C]]() + new MultiCov[C]
+ def g4 = Set[Multi[A, B, C]]() + new MultiInv[A]
+ def g5 = Set[Multi[A, B, C]]() + new MultiInv[B]
+ def g6 = Set[Multi[A, B, C]]() + new MultiInv[C]
+ def g7 = Set[Multi[A, B, C]]() + new MultiCon[A]
+ def g8 = Set[Multi[A, B, C]]() + new MultiCon[B]
+ def g9 = Set[Multi[A, B, C]]() + new MultiCon[C]
+}
+
+object Functions {
+ object Set1 {
+ def f[T, R](x: FF1[T, R]) = ()
+ def h[T, R] : FF1[T, R] = system.error("")
+
+ def ff1 = f[B, B](h[A, A]) // fail
+ def ff2 = f[B, B](h[B, A]) // fail
+ def ff3 = f[B, B](h[C, A]) // fail
+ def ff4 = f[B, B](h[A, B]) // suggest
+ def ff5 = f[B, B](h[B, B]) // ok
+ def ff6 = f[B, B](h[C, B]) // suggest
+ def ff7 = f[B, B](h[A, C]) // suggest
+ def ff8 = f[B, B](h[B, C]) // ok
+ def ff9 = f[B, B](h[C, C]) // suggest
+ }
+ object Set2 {
+ def f[T, R](x: FF2[T, R]) = ()
+ def h[T, R] : FF2[T, R] = system.error("")
+
+ def ff1 = f[B, B](h[A, A]) // suggest
+ def ff2 = f[B, B](h[B, A]) // suggest
+ def ff3 = f[B, B](h[C, A]) // fail
+ def ff4 = f[B, B](h[A, B]) // ok
+ def ff5 = f[B, B](h[B, B]) // ok
+ def ff6 = f[B, B](h[C, B]) // fail
+ def ff7 = f[B, B](h[A, C]) // suggest
+ def ff8 = f[B, B](h[B, C]) // suggest
+ def ff9 = f[B, B](h[C, C]) // fail
+ }
+}
+
+// TODO
+// object TypeAlias {
+// type LL[T] = List[T]
+// val LL = List
+//
+// def f1 = Set[LL[B]]() + LL[A](new A)
+// def f2 = Set[LL[B]]() + LL[C](new C)
+// }
+
+object Javas {
+ def f[T](x: java.util.List[T]) = ()
+ def g[T](x: java.util.Comparator[T]) = ()
+
+ def g1 = f[AnyRef](new java.util.ArrayList[String] { })
+ def g2 = g[String](Ordering.fromLessThan[AnyRef](_.toString < _.toString))
+}
+
+object Misc {
+ // original motivation
+ class Data[A <: AnyVal]
+ class MyData extends Data[Int] { }
+ def f1 = Set[Data[AnyVal]]() + new MyData
+
+ // from stackoverflow
+ def foo(s: Set[CharSequence]): Unit = ()
+ def f4 = {
+ val s: Set[String] = Set("Hello", "World");
+ foo(s)
+ }
+
+ class Trippy[+T1, T2, +T3]
+ def g1 = Set[Trippy[AnyRef, AnyRef, AnyRef]]() + new Trippy[String, String, String]
+ def g2 = Set[Map[String, String]]() + Map[AnyRef, String]()
+} \ No newline at end of file