Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upUnsoundness bug in pattern matcher when pattern reveals existentials #6680
Comments
This comment has been minimized.
This comment has been minimized.
Imported From: https://issues.scala-lang.org/browse/SI-6680?orig=1 |
This comment has been minimized.
This comment has been minimized.
@retronym said: |
This comment has been minimized.
This comment has been minimized.
@adriaanm said (edited on Nov 17, 2012 10:17:34 PM UTC): trait Super[+A]
// `Hidden` must occur in both variance positions (covariant/contravariant) for the sneakiness to work
// this way type inference will infer Any for `Hidden` and `A` in the pattern below
case class Concrete[Hidden, +A](havoc: Hidden => Hidden) extends Super[A]
object Test extends App {
(Concrete((x: Int) => x): Super[Any]) match {
case Concrete(f) => f("not what you'd expect")
}
} |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@pchiusano said: |
This comment has been minimized.
This comment has been minimized.
@adriaanm said: |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@adriaanm said: |
This comment has been minimized.
This comment has been minimized.
@paulp said: case class Cell[A](var x: A)
object Test {
def f(x: Any) = x match { case y @ Cell(_) => y } // Inferred type is Cell[Any]
def g(x: Any) = x match { case y: Cell[_] => y } // This one infers Cell[_] as the other one should
def main(args: Array[String]): Unit = {
val x = new Cell(1)
val y = f(x)
y.x = "abc"
println(x.x + 1)
}
}
/***
% scala Test
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at scala.runtime.BoxesRunTime.unboxToInt(BoxesRunTime.java:105)
at Test$.main(a.scala:12)
at Test.main(a.scala)
***/ |
This comment has been minimized.
This comment has been minimized.
@paulp said: case class Concrete[A](havoc: A => A)
object Test extends App {
(Concrete((x: Int) => x): Any) match { case Concrete(f) => f("not what you'd expect") }
} |
This comment has been minimized.
This comment has been minimized.
Stephen Compall (s11001001) said: trait AO[A];case class AOA[A,B](f:A=>B,e:A=>A)extends AO[B]
def unsfeCoerce[A,B]=(AOA[B,B](a=>a,a=>a):AO[B])match{case AOA(id,_)=>id:(A=>B)} |
This comment has been minimized.
This comment has been minimized.
@paulp said: |
This comment has been minimized.
This comment has been minimized.
@paulp said: case class C[A](f:A=>A);def f(x:Any)=x match { case C(f)=>f("") };f(C[Int](x=>x)) |
This comment has been minimized.
This comment has been minimized.
@retronym said: This is only enabled under -Xstrict-inference as the unsoundness is widely exploited. I'll leave this ticket open to track that. But I'd be interested in Paul C. and Stephen's experiences with that flag. |
This comment has been minimized.
This comment has been minimized.
Stephen Compall (s11001001) said: I've found #7990 and #7991 in some initial experiments, though. I tried to build scalaz-seven core head against M7 with that option, and I believe #7991 would fix almost all of the resulting errors, if not all. I also imagine Paul C would prefer #7990 just be an error right out. |
This comment has been minimized.
This comment has been minimized.
Stephen Compall (s11001001) said: |
This comment has been minimized.
This comment has been minimized.
@adriaanm said: |
This comment has been minimized.
This comment has been minimized.
@retronym said: In the process I've spotted implementation issues with |
This comment has been minimized.
This comment has been minimized.
@paulp said: |
This comment has been minimized.
This comment has been minimized.
Stephen Compall (s11001001) said: |
scabug commentedNov 17, 2012
Scala seems to refine all existential type variables to Any when pattern matching, which is unsound and can result in runtime errors. Here's an example data type:
Now take a look at an example usage which breaks. If I have a Stream[Int], and I match and obtain an Unfold(s, f), the type of (s,f) should be (S, S => Option[(A,S)]) forSome { type S }. But Scala just promotes everything to Any:
Notice that the type of s is Any. Likewise, the type of f is also wrong, it accepts an Any:
Since f expects Any, we can give it a String and get a runtime error:
I am using Scala 2.10.0-RC2, which, I understand has the new virtual pattern matcher turned on by default (I tried passing -Yvirtpatmat to the compiler but the option was not recognized.)
I find that this situation comes up a lot when writing libraries in Scala. It is extremely common to have a data type describing computations of some sort, and a separate interpreter which pattern matches to evaluate these computations for some semantics. The data type will frequently have existential type variables like this, and this bug means that the interpreter of the DSL is not typechecked!
Incidentally, the expression 'blah match { case Unfold(s,f) => f }' should trigger a type error due to an escaping skolem (rigid) type variable. Here's Haskell by comparison:
This yields: