Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Critical Critical
    • Resolution: Fixed
    • Affects Version/s: Scala 2.9.0
    • Fix Version/s: Scala 2.10.0-M3, Scala 2.10.0
    • Component/s: Type Checker
    • Labels:
    • Environment:

      Mac OS X 10.6.7
      java version "1.6.0_24"
      Java(TM) SE Runtime Environment (build 1.6.0_24-b07-334-10M3326)
      Java HotSpot(TM) 64-Bit Server VM (build 19.1-b02-334, mixed mode)

      Description

      After a discussion on #scala I put together a simple little expression evaluator using case classes:

      https://gist.github.com/1094143

      Function "eval" is correct, while function "badEval" is not, as it does not eval the input value to the function before passing it, meaning a Term[A] is being passed instead of an A.

      I have tried running this gist using both Scala 2.9.0-1 and also the current trunk of scala/scala on github (commit e70f043d15a7ad759074), both throw a runtime exception when the gist is put through the scala REPL:

      $ ./build/pack/bin/scala ~/GADT.scala
      eval gives:25
      java.lang.ClassCastException: Main$$anon$1$Val cannot be cast to java.lang.Integer
      at scala.runtime.BoxesRunTime.unboxToInt(Unknown Source)
      at Main$$anon$1$$anonfun$1.apply(GADT.scala:22)
      at Main$$anon$1.badEval(GADT.scala:18)
      at Main$$anon$1.<init>(GADT.scala:25)
      at Main$.main(GADT.scala:1)
      at Main.main(GADT.scala)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
      at java.lang.reflect.Method.invoke(Method.java:597)
      at scala.tools.nsc.util.ScalaClassLoader$$anonfun$run$1.apply(ScalaClassLoader.scala:78)
      at scala.tools.nsc.util.ScalaClassLoader$class.asContext(ScalaClassLoader.scala:24)
      at scala.tools.nsc.util.ScalaClassLoader$URLClassLoader.asContext(ScalaClassLoader.scala:88)
      at scala.tools.nsc.util.ScalaClassLoader$class.run(ScalaClassLoader.scala:78)
      at scala.tools.nsc.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:101)
      at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:33)
      at scala.tools.nsc.ObjectRunner$.runAndCatch(ObjectRunner.scala:40)
      at scala.tools.nsc.ScriptRunner.scala$tools$nsc$ScriptRunner$$runCompiled(ScriptRunner.scala:171)
      at scala.tools.nsc.ScriptRunner$$anonfun$runScript$1.apply(ScriptRunner.scala:188)
      at scala.tools.nsc.ScriptRunner$$anonfun$runScript$1.apply(ScriptRunner.scala:188)
      at scala.tools.nsc.ScriptRunner$$anonfun$withCompiledScript$1$$anonfun$apply$mcZ$sp$1.apply(ScriptRunner.scala:157)
      at scala.tools.nsc.ScriptRunner$$anonfun$withCompiledScript$1$$anonfun$apply$mcZ$sp$1.apply(ScriptRunner.scala:157)
      at scala.Option.exists(Option.scala:188)
      at scala.tools.nsc.ScriptRunner$$anonfun$withCompiledScript$1.apply$mcZ$sp(ScriptRunner.scala:157)
      at scala.tools.nsc.ScriptRunner$$anonfun$withCompiledScript$1.apply(ScriptRunner.scala:131)
      at scala.tools.nsc.ScriptRunner$$anonfun$withCompiledScript$1.apply(ScriptRunner.scala:131)
      at scala.tools.nsc.util.package$.waitingForThreads(package.scala:34)
      at scala.tools.nsc.ScriptRunner.withCompiledScript(ScriptRunner.scala:130)
      at scala.tools.nsc.ScriptRunner.runScript(ScriptRunner.scala:188)
      at scala.tools.nsc.ScriptRunner.runScriptAndCatch(ScriptRunner.scala:201)
      at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:58)
      at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:80)
      at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:89)
      at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)

        Activity

        Hide
        Seth Tisue added a comment - - edited

        minimized (I think):

        trait Term[A]
        case class Fn[A, B](f: A => B) extends Term[A => B]
        case class Apply[A, B](fn: Term[A => B]) extends Term[B]
        (Apply(Fn(identity[String])): Term[String]) match {
          case Apply(Fn(f)) => f(()) }
        

        note that it doesn't matter what you pass to f. here I pass (), but you can pass anything at all and the compiler swallows it without complaining (!!!)

        Show
        Seth Tisue added a comment - - edited minimized (I think): trait Term[A] case class Fn[A, B](f: A => B) extends Term[A => B] case class Apply[A, B](fn: Term[A => B]) extends Term[B] (Apply(Fn(identity[String])): Term[String]) match { case Apply(Fn(f)) => f(()) } note that it doesn't matter what you pass to f. here I pass (), but you can pass anything at all and the compiler swallows it without complaining (!!!)
        Hide
        Paul Phillips added a comment -

        Mmm, that's pretty spectacularly broken. It's been like that forever too.

        Simpler reproduction:

        scala> case class Fn[A, B](f: A => B) ; def f(x: Any) = x match { case Fn(f) => f(5) } ; f(Fn((x: String) => x))
        java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
        	at $anonfun$1.apply(<console>:12)
        	at .f(<console>:10)
        	at .<init>(<console>:12)
        

        I think the inferred type should be Fn[Nothing => Any] and instead is inferring Fn[Any => Any], which of course means it's telling itself that it can be applied to anything. The contravariant parameter is not flipping the flippy bits.

        Adriaan, what do you think?

        Show
        Paul Phillips added a comment - Mmm, that's pretty spectacularly broken. It's been like that forever too. Simpler reproduction: scala> case class Fn[A, B](f: A => B) ; def f(x: Any) = x match { case Fn(f) => f(5) } ; f(Fn((x: String) => x)) java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String at $anonfun$1.apply(<console>:12) at .f(<console>:10) at .<init>(<console>:12) I think the inferred type should be Fn [Nothing => Any] and instead is inferring Fn [Any => Any] , which of course means it's telling itself that it can be applied to anything. The contravariant parameter is not flipping the flippy bits. Adriaan, what do you think?
        Hide
        Jason Zaugg added a comment -
        Show
        Jason Zaugg added a comment - Fixed in 2.10.0-M3: https://github.com/scala/scala/pull/626

          People

          • Assignee:
            Adriaan Moors
            Reporter:
            Iain McGinniss
          • Votes:
            1 Vote for this issue
            Watchers:
            6 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development