Scala Programming Language
  1. Scala Programming Language
  2. SI-1697

Compiler generates wrong code from some code using extractor

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Critical Critical
    • Resolution: Fixed
    • Affects Version/s: None
    • Fix Version/s: Scala 2.10.0
    • Component/s: Pattern Matcher
    • Labels:
      None
    • Environment:

      extractor

      Description

      I wrote following code and compiled it(scala 2.7.3.final).

      case class Num(n : scala.Int) extends Term
      case class Add(x : Term,y : Term) extends Term
      object Value {
        def unapply(term : Term) : Boolean =
          term match {
            case Num(_) => true
            case Add(_,_) => false
          }
      }
      
      object App {
        def main(args: Array[String]) {
          val term = Add(Num(1), Add(Num(2),Num(3)))
          term match {
            case Add(Num(x),Num(y)) =>
              println("Add(Num, Num)")
            case Add(Value(),y) => // <- not match!
              println("Add(Value, ?)")
            case _ =>
              println("?")
          }
        }
      }
      

      I expects that "Add(Value, ?)" is displayed , but "?" is displayed.
      Diassembling the compiled code, It seems that the compiler generates the code that skips pattern "Add(Value(),y)" when pattern "Num" fails.

        Issue Links

          Activity

          Hide
          Justin Wick added a comment -

          This issue appears to be solved in scalac as of scala-2.8.0.r18746-b20090923021937. fsc refuses to compile the code, however. Is that a bug?

          Also, I motion that someone create a script to make simple combinations of standalone code and expected output into a ParTest test.

          Show
          Justin Wick added a comment - This issue appears to be solved in scalac as of scala-2.8.0.r18746-b20090923021937. fsc refuses to compile the code, however. Is that a bug? Also, I motion that someone create a script to make simple combinations of standalone code and expected output into a ParTest test.
          Hide
          Paul Phillips added a comment -

          Replying to [comment:6 JustinWick]:
          > This issue appears to be solved in scalac as of scala-2.8.0.r18746-b20090923021937. fsc refuses to compile the code, however. Is that a bug?

          I don't know what refusal it is giving you but fsc works for me. The exact case given above I fixed in r18184 but the general issue is not fixed so I left this open.

          > Also, I motion that someone create a script to make simple combinations of standalone code and expected output into a ParTest test.

          I second the motion! All those in favor of assert(Someone == JustinWick)? The motion is carried unanimously!

          Show
          Paul Phillips added a comment - Replying to [comment:6 JustinWick] : > This issue appears to be solved in scalac as of scala-2.8.0.r18746-b20090923021937. fsc refuses to compile the code, however. Is that a bug? I don't know what refusal it is giving you but fsc works for me. The exact case given above I fixed in r18184 but the general issue is not fixed so I left this open. > Also, I motion that someone create a script to make simple combinations of standalone code and expected output into a ParTest test. I second the motion! All those in favor of assert(Someone == JustinWick)? The motion is carried unanimously!
          Hide
          Ingo Maier added a comment -

          Some of our students trapped into this issue. I am not sure it is the very same underlying issue, so here is another test case:

          abstract class Term
          case object Var extends Term
          case object Abs extends Term
          
          object Ex {
            def unapply(t: Term): Option[Term] = {
              println("unapply " + t)
              t match {
                case Abs => Some(t)
                case _ => None
              }
            }
          }
          
          object Test extends Application {
            (Abs, Var) match {
              case (Abs, Ex(v)) => println("Hm") // Comment this line out to get "Success"
              case (Ex(v), t) => println("Success")
              case (a, b) if Value.unapply(a) != None => println("Fail")
            }
          }
          
          
          Show
          Ingo Maier added a comment - Some of our students trapped into this issue. I am not sure it is the very same underlying issue, so here is another test case: abstract class Term case object Var extends Term case object Abs extends Term object Ex { def unapply(t: Term): Option[Term] = { println("unapply " + t) t match { case Abs => Some(t) case _ => None } } } object Test extends Application { (Abs, Var) match { case (Abs, Ex(v)) => println("Hm") // Comment this line out to get "Success" case (Ex(v), t) => println("Success") case (a, b) if Value.unapply(a) != None => println("Fail") } }
          Hide
          Simon Schäfer added a comment -

          I got also problems with extractors - but I used unapplySeq instead of unapply. Comment out the first case to get the expected output.

          object MyListTest extends App {
            val xs = MyList(1 to 5: _*)
            xs match {
          //    case MyList(head) => println("no") // works fine
              case head :: MyNil => println("no") // produces a MatchError
              case MyList(head, tail @ _*) => println("yay")
            }
          }
          
          object MyList {
            def apply[A](a: A*): MyList[A] =
              (a :\ empty[A]) { _ :: _ }
              
            def empty[A]: MyList[A] = MyNil
            
            def unapplySeq[A](a: MyList[A]): Option[Seq[A]] = {
              var xs: List[A] = Nil
              var ys = a
              while (!ys.isEmpty) {
                xs = ys.head :: xs
                ys = ys.tail
              }
              Some(xs.reverse)
            }
          }
          
          abstract class MyList[+A] {
            def head: A
            def tail: MyList[A]
            def isEmpty: Boolean
            
            def :: [B >: A](b: B): MyList[B] =
              new ::(b, this)
          }
          
          case class :: [A](head: A, tail: MyList[A]) extends MyList[A] {
            def isEmpty = false
          }
          
          case object MyNil extends MyList[Nothing] {
            def head = throw new UnsupportedOperationException("nil head")
            def tail = throw new UnsupportedOperationException("nil tail")
            def isEmpty = true
          }
          
          Show
          Simon Schäfer added a comment - I got also problems with extractors - but I used unapplySeq instead of unapply. Comment out the first case to get the expected output. object MyListTest extends App { val xs = MyList(1 to 5: _*) xs match { // case MyList(head) => println("no") // works fine case head :: MyNil => println("no") // produces a MatchError case MyList(head, tail @ _*) => println("yay") } } object MyList { def apply[A](a: A*): MyList[A] = (a :\ empty[A]) { _ :: _ } def empty[A]: MyList[A] = MyNil def unapplySeq[A](a: MyList[A]): Option[Seq[A]] = { var xs: List[A] = Nil var ys = a while (!ys.isEmpty) { xs = ys.head :: xs ys = ys.tail } Some(xs.reverse) } } abstract class MyList[+A] { def head: A def tail: MyList[A] def isEmpty: Boolean def :: [B >: A](b: B): MyList[B] = new ::(b, this) } case class :: [A](head: A, tail: MyList[A]) extends MyList[A] { def isEmpty = false } case object MyNil extends MyList[Nothing] { def head = throw new UnsupportedOperationException("nil head") def tail = throw new UnsupportedOperationException("nil tail") def isEmpty = true }
          Hide
          Commit Message Bot added a comment -

          (moors in r25966) smarter bridges to unapplies

          wraps the call to a bridged synthetic unapply(Seq) in a defensive if-test:

          if (x.isInstanceOf[expectedType]) real.unapply(x.asInstanceOf[expectedType]) else None

          NOTE: the test is WRONG, but it has to be due to #1697/#2337 – once those are fixed, this one should generate the expected output

          Show
          Commit Message Bot added a comment - (moors in r25966 ) smarter bridges to unapplies wraps the call to a bridged synthetic unapply(Seq) in a defensive if-test: if (x.isInstanceOf [expectedType] ) real.unapply(x.asInstanceOf [expectedType] ) else None NOTE: the test is WRONG, but it has to be due to #1697/#2337 – once those are fixed, this one should generate the expected output
          Hide
          Paul Phillips added a comment -

          virtpatmat saves day, test in 8bc8b83f0b .

          Show
          Paul Phillips added a comment - virtpatmat saves day, test in 8bc8b83f0b .

            People

            • Assignee:
              Adriaan Moors
              Reporter:
              Kota Mizushima
              TracCC:
              Ismael Juma, Johannes Rudolph, jpalecek, Justin Wick, Paul Phillips, Seth Tisue, Simon Ochsenreither
            • Votes:
              1 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Development