Details

    • Type: Bug
    • Status: CLOSED
    • Priority: 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)

        Attachments

          Activity

          Hide
          sethtisue 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
          sethtisue 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
          extempore 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
          extempore 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
          retronym Jason Zaugg added a comment -
          Show
          retronym Jason Zaugg added a comment - Fixed in 2.10.0-M3: https://github.com/scala/scala/pull/626

            People

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

              Dates

              • Created:
                Updated:
                Resolved: