Added sanity check to lub calculation to prevent invalid lubs from
emerging. The underlying cause of said lubs is that higher-order type parameters are not handled correctly: this is why the issue is seen so frequently in the collections. See pending test pending/pos/those-kinds-are-high.scala for a demonstration. Until that's fixed, we can at least raise the bar a bit. Closes #2094, #2322, #4501. Also, some test cases in neg have been promoted into working programs: #2179, #3774. (They're not in neg for the "shouldn't work" reason, but out of despair.) In some cases, such as the original reported ticket in #3528, this only pushes the problem downfield: it still fails due to inferred type parameters not conforming to bounds. I believe a similar issue with higher-order type parameters underlies that. Look at how far this takes us though. All kinds of stuff which did not work, now works. None of these even compiled until now: scala> :type List(mutable.Map(1 -> 1), immutable.Map(1 -> 1)) List[scala.collection.Map[Int,Int]] scala> :type Set(List(1), mutable.Map(1 -> 1)) scala.collection.Set[Iterable[Any] with PartialFunction[Int,Int]] scala> :type Stream(List(1), Set(1), 1 to 5) Stream[Iterable[Int] with Int => AnyVal{def getClass(): Class[_ >: Int with Boolean <: AnyVal]}] scala> :type Map(1 -> (1 to 10), 2 -> (1 to 10).toList) scala.collection.immutable.Map[Int,scala.collection.immutable.Seq[Int]] PERFORMANCE: compiling quick.lib and quick.comp, this patch results in an extra 27 subtype tests. Total. Time difference too small to measure. However to be on the safe side I made it really easy to disable. private final val verifyLubs = true // set to false Review by moors, odersky. git-svn-id: http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk@25149 5e8d7ff9-d8ef-0310-90f0-a4852d11357a
This commit is contained in:
parent
5701bdac97
commit
c1698844e8
|
@ -90,6 +90,8 @@ trait Types /*extends reflect.generic.Types*/ { self: SymbolTable =>
|
|||
private final def decr(depth: Int) = if (depth == AnyDepth) AnyDepth else depth - 1
|
||||
|
||||
private final val printLubs = false
|
||||
/** In case anyone wants to turn off lub verification without reverting anything. */
|
||||
private final val verifyLubs = true
|
||||
|
||||
/** The current skolemization level, needed for the algorithms
|
||||
* in isSameType, isSubType that do constraint solving under a prefix.
|
||||
|
@ -5309,10 +5311,26 @@ A type's typeSymbol should never be inspected directly.
|
|||
case ex: NoCommonType =>
|
||||
}
|
||||
}
|
||||
if (lubRefined.decls.isEmpty) lubBase
|
||||
if (lubRefined.decls.isEmpty) lubBase
|
||||
else if (!verifyLubs) lubRefined
|
||||
else {
|
||||
// println("refined lub of "+ts+"/"+narrowts+" is "+lubRefined+", baseclasses = "+(ts map (_.baseTypeSeq) map (_.toList)))
|
||||
lubRefined
|
||||
// Verify that every given type conforms to the calculated lub.
|
||||
// In theory this should not be necessary, but higher-order type
|
||||
// parameters are not handled correctly.
|
||||
val ok = ts forall { t =>
|
||||
(t <:< lubRefined) || {
|
||||
if (settings.debug.value) {
|
||||
Console.println(
|
||||
"Malformed lub: " + lubRefined + "\n" +
|
||||
"Argument " + t + " does not conform. Falling back to " + lubBase
|
||||
)
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
// If not, fall back on the more conservative calculation.
|
||||
if (ok) lubRefined
|
||||
else lubBase
|
||||
}
|
||||
}
|
||||
existentialAbstraction(tparams, lubType)
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
t2179.scala:2: error: inferred type arguments [scala.collection.immutable.Seq[Double]{def companion: scala.collection.generic.GenericCompanion[scala.collection.immutable.Seq[Any]]; protected def thisCollection: Seq[Double]{def companion: scala.collection.generic.GenericCompanion[Seq[Any]]}}] do not conform to method reduceLeft's type parameter bounds [B >: List[Double]]
|
||||
(Nil:List[List[Double]]).reduceLeft((_: Any, _: Any) => Nil.indices.map(_ => 0d))
|
||||
^
|
||||
t2179.scala:2: error: type mismatch;
|
||||
found : (Any, Any) => scala.collection.immutable.IndexedSeq[Double]
|
||||
required: (scala.collection.immutable.Seq[Double]{def companion: scala.collection.generic.GenericCompanion[scala.collection.immutable.Seq[Any]]; protected def thisCollection: Seq[Double]{def companion: scala.collection.generic.GenericCompanion[Seq[Any]]}}, List[Double]) => scala.collection.immutable.Seq[Double]{def companion: scala.collection.generic.GenericCompanion[scala.collection.immutable.Seq[Any]]; protected def thisCollection: Seq[Double]{def companion: scala.collection.generic.GenericCompanion[Seq[Any]]}}
|
||||
(Nil:List[List[Double]]).reduceLeft((_: Any, _: Any) => Nil.indices.map(_ => 0d))
|
||||
^
|
||||
two errors found
|
|
@ -1,7 +0,0 @@
|
|||
t3774.scala:4: error: overloaded method value ++ with alternatives:
|
||||
[B1 >: List[Int]](xs: scala.collection.GenTraversableOnce[((Int, Int), B1)])scala.collection.immutable.Map[(Int, Int),B1] <and>
|
||||
[B >: ((Int, Int), List[Int]), That](that: scala.collection.GenTraversableOnce[B])(implicit bf: scala.collection.generic.CanBuildFrom[scala.collection.immutable.Map[(Int, Int),List[Int]],B,That])That
|
||||
cannot be applied to (scala.collection.immutable.IndexedSeq[((Int, Int), scala.collection.immutable.Range.Inclusive)])
|
||||
Map[(Int,Int),List[Int]]() ++ (for(x <- 0 to 1 ; y <- 0 to 1) yield {(x,y)-> (0 to 1)})
|
||||
^
|
||||
one error found
|
|
@ -0,0 +1,31 @@
|
|||
object Test extends App {
|
||||
// compiles:
|
||||
Map[Int, Value](
|
||||
0 -> KnownType(classOf[Object]),
|
||||
1 -> UnknownValue())
|
||||
|
||||
// does not compile:
|
||||
Map(
|
||||
0 -> KnownType(classOf[Object]),
|
||||
1 -> UnknownValue())
|
||||
|
||||
// Experiment.scala:10: error: type mismatch;
|
||||
// found : (Int, KnownType)
|
||||
// required: (Int, Product with Value{def getType: Option[java.lang.Class[_$$2]]}) where type _$$2
|
||||
// 0 -> KnownType(classOf[Object]),
|
||||
// ^
|
||||
// one error found
|
||||
}
|
||||
sealed trait Value {
|
||||
def getType: Option[Class[_]]
|
||||
}
|
||||
|
||||
case class UnknownValue() extends Value {
|
||||
def getType = None
|
||||
// compiles if changed to:
|
||||
// def getType: Option[Class[_]] = None
|
||||
}
|
||||
|
||||
case class KnownType(typ: Class[_]) extends Value {
|
||||
def getType = Some(typ)
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
class A {
|
||||
// 3528 - not fixed
|
||||
// def f1 = List(List(1), Stream(1))
|
||||
// 3528 comments
|
||||
def f2 = List(Set(1,2,3), List(1,2,3))
|
||||
// 2322
|
||||
def f3 = List(null: Range, null: List[Int])
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// After lub modification
|
||||
import scala.collection.mutable.ListBuffer
|
||||
|
||||
class A {
|
||||
def foo[T](a:T, b:T):T = a
|
||||
def f1 = foo(ListBuffer(), List())
|
||||
def f2 = foo(ListBuffer(), ListBuffer())
|
||||
def f3 = foo(List(), List())
|
||||
|
||||
// scalap
|
||||
// def f1 : scala.collection.Seq[scala.Nothing] = { /* compiled code */ }
|
||||
// def f2 : scala.collection.mutable.ListBuffer[scala.Nothing] = { /* compiled code */ }
|
||||
// def f3 : scala.collection.immutable.List[scala.Nothing] = { /* compiled code */ }
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
class A {
|
||||
trait Container[+T]
|
||||
trait Template[+CC[X] <: Container[X]]
|
||||
|
||||
class C1[T] extends Template[C1] with Container[T]
|
||||
class C2[T] extends Template[C2] with Container[T]
|
||||
|
||||
/** Target expression:
|
||||
* List(new C1[String], new C2[String])
|
||||
*/
|
||||
|
||||
// Here's what would ideally be inferred.
|
||||
//
|
||||
// scala> :type List[Template[Container] with Container[String]](new C1[String], new C2[String])
|
||||
// List[Template[Container] with Container[java.lang.String]]
|
||||
//
|
||||
// Here's what it does infer.
|
||||
//
|
||||
// scala> :type List(new C1[String], new C2[String])
|
||||
// <console>:8: error: type mismatch;
|
||||
// found : C1[String]
|
||||
// required: Container[String] with Template[Container[Any] with Template[Container[Any] with Template[Any] with ScalaObject] with ScalaObject] with ScalaObject
|
||||
// List(new C1[String], new C2[String])
|
||||
// ^
|
||||
//
|
||||
// Simplified, the inferred type is:
|
||||
//
|
||||
// List[Container[String] with Template[Container[Any] with Template[Container[Any] with Template[Any]]]
|
||||
//
|
||||
|
||||
/** Working version explicitly typed.
|
||||
*/
|
||||
def fExplicit = List[Template[Container] with Container[String]](new C1[String], new C2[String])
|
||||
|
||||
// nope
|
||||
// def fFail = List(new C1[String], new C2[String])
|
||||
}
|
Loading…
Reference in New Issue