Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Specialized pattern matches don't eliminate impossible cases under virtpatmat #7172

Open
scabug opened this issue Feb 22, 2013 · 15 comments
Open

Comments

@scabug
Copy link

@scabug scabug commented Feb 22, 2013

Considering test/files/specialized/spec-patmatch.scala / scala/scala@b94c6e0

class Foo[@specialized A] {
  def test(x: A) = println(x match {
   case _: Boolean => "bool"
   case _: Byte => "byte"
   case _: Short => "short"
   case _: Char => "char"
   case i: Int => "int"
   case l: Long => "long"
   case d: Double => "double"
   case e: Float => "float"
   case _ => "default"
 })
}

object Test {
 def test[@specialized A] (x: A) = println(x match {
   case _: Boolean => "bool"
   case _: Byte => "byte"
   case _: Short => "short"
   case _: Char => "char"
   case i: Int => "int"
   case l: Long => "long"
   case d: Double => "double"
   case e: Float => "float"
   case _ => "default"
 })

  def main(args: Array[String]) {
    test(true)
    test(42.toByte)
    test(42.toShort)
    test('b')
    test(42)
    test(42l)
    test(42.0)
    test(42.0f)
    test(new Object)

    println("object instantiations:")
    (new Foo).test(true)
    (new Foo).test(42.toByte)
    (new Foo).test(42.toShort)
    (new Foo).test('b')
    (new Foo).test(42)
    (new Foo).test(42l)
    (new Foo).test(42.0)
    (new Foo).test(42.0f)
    (new Foo).test(new Object)
    
    //println(runtime.BoxesRunTime.integerBoxCount)
  }

}

-Xoldpatmat

[[syntax trees at end of                specialize]] // spec-patmatch.scala
package <empty> {
  class Foo[@specialized A >: Nothing <: Any] extends Object {
    def <init>(): Foo[A] = {
      Foo.super.<init>();
      ()
    };
    def test(x: A): Unit = scala.this.Predef.println(x match {
      case (_: Boolean) => "bool"
      case (_: Byte) => "byte"
      case (_: Short) => "short"
      case (_: Char) => "char"
      case (i @ (_: Int)) => "int"
      case (l @ (_: Long)) => "long"
      case (d @ (_: Double)) => "double"
      case (e @ (_: Float)) => "float"
      case _ => "default"
    });
    <specialized> def test$mcZ$sp(x: Boolean): Unit = Foo.this.test(x.asInstanceOf[A]());
    <specialized> def test$mcB$sp(x: Byte): Unit = Foo.this.test(x.asInstanceOf[A]());
    <specialized> def test$mcC$sp(x: Char): Unit = Foo.this.test(x.asInstanceOf[A]());
    <specialized> def test$mcD$sp(x: Double): Unit = Foo.this.test(x.asInstanceOf[A]());
    <specialized> def test$mcF$sp(x: Float): Unit = Foo.this.test(x.asInstanceOf[A]());
    <specialized> def test$mcI$sp(x: Int): Unit = Foo.this.test(x.asInstanceOf[A]());
    <specialized> def test$mcJ$sp(x: Long): Unit = Foo.this.test(x.asInstanceOf[A]());
    <specialized> def test$mcS$sp(x: Short): Unit = Foo.this.test(x.asInstanceOf[A]());
    <specialized> def test$mcV$sp(x: Unit): Unit = Foo.this.test(x.asInstanceOf[A]())
  };
  object Test extends Object {
    def <init>(): Test.type = {
      Test.super.<init>();
      ()
    };
    def test[@specialized A >: Nothing <: Any](x: A): Unit = scala.this.Predef.println(x match {
      case (_: Boolean) => "bool"
      case (_: Byte) => "byte"
      case (_: Short) => "short"
      case (_: Char) => "char"
      case (i @ (_: Int)) => "int"
      case (l @ (_: Long)) => "long"
      case (d @ (_: Double)) => "double"
      case (e @ (_: Float)) => "float"
      case _ => "default"
    });
    def main(args: Array[String]): Unit = {
      Test.this.test$mZc$sp(true);
      Test.this.test$mBc$sp(42.toByte());
      Test.this.test$mSc$sp(42.toShort());
      Test.this.test$mCc$sp('b');
      Test.this.test$mIc$sp(42);
      Test.this.test$mJc$sp(42L);
      Test.this.test$mDc$sp(42.0);
      Test.this.test$mFc$sp(42.0);
      Test.this.test[Object](new Object());
      scala.this.Predef.println("object instantiations:");
      new Foo$mcZ$sp().test$mcZ$sp(true);
      new Foo$mcB$sp().test$mcB$sp(42.toByte());
      new Foo$mcS$sp().test$mcS$sp(42.toShort());
      new Foo$mcC$sp().test$mcC$sp('b');
      new Foo$mcI$sp().test$mcI$sp(42);
      new Foo$mcJ$sp().test$mcJ$sp(42L);
      new Foo$mcD$sp().test$mcD$sp(42.0);
      new Foo$mcF$sp().test$mcF$sp(42.0);
      new Foo[Object]().test(new Object())
    };
    <specialized> def test$mZc$sp(x: Boolean): Unit = scala.this.Predef.println(x match {
      case (_: Boolean) => "bool"
      case _ => "default"
    });
    <specialized> def test$mBc$sp(x: Byte): Unit = scala.this.Predef.println(x match {
      case (_: Byte) => "byte"
      case _ => "default"
    });
    <specialized> def test$mCc$sp(x: Char): Unit = scala.this.Predef.println(x match {
      case (_: Char) => "char"
      case _ => "default"
    });
    <specialized> def test$mDc$sp(x: Double): Unit = scala.this.Predef.println(x match {
      case (d @ (_: Double)) => "double"
      case _ => "default"
    });
    <specialized> def test$mFc$sp(x: Float): Unit = scala.this.Predef.println(x match {
      case (e @ (_: Float)) => "float"
      case _ => "default"
    });
    <specialized> def test$mIc$sp(x: Int): Unit = scala.this.Predef.println(x match {
      case (i @ (_: Int)) => "int"
      case _ => "default"
    });
    <specialized> def test$mJc$sp(x: Long): Unit = scala.this.Predef.println(x match {
      case (l @ (_: Long)) => "long"
      case _ => "default"
    });
    <specialized> def test$mSc$sp(x: Short): Unit = scala.this.Predef.println(x match {
      case (_: Short) => "short"
      case _ => "default"
    });
    <specialized> def test$mVc$sp(x: Unit): Unit = scala.this.Predef.println(x match {
      case _ => "default"
    })
  };
  <specialized> class Foo$mcZ$sp extends Foo[Boolean] {
    <specialized> def <init>(): Foo$mcZ$sp = {
      Foo$mcZ$sp.super.<init>();
      ()
    };
    override <specialized> def test(x: Boolean): Unit = Foo$mcZ$sp.this.test$mcZ$sp(x);
    override <specialized> def test$mcZ$sp(x: Boolean): Unit = scala.this.Predef.println(x match {
      case (_: Boolean) => "bool"
      case _ => "default"
    })
  };
  <specialized> class Foo$mcB$sp extends Foo[Byte] {
    <specialized> def <init>(): Foo$mcB$sp = {
      Foo$mcB$sp.super.<init>();
      ()
    };
    override <specialized> def test(x: Byte): Unit = Foo$mcB$sp.this.test$mcB$sp(x);
    override <specialized> def test$mcB$sp(x: Byte): Unit = scala.this.Predef.println(x match {
      case (_: Byte) => "byte"
      case _ => "default"
    })
  };
  <specialized> class Foo$mcC$sp extends Foo[Char] {
    <specialized> def <init>(): Foo$mcC$sp = {
      Foo$mcC$sp.super.<init>();
      ()
    };
    override <specialized> def test(x: Char): Unit = Foo$mcC$sp.this.test$mcC$sp(x);
    override <specialized> def test$mcC$sp(x: Char): Unit = scala.this.Predef.println(x match {
      case (_: Char) => "char"
      case _ => "default"
    })
  };
  <specialized> class Foo$mcD$sp extends Foo[Double] {
    <specialized> def <init>(): Foo$mcD$sp = {
      Foo$mcD$sp.super.<init>();
      ()
    };
    override <specialized> def test(x: Double): Unit = Foo$mcD$sp.this.test$mcD$sp(x);
    override <specialized> def test$mcD$sp(x: Double): Unit = scala.this.Predef.println(x match {
      case (d @ (_: Double)) => "double"
      case _ => "default"
    })
  };
  <specialized> class Foo$mcF$sp extends Foo[Float] {
    <specialized> def <init>(): Foo$mcF$sp = {
      Foo$mcF$sp.super.<init>();
      ()
    };
    override <specialized> def test(x: Float): Unit = Foo$mcF$sp.this.test$mcF$sp(x);
    override <specialized> def test$mcF$sp(x: Float): Unit = scala.this.Predef.println(x match {
      case (e @ (_: Float)) => "float"
      case _ => "default"
    })
  };
  <specialized> class Foo$mcI$sp extends Foo[Int] {
    <specialized> def <init>(): Foo$mcI$sp = {
      Foo$mcI$sp.super.<init>();
      ()
    };
    override <specialized> def test(x: Int): Unit = Foo$mcI$sp.this.test$mcI$sp(x);
    override <specialized> def test$mcI$sp(x: Int): Unit = scala.this.Predef.println(x match {
      case (i @ (_: Int)) => "int"
      case _ => "default"
    })
  };
  <specialized> class Foo$mcJ$sp extends Foo[Long] {
    <specialized> def <init>(): Foo$mcJ$sp = {
      Foo$mcJ$sp.super.<init>();
      ()
    };
    override <specialized> def test(x: Long): Unit = Foo$mcJ$sp.this.test$mcJ$sp(x);
    override <specialized> def test$mcJ$sp(x: Long): Unit = scala.this.Predef.println(x match {
      case (l @ (_: Long)) => "long"
      case _ => "default"
    })
  };
  <specialized> class Foo$mcS$sp extends Foo[Short] {
    <specialized> def <init>(): Foo$mcS$sp = {
      Foo$mcS$sp.super.<init>();
      ()
    };
    override <specialized> def test(x: Short): Unit = Foo$mcS$sp.this.test$mcS$sp(x);
    override <specialized> def test$mcS$sp(x: Short): Unit = scala.this.Predef.println(x match {
      case (_: Short) => "short"
      case _ => "default"
    })
  };
  <specialized> class Foo$mcV$sp extends Foo[Unit] {
    <specialized> def <init>(): Foo$mcV$sp = {
      Foo$mcV$sp.super.<init>();
      ()
    };
    override <specialized> def test(x: Unit): Unit = Foo$mcV$sp.this.test$mcV$sp(x);
    override <specialized> def test$mcV$sp(x: Unit): Unit = scala.this.Predef.println(x match {
      case _ => "default"
    })
  }
}

With virtpatmat (in which Match nodes are eliminated before the specialize phase)

[[syntax trees at end of                specialize]] // spec-patmatch.scala
package <empty> {
  class Foo[@specialized A >: Nothing <: Any] extends Object {
    def <init>(): Foo[A] = {
      Foo.super.<init>();
      ()
    };
    def test(x: A): Unit = scala.this.Predef.println({
      case <synthetic> val x1: A = x;
      case12(){
        if (x1.isInstanceOf[Boolean]())
          matchEnd11("bool")
        else
          case13()
      };
      case13(){
        if (x1.isInstanceOf[Byte]())
          matchEnd11("byte")
        else
          case14()
      };
      case14(){
        if (x1.isInstanceOf[Short]())
          matchEnd11("short")
        else
          case15()
      };
      case15(){
        if (x1.isInstanceOf[Char]())
          matchEnd11("char")
        else
          case16()
      };
      case16(){
        if (x1.isInstanceOf[Int]())
          matchEnd11("int")
        else
          case17()
      };
      case17(){
        if (x1.isInstanceOf[Long]())
          matchEnd11("long")
        else
          case18()
      };
      case18(){
        if (x1.isInstanceOf[Double]())
          matchEnd11("double")
        else
          case19()
      };
      case19(){
        if (x1.isInstanceOf[Float]())
          matchEnd11("float")
        else
          case20()
      };
      case20(){
        matchEnd11("default")
      };
      matchEnd11(x: Any){
        x
      }
    });
    <specialized> def test$mcZ$sp(x: Boolean): Unit = Foo.this.test(x.asInstanceOf[A]());
    <specialized> def test$mcB$sp(x: Byte): Unit = Foo.this.test(x.asInstanceOf[A]());
    <specialized> def test$mcC$sp(x: Char): Unit = Foo.this.test(x.asInstanceOf[A]());
    <specialized> def test$mcD$sp(x: Double): Unit = Foo.this.test(x.asInstanceOf[A]());
    <specialized> def test$mcF$sp(x: Float): Unit = Foo.this.test(x.asInstanceOf[A]());
    <specialized> def test$mcI$sp(x: Int): Unit = Foo.this.test(x.asInstanceOf[A]());
    <specialized> def test$mcJ$sp(x: Long): Unit = Foo.this.test(x.asInstanceOf[A]());
    <specialized> def test$mcS$sp(x: Short): Unit = Foo.this.test(x.asInstanceOf[A]());
    <specialized> def test$mcV$sp(x: Unit): Unit = Foo.this.test(x.asInstanceOf[A]())
  };
  object Test extends Object {
    def <init>(): Test.type = {
      Test.super.<init>();
      ()
    };
    def test[@specialized A >: Nothing <: Any](x: A): Unit = scala.this.Predef.println({
      case <synthetic> val x1: A = x;
      case12(){
        if (x1.isInstanceOf[Boolean]())
          matchEnd11("bool")
        else
          case13()
      };
      case13(){
        if (x1.isInstanceOf[Byte]())
          matchEnd11("byte")
        else
          case14()
      };
      case14(){
        if (x1.isInstanceOf[Short]())
          matchEnd11("short")
        else
          case15()
      };
      case15(){
        if (x1.isInstanceOf[Char]())
          matchEnd11("char")
        else
          case16()
      };
      case16(){
        if (x1.isInstanceOf[Int]())
          matchEnd11("int")
        else
          case17()
      };
      case17(){
        if (x1.isInstanceOf[Long]())
          matchEnd11("long")
        else
          case18()
      };
      case18(){
        if (x1.isInstanceOf[Double]())
          matchEnd11("double")
        else
          case19()
      };
      case19(){
        if (x1.isInstanceOf[Float]())
          matchEnd11("float")
        else
          case20()
      };
      case20(){
        matchEnd11("default")
      };
      matchEnd11(x: Any){
        x
      }
    });
    def main(args: Array[String]): Unit = {
      Test.this.test$mZc$sp(true);
      Test.this.test$mBc$sp(42.toByte());
      Test.this.test$mSc$sp(42.toShort());
      Test.this.test$mCc$sp('b');
      Test.this.test$mIc$sp(42);
      Test.this.test$mJc$sp(42L);
      Test.this.test$mDc$sp(42.0);
      Test.this.test$mFc$sp(42.0);
      Test.this.test[Object](new Object());
      scala.this.Predef.println("object instantiations:");
      new Foo$mcZ$sp().test$mcZ$sp(true);
      new Foo$mcB$sp().test$mcB$sp(42.toByte());
      new Foo$mcS$sp().test$mcS$sp(42.toShort());
      new Foo$mcC$sp().test$mcC$sp('b');
      new Foo$mcI$sp().test$mcI$sp(42);
      new Foo$mcJ$sp().test$mcJ$sp(42L);
      new Foo$mcD$sp().test$mcD$sp(42.0);
      new Foo$mcF$sp().test$mcF$sp(42.0);
      new Foo[Object]().test(new Object())
    };
    <specialized> def test$mZc$sp(x: Boolean): Unit = scala.this.Predef.println({
      case <synthetic> val x1: Boolean = x;
      case12(){
        if (x1.isInstanceOf[Boolean]())
          matchEnd11("bool")
        else
          case13()
      };
      case13(){
        if (x1.isInstanceOf[Byte]())
          matchEnd11("byte")
        else
          case14()
      };
      case14(){
        if (x1.isInstanceOf[Short]())
          matchEnd11("short")
        else
          case15()
      };
      case15(){
        if (x1.isInstanceOf[Char]())
          matchEnd11("char")
        else
          case16()
      };
      case16(){
        if (x1.isInstanceOf[Int]())
          matchEnd11("int")
        else
          case17()
      };
      case17(){
        if (x1.isInstanceOf[Long]())
          matchEnd11("long")
        else
          case18()
      };
      case18(){
        if (x1.isInstanceOf[Double]())
          matchEnd11("double")
        else
          case19()
      };
      case19(){
        if (x1.isInstanceOf[Float]())
          matchEnd11("float")
        else
          case20()
      };
      case20(){
        matchEnd11("default")
      };
      matchEnd11(x: Any){
        x
      }
    });
    <specialized> def test$mBc$sp(x: Byte): Unit = scala.this.Predef.println({
      case <synthetic> val x1: Byte = x;
      case12(){
        if (x1.isInstanceOf[Boolean]())
          matchEnd11("bool")
        else
          case13()
      };
      case13(){
        if (x1.isInstanceOf[Byte]())
          matchEnd11("byte")
        else
          case14()
      };
      case14(){
        if (x1.isInstanceOf[Short]())
          matchEnd11("short")
        else
          case15()
      };
      case15(){
        if (x1.isInstanceOf[Char]())
          matchEnd11("char")
        else
          case16()
      };
      case16(){
        if (x1.isInstanceOf[Int]())
          matchEnd11("int")
        else
          case17()
      };
      case17(){
        if (x1.isInstanceOf[Long]())
          matchEnd11("long")
        else
          case18()
      };
      case18(){
        if (x1.isInstanceOf[Double]())
          matchEnd11("double")
        else
          case19()
      };
      case19(){
        if (x1.isInstanceOf[Float]())
          matchEnd11("float")
        else
          case20()
      };
      case20(){
        matchEnd11("default")
      };
      matchEnd11(x: Any){
        x
      }
    });
    <specialized> def test$mCc$sp(x: Char): Unit = scala.this.Predef.println({
      case <synthetic> val x1: Char = x;
      case12(){
        if (x1.isInstanceOf[Boolean]())
          matchEnd11("bool")
        else
          case13()
      };
      case13(){
        if (x1.isInstanceOf[Byte]())
          matchEnd11("byte")
        else
          case14()
      };
      case14(){
        if (x1.isInstanceOf[Short]())
          matchEnd11("short")
        else
          case15()
      };
      case15(){
        if (x1.isInstanceOf[Char]())
          matchEnd11("char")
        else
          case16()
      };
      case16(){
        if (x1.isInstanceOf[Int]())
          matchEnd11("int")
        else
          case17()
      };
      case17(){
        if (x1.isInstanceOf[Long]())
          matchEnd11("long")
        else
          case18()
      };
      case18(){
        if (x1.isInstanceOf[Double]())
          matchEnd11("double")
        else
          case19()
      };
      case19(){
        if (x1.isInstanceOf[Float]())
          matchEnd11("float")
        else
          case20()
      };
      case20(){
        matchEnd11("default")
      };
      matchEnd11(x: Any){
        x
      }
    });
    <specialized> def test$mDc$sp(x: Double): Unit = scala.this.Predef.println({
      case <synthetic> val x1: Double = x;
      case12(){
        if (x1.isInstanceOf[Boolean]())
          matchEnd11("bool")
        else
          case13()
      };
      case13(){
        if (x1.isInstanceOf[Byte]())
          matchEnd11("byte")
        else
          case14()
      };
      case14(){
        if (x1.isInstanceOf[Short]())
          matchEnd11("short")
        else
          case15()
      };
      case15(){
        if (x1.isInstanceOf[Char]())
          matchEnd11("char")
        else
          case16()
      };
      case16(){
        if (x1.isInstanceOf[Int]())
          matchEnd11("int")
        else
          case17()
      };
      case17(){
        if (x1.isInstanceOf[Long]())
          matchEnd11("long")
        else
          case18()
      };
      case18(){
        if (x1.isInstanceOf[Double]())
          matchEnd11("double")
        else
          case19()
      };
      case19(){
        if (x1.isInstanceOf[Float]())
          matchEnd11("float")
        else
          case20()
      };
      case20(){
        matchEnd11("default")
      };
      matchEnd11(x: Any){
        x
      }
    });
    <specialized> def test$mFc$sp(x: Float): Unit = scala.this.Predef.println({
      case <synthetic> val x1: Float = x;
      case12(){
        if (x1.isInstanceOf[Boolean]())
          matchEnd11("bool")
        else
          case13()
      };
      case13(){
        if (x1.isInstanceOf[Byte]())
          matchEnd11("byte")
        else
          case14()
      };
      case14(){
        if (x1.isInstanceOf[Short]())
          matchEnd11("short")
        else
          case15()
      };
      case15(){
        if (x1.isInstanceOf[Char]())
          matchEnd11("char")
        else
          case16()
      };
      case16(){
        if (x1.isInstanceOf[Int]())
          matchEnd11("int")
        else
          case17()
      };
      case17(){
        if (x1.isInstanceOf[Long]())
          matchEnd11("long")
        else
          case18()
      };
      case18(){
        if (x1.isInstanceOf[Double]())
          matchEnd11("double")
        else
          case19()
      };
      case19(){
        if (x1.isInstanceOf[Float]())
          matchEnd11("float")
        else
          case20()
      };
      case20(){
        matchEnd11("default")
      };
      matchEnd11(x: Any){
        x
      }
    });
    <specialized> def test$mIc$sp(x: Int): Unit = scala.this.Predef.println({
      case <synthetic> val x1: Int = x;
      case12(){
        if (x1.isInstanceOf[Boolean]())
          matchEnd11("bool")
        else
          case13()
      };
      case13(){
        if (x1.isInstanceOf[Byte]())
          matchEnd11("byte")
        else
          case14()
      };
      case14(){
        if (x1.isInstanceOf[Short]())
          matchEnd11("short")
        else
          case15()
      };
      case15(){
        if (x1.isInstanceOf[Char]())
          matchEnd11("char")
        else
          case16()
      };
      case16(){
        if (x1.isInstanceOf[Int]())
          matchEnd11("int")
        else
          case17()
      };
      case17(){
        if (x1.isInstanceOf[Long]())
          matchEnd11("long")
        else
          case18()
      };
      case18(){
        if (x1.isInstanceOf[Double]())
          matchEnd11("double")
        else
          case19()
      };
      case19(){
        if (x1.isInstanceOf[Float]())
          matchEnd11("float")
        else
          case20()
      };
      case20(){
        matchEnd11("default")
      };
      matchEnd11(x: Any){
        x
      }
    });
    <specialized> def test$mJc$sp(x: Long): Unit = scala.this.Predef.println({
      case <synthetic> val x1: Long = x;
      case12(){
        if (x1.isInstanceOf[Boolean]())
          matchEnd11("bool")
        else
          case13()
      };
      case13(){
        if (x1.isInstanceOf[Byte]())
          matchEnd11("byte")
        else
          case14()
      };
      case14(){
        if (x1.isInstanceOf[Short]())
          matchEnd11("short")
        else
          case15()
      };
      case15(){
        if (x1.isInstanceOf[Char]())
          matchEnd11("char")
        else
          case16()
      };
      case16(){
        if (x1.isInstanceOf[Int]())
          matchEnd11("int")
        else
          case17()
      };
      case17(){
        if (x1.isInstanceOf[Long]())
          matchEnd11("long")
        else
          case18()
      };
      case18(){
        if (x1.isInstanceOf[Double]())
          matchEnd11("double")
        else
          case19()
      };
      case19(){
        if (x1.isInstanceOf[Float]())
          matchEnd11("float")
        else
          case20()
      };
      case20(){
        matchEnd11("default")
      };
      matchEnd11(x: Any){
        x
      }
    });
    <specialized> def test$mSc$sp(x: Short): Unit = scala.this.Predef.println({
      case <synthetic> val x1: Short = x;
      case12(){
        if (x1.isInstanceOf[Boolean]())
          matchEnd11("bool")
        else
          case13()
      };
      case13(){
        if (x1.isInstanceOf[Byte]())
          matchEnd11("byte")
        else
          case14()
      };
      case14(){
        if (x1.isInstanceOf[Short]())
          matchEnd11("short")
        else
          case15()
      };
      case15(){
        if (x1.isInstanceOf[Char]())
          matchEnd11("char")
        else
          case16()
      };
      case16(){
        if (x1.isInstanceOf[Int]())
          matchEnd11("int")
        else
          case17()
      };
      case17(){
        if (x1.isInstanceOf[Long]())
          matchEnd11("long")
        else
          case18()
      };
      case18(){
        if (x1.isInstanceOf[Double]())
          matchEnd11("double")
        else
          case19()
      };
      case19(){
        if (x1.isInstanceOf[Float]())
          matchEnd11("float")
        else
          case20()
      };
      case20(){
        matchEnd11("default")
      };
      matchEnd11(x: Any){
        x
      }
    });
    <specialized> def test$mVc$sp(x: Unit): Unit = scala.this.Predef.println({
      case <synthetic> val x1: Unit = x;
      case12(){
        if (x1.isInstanceOf[Boolean]())
          matchEnd11("bool")
        else
          case13()
      };
      case13(){
        if (x1.isInstanceOf[Byte]())
          matchEnd11("byte")
        else
          case14()
      };
      case14(){
        if (x1.isInstanceOf[Short]())
          matchEnd11("short")
        else
          case15()
      };
      case15(){
        if (x1.isInstanceOf[Char]())
          matchEnd11("char")
        else
          case16()
      };
      case16(){
        if (x1.isInstanceOf[Int]())
          matchEnd11("int")
        else
          case17()
      };
      case17(){
        if (x1.isInstanceOf[Long]())
          matchEnd11("long")
        else
          case18()
      };
      case18(){
        if (x1.isInstanceOf[Double]())
          matchEnd11("double")
        else
          case19()
      };
      case19(){
        if (x1.isInstanceOf[Float]())
          matchEnd11("float")
        else
          case20()
      };
      case20(){
        matchEnd11("default")
      };
      matchEnd11(x: Any){
        x
      }
    })
  };
  <specialized> class Foo$mcZ$sp extends Foo[Boolean] {
    <specialized> def <init>(): Foo$mcZ$sp = {
      Foo$mcZ$sp.super.<init>();
      ()
    };
    override <specialized> def test(x: Boolean): Unit = Foo$mcZ$sp.this.test$mcZ$sp(x);
    override <specialized> def test$mcZ$sp(x: Boolean): Unit = scala.this.Predef.println({
      case <synthetic> val x1: Boolean = x;
      case12(){
        if (x1.isInstanceOf[Boolean]())
          matchEnd11("bool")
        else
          case13()
      };
      case13(){
        if (x1.isInstanceOf[Byte]())
          matchEnd11("byte")
        else
          case14()
      };
      case14(){
        if (x1.isInstanceOf[Short]())
          matchEnd11("short")
        else
          case15()
      };
      case15(){
        if (x1.isInstanceOf[Char]())
          matchEnd11("char")
        else
          case16()
      };
      case16(){
        if (x1.isInstanceOf[Int]())
          matchEnd11("int")
        else
          case17()
      };
      case17(){
        if (x1.isInstanceOf[Long]())
          matchEnd11("long")
        else
          case18()
      };
      case18(){
        if (x1.isInstanceOf[Double]())
          matchEnd11("double")
        else
          case19()
      };
      case19(){
        if (x1.isInstanceOf[Float]())
          matchEnd11("float")
        else
          case20()
      };
      case20(){
        matchEnd11("default")
      };
      matchEnd11(x: Any){
        x
      }
    })
  };
  <specialized> class Foo$mcB$sp extends Foo[Byte] {
    <specialized> def <init>(): Foo$mcB$sp = {
      Foo$mcB$sp.super.<init>();
      ()
    };
    override <specialized> def test(x: Byte): Unit = Foo$mcB$sp.this.test$mcB$sp(x);
    override <specialized> def test$mcB$sp(x: Byte): Unit = scala.this.Predef.println({
      case <synthetic> val x1: Byte = x;
      case12(){
        if (x1.isInstanceOf[Boolean]())
          matchEnd11("bool")
        else
          case13()
      };
      case13(){
        if (x1.isInstanceOf[Byte]())
          matchEnd11("byte")
        else
          case14()
      };
      case14(){
        if (x1.isInstanceOf[Short]())
          matchEnd11("short")
        else
          case15()
      };
      case15(){
        if (x1.isInstanceOf[Char]())
          matchEnd11("char")
        else
          case16()
      };
      case16(){
        if (x1.isInstanceOf[Int]())
          matchEnd11("int")
        else
          case17()
      };
      case17(){
        if (x1.isInstanceOf[Long]())
          matchEnd11("long")
        else
          case18()
      };
      case18(){
        if (x1.isInstanceOf[Double]())
          matchEnd11("double")
        else
          case19()
      };
      case19(){
        if (x1.isInstanceOf[Float]())
          matchEnd11("float")
        else
          case20()
      };
      case20(){
        matchEnd11("default")
      };
      matchEnd11(x: Any){
        x
      }
    })
  };
  <specialized> class Foo$mcC$sp extends Foo[Char] {
    <specialized> def <init>(): Foo$mcC$sp = {
      Foo$mcC$sp.super.<init>();
      ()
    };
    override <specialized> def test(x: Char): Unit = Foo$mcC$sp.this.test$mcC$sp(x);
    override <specialized> def test$mcC$sp(x: Char): Unit = scala.this.Predef.println({
      case <synthetic> val x1: Char = x;
      case12(){
        if (x1.isInstanceOf[Boolean]())
          matchEnd11("bool")
        else
          case13()
      };
      case13(){
        if (x1.isInstanceOf[Byte]())
          matchEnd11("byte")
        else
          case14()
      };
      case14(){
        if (x1.isInstanceOf[Short]())
          matchEnd11("short")
        else
          case15()
      };
      case15(){
        if (x1.isInstanceOf[Char]())
          matchEnd11("char")
        else
          case16()
      };
      case16(){
        if (x1.isInstanceOf[Int]())
          matchEnd11("int")
        else
          case17()
      };
      case17(){
        if (x1.isInstanceOf[Long]())
          matchEnd11("long")
        else
          case18()
      };
      case18(){
        if (x1.isInstanceOf[Double]())
          matchEnd11("double")
        else
          case19()
      };
      case19(){
        if (x1.isInstanceOf[Float]())
          matchEnd11("float")
        else
          case20()
      };
      case20(){
        matchEnd11("default")
      };
      matchEnd11(x: Any){
        x
      }
    })
  };
  <specialized> class Foo$mcD$sp extends Foo[Double] {
    <specialized> def <init>(): Foo$mcD$sp = {
      Foo$mcD$sp.super.<init>();
      ()
    };
    override <specialized> def test(x: Double): Unit = Foo$mcD$sp.this.test$mcD$sp(x);
    override <specialized> def test$mcD$sp(x: Double): Unit = scala.this.Predef.println({
      case <synthetic> val x1: Double = x;
      case12(){
        if (x1.isInstanceOf[Boolean]())
          matchEnd11("bool")
        else
          case13()
      };
      case13(){
        if (x1.isInstanceOf[Byte]())
          matchEnd11("byte")
        else
          case14()
      };
      case14(){
        if (x1.isInstanceOf[Short]())
          matchEnd11("short")
        else
          case15()
      };
      case15(){
        if (x1.isInstanceOf[Char]())
          matchEnd11("char")
        else
          case16()
      };
      case16(){
        if (x1.isInstanceOf[Int]())
          matchEnd11("int")
        else
          case17()
      };
      case17(){
        if (x1.isInstanceOf[Long]())
          matchEnd11("long")
        else
          case18()
      };
      case18(){
        if (x1.isInstanceOf[Double]())
          matchEnd11("double")
        else
          case19()
      };
      case19(){
        if (x1.isInstanceOf[Float]())
          matchEnd11("float")
        else
          case20()
      };
      case20(){
        matchEnd11("default")
      };
      matchEnd11(x: Any){
        x
      }
    })
  };
  <specialized> class Foo$mcF$sp extends Foo[Float] {
    <specialized> def <init>(): Foo$mcF$sp = {
      Foo$mcF$sp.super.<init>();
      ()
    };
    override <specialized> def test(x: Float): Unit = Foo$mcF$sp.this.test$mcF$sp(x);
    override <specialized> def test$mcF$sp(x: Float): Unit = scala.this.Predef.println({
      case <synthetic> val x1: Float = x;
      case12(){
        if (x1.isInstanceOf[Boolean]())
          matchEnd11("bool")
        else
          case13()
      };
      case13(){
        if (x1.isInstanceOf[Byte]())
          matchEnd11("byte")
        else
          case14()
      };
      case14(){
        if (x1.isInstanceOf[Short]())
          matchEnd11("short")
        else
          case15()
      };
      case15(){
        if (x1.isInstanceOf[Char]())
          matchEnd11("char")
        else
          case16()
      };
      case16(){
        if (x1.isInstanceOf[Int]())
          matchEnd11("int")
        else
          case17()
      };
      case17(){
        if (x1.isInstanceOf[Long]())
          matchEnd11("long")
        else
          case18()
      };
      case18(){
        if (x1.isInstanceOf[Double]())
          matchEnd11("double")
        else
          case19()
      };
      case19(){
        if (x1.isInstanceOf[Float]())
          matchEnd11("float")
        else
          case20()
      };
      case20(){
        matchEnd11("default")
      };
      matchEnd11(x: Any){
        x
      }
    })
  };
  <specialized> class Foo$mcI$sp extends Foo[Int] {
    <specialized> def <init>(): Foo$mcI$sp = {
      Foo$mcI$sp.super.<init>();
      ()
    };
    override <specialized> def test(x: Int): Unit = Foo$mcI$sp.this.test$mcI$sp(x);
    override <specialized> def test$mcI$sp(x: Int): Unit = scala.this.Predef.println({
      case <synthetic> val x1: Int = x;
      case12(){
        if (x1.isInstanceOf[Boolean]())
          matchEnd11("bool")
        else
          case13()
      };
      case13(){
        if (x1.isInstanceOf[Byte]())
          matchEnd11("byte")
        else
          case14()
      };
      case14(){
        if (x1.isInstanceOf[Short]())
          matchEnd11("short")
        else
          case15()
      };
      case15(){
        if (x1.isInstanceOf[Char]())
          matchEnd11("char")
        else
          case16()
      };
      case16(){
        if (x1.isInstanceOf[Int]())
          matchEnd11("int")
        else
          case17()
      };
      case17(){
        if (x1.isInstanceOf[Long]())
          matchEnd11("long")
        else
          case18()
      };
      case18(){
        if (x1.isInstanceOf[Double]())
          matchEnd11("double")
        else
          case19()
      };
      case19(){
        if (x1.isInstanceOf[Float]())
          matchEnd11("float")
        else
          case20()
      };
      case20(){
        matchEnd11("default")
      };
      matchEnd11(x: Any){
        x
      }
    })
  };
  <specialized> class Foo$mcJ$sp extends Foo[Long] {
    <specialized> def <init>(): Foo$mcJ$sp = {
      Foo$mcJ$sp.super.<init>();
      ()
    };
    override <specialized> def test(x: Long): Unit = Foo$mcJ$sp.this.test$mcJ$sp(x);
    override <specialized> def test$mcJ$sp(x: Long): Unit = scala.this.Predef.println({
      case <synthetic> val x1: Long = x;
      case12(){
        if (x1.isInstanceOf[Boolean]())
          matchEnd11("bool")
        else
          case13()
      };
      case13(){
        if (x1.isInstanceOf[Byte]())
          matchEnd11("byte")
        else
          case14()
      };
      case14(){
        if (x1.isInstanceOf[Short]())
          matchEnd11("short")
        else
          case15()
      };
      case15(){
        if (x1.isInstanceOf[Char]())
          matchEnd11("char")
        else
          case16()
      };
      case16(){
        if (x1.isInstanceOf[Int]())
          matchEnd11("int")
        else
          case17()
      };
      case17(){
        if (x1.isInstanceOf[Long]())
          matchEnd11("long")
        else
          case18()
      };
      case18(){
        if (x1.isInstanceOf[Double]())
          matchEnd11("double")
        else
          case19()
      };
      case19(){
        if (x1.isInstanceOf[Float]())
          matchEnd11("float")
        else
          case20()
      };
      case20(){
        matchEnd11("default")
      };
      matchEnd11(x: Any){
        x
      }
    })
  };
  <specialized> class Foo$mcS$sp extends Foo[Short] {
    <specialized> def <init>(): Foo$mcS$sp = {
      Foo$mcS$sp.super.<init>();
      ()
    };
    override <specialized> def test(x: Short): Unit = Foo$mcS$sp.this.test$mcS$sp(x);
    override <specialized> def test$mcS$sp(x: Short): Unit = scala.this.Predef.println({
      case <synthetic> val x1: Short = x;
      case12(){
        if (x1.isInstanceOf[Boolean]())
          matchEnd11("bool")
        else
          case13()
      };
      case13(){
        if (x1.isInstanceOf[Byte]())
          matchEnd11("byte")
        else
          case14()
      };
      case14(){
        if (x1.isInstanceOf[Short]())
          matchEnd11("short")
        else
          case15()
      };
      case15(){
        if (x1.isInstanceOf[Char]())
          matchEnd11("char")
        else
          case16()
      };
      case16(){
        if (x1.isInstanceOf[Int]())
          matchEnd11("int")
        else
          case17()
      };
      case17(){
        if (x1.isInstanceOf[Long]())
          matchEnd11("long")
        else
          case18()
      };
      case18(){
        if (x1.isInstanceOf[Double]())
          matchEnd11("double")
        else
          case19()
      };
      case19(){
        if (x1.isInstanceOf[Float]())
          matchEnd11("float")
        else
          case20()
      };
      case20(){
        matchEnd11("default")
      };
      matchEnd11(x: Any){
        x
      }
    })
  };
  <specialized> class Foo$mcV$sp extends Foo[Unit] {
    <specialized> def <init>(): Foo$mcV$sp = {
      Foo$mcV$sp.super.<init>();
      ()
    };
    override <specialized> def test(x: Unit): Unit = Foo$mcV$sp.this.test$mcV$sp(x);
    override <specialized> def test$mcV$sp(x: Unit): Unit = scala.this.Predef.println({
      case <synthetic> val x1: Unit = x;
      case12(){
        if (x1.isInstanceOf[Boolean]())
          matchEnd11("bool")
        else
          case13()
      };
      case13(){
        if (x1.isInstanceOf[Byte]())
          matchEnd11("byte")
        else
          case14()
      };
      case14(){
        if (x1.isInstanceOf[Short]())
          matchEnd11("short")
        else
          case15()
      };
      case15(){
        if (x1.isInstanceOf[Char]())
          matchEnd11("char")
        else
          case16()
      };
      case16(){
        if (x1.isInstanceOf[Int]())
          matchEnd11("int")
        else
          case17()
      };
      case17(){
        if (x1.isInstanceOf[Long]())
          matchEnd11("long")
        else
          case18()
      };
      case18(){
        if (x1.isInstanceOf[Double]())
          matchEnd11("double")
        else
          case19()
      };
      case19(){
        if (x1.isInstanceOf[Float]())
          matchEnd11("float")
        else
          case20()
      };
      case20(){
        matchEnd11("default")
      };
      matchEnd11(x: Any){
        x
      }
    })
  }
}

I'm going to ask Iulian what the motivation was for getting Duplicators to strip out the impossible cases; was it performance, avoidance of errors from the subsequent match translation, or both?

The simplest resolution here will be to remove the now-redundant code from Duplicators and live with the extra bytecode.

@scabug
Copy link
Author

@scabug scabug commented Feb 22, 2013

Imported From: https://issues.scala-lang.org/browse/SI-7172?orig=1
Reporter: @retronym
Affected Versions: 2.10.0

@scabug
Copy link
Author

@scabug scabug commented Feb 22, 2013

@VladUreche said:
I have no idea about Iulian's motivation, but I guess there's at least some performance gain from removing the dead cases, if not a lot.

But couldn't we just remove the ifs that cannot be true? This would yield a series of jumps from one label to the next, that could further be optimized down to the previous patmat output. Or is there a problem which I'm not seeing?

@scabug
Copy link
Author

@scabug scabug commented Feb 22, 2013

@retronym said:
Yep, it would be ideal to just rely on the optimizer to kill the dead branches.

But it doesn't seem like we currently eliminate impossible isInstanceOf tests:

% scala210 -optimize -Xprint:jvm -e  'if (Int.box(1).isInstanceOf[Byte]) "byte" else "other"'

[[syntax trees at end of                       jvm]] // scalacmd9042292549797194950.scala
package <empty> {
  object Main extends Object {
    def main(argv: Array[String]): Unit = {
      val args: Array[String] = argv;
      {
        {
          new anonymous class anon$1();
          ()
        }
      }
    };
    def <init>(): Main.type = {
      Main.super.<init>();
      ()
    }
  };
  final class Main$$anon$1 extends Object {
    def <init>(): anonymous class anon$1 = {
      Main$$anon$1.super.<init>();
      if (scala.Int.box(1).$isInstanceOf[Byte]())
        "byte"
      else
        "other";
      ()
    }
  }
}

I don't suppose that would be such a tough optimization to add.

@scabug
Copy link
Author

@scabug scabug commented Feb 22, 2013

@VladUreche said:
Probably not, but wouldn't we knock out more cases if we did it pre-erasure? Or does the patmat already eliminate impossible cases?

@scabug
Copy link
Author

@scabug scabug commented Feb 22, 2013

@VladUreche said:
Strange, I would have expected unreachability to mean code is not generated at all:

$ cat patmat.scala 
object Test {
  def foo(l: List[_]) = l match {
    case Nil => "okay"
    case _ :: _ => "okay"
    case ll: List[_] => "dead code"
  }
}

$ ../build/quick/bin/scalac patmat.scala -Xprint:cleanup
patmat.scala:5: warning: unreachable code
    case ll: List[_] => "dead code"
                        ^

[[syntax trees at end of                   cleanup]] // patmat.scala
package <empty> {
  object Test extends Object {
    def foo(l: List): String = {
      case <synthetic> val x1: List = l;
      ...
      case11(){
        if (x1.$isInstanceOf[List]())
          matchEnd8("dead code")
        else
          case12()
      };
      ...
    };
    ...
  }
}

@scabug
Copy link
Author

@scabug scabug commented Feb 22, 2013

@retronym said (edited on Feb 22, 2013 11:29:04 PM UTC):
Match translation now happens immediately after typer. That's new in 2.10. It's only after specialization that some of the cases become dead code. Previously, these dead cases were eliminated from the Match node (during Tree#duplicate) before match translation. Now, we would need to eliminate them from if/else-s.

@scabug
Copy link
Author

@scabug scabug commented Feb 22, 2013

@retronym said (edited on Feb 22, 2013 11:31:34 PM UTC):
I'm not sure why the pattern matcher bothers to emit the dead code. Perhaps it is being conservative by just warning, worried about a bug in its analysis (e.g. #6771).

@scabug
Copy link
Author

@scabug scabug commented Feb 22, 2013

@VladUreche said:
Okay, so we have two problems at play here: the pattern matcher being too conservative about unreachable code and the very simple cases that are only generated after specialzation, for which we could hardcode some simple rules in the backend.

For a backend phase, I'm not sure it's worth tying to icode, as the new backend is already knocking at our door. WDYT?

@scabug
Copy link
Author

@scabug scabug commented Feb 22, 2013

@retronym said:
I believe that HotSpot will get rid of the impossible cases:

void SharkTopLevelBlock::do_instance_check() {
  // Get the class we're checking against
  bool will_link;
  ciKlass *check_klass = iter()->get_klass(will_link);

  // Get the class of the object we're checking
  ciKlass *object_klass = xstack(0)->type()->as_klass();

  // Can we optimize this check away?
  if (static_subtype_check(check_klass, object_klass)) {
    if (bc() == Bytecodes::_instanceof) {
      pop();
      push(SharkValue::jint_constant(1));
    }
    return;
  }

  // Need to check this one at runtime
  if (will_link)
    do_full_instance_check(check_klass);
  else
    do_trapping_instance_check(check_klass);
}

But it would be nice to eliminate them on our side on a bytecode size argument. I'm not sure whether this (@specialized + type casing) comes up enough to make it high priority.

But we could immediately remove the code from Duplicators that no longer has the intended effect.

@scabug
Copy link
Author

@scabug scabug commented Feb 23, 2013

@VladUreche said:
I would also look at it from a different perspective: there's no reason to leave a computation that we can do statically for the runtime critical path.

Btw, shark is the llvm backend of the vm, but I couldn't quickly identify the same for c1 and opto. They probably have it, it's just that I'm not familiar enough with the source code to quickly locate it.

@scabug
Copy link
Author

@scabug scabug commented Feb 23, 2013

@retronym said:
I think we can put the optimization into Erasure, which is backend agnostic.

It's already doing something similar for refinement types, although it doesn't deal with null correctly.

scala> (null: A with B).isInstanceOf[A with B]
res6: Boolean = true // should be false

@scabug
Copy link
Author

@scabug scabug commented Feb 23, 2013

@retronym said:
I've implemented the instanceof elimination in Erasure. Along the way, I made the existing optimization null-aware.

retronym/scala@scala:2.10.x...retronym:ticket/7172

With this in place, the original code is now compiled to:

<specialized> class Foo$mcS$sp extends Foo {
    override <specialized> def test(x: Short): Unit = Foo$mcS$sp.this.test$mcS$sp(x);
    override <specialized> def test$mcS$sp(x: Short): Unit = scala.this.Predef.println({
      case <synthetic> val x1: Short = x;
      case12(){
        if (if (scala.Short.box(x1).==(null))
          false
        else
          false)
          matchEnd11("bool")
        else
          case13()
      };
      case13(){
        if (if (scala.Short.box(x1).==(null))
          false
        else
          false)
          matchEnd11("byte")
        else
          case14()
      };
      case14(){
        if (if (scala.Short.box(x1).==(null))
          false
        else
          true)
          matchEnd11("short")
        else
          case15()
      };
      // ...
  Code:
   Stack=2, Locals=3, Args_size=2
   0:	getstatic	#21; //Field scala/Predef$.MODULE$:Lscala/Predef$;
   3:	iload_1
   4:	invokestatic	#27; //Method scala/runtime/BoxesRunTime.boxToShort:(S)Ljava/lang/Short;
   7:	ifnonnull	14
   10:	iconst_0
   11:	goto	15
   14:	iconst_0
   15:	ifeq	24
   18:	ldc	#29; //String bool
   20:	astore_2
   21:	goto	174
   24:	iload_1
   25:	invokestatic	#27; //Method scala/runtime/BoxesRunTime.boxToShort:(S)Ljava/lang/Short;
   28:	ifnonnull	35
   31:	iconst_0
   32:	goto	36
   35:	iconst_0
   36:	ifeq	45
   39:	ldc	#31; //String byte
   41:	astore_2
   42:	goto	174
   45:	iload_1
   46:	invokestatic	#27; //Method scala/runtime/BoxesRunTime.boxToShort:(S)Ljava/lang/Short;
   49:	ifnonnull	56
   52:	iconst_0
   53:	goto	57
   56:	iconst_1
   57:	ifeq	66
   60:	ldc	#33; //String short
   62:	astore_2
   63:	goto	174
   66:	iload_1
   ...

So we still need to teach the optimizer that box(x1).==(null) is always false. Suggestions for the right place to put this?

@scabug
Copy link
Author

@scabug scabug commented Feb 23, 2013

@dragos said:
The code in Duplicators was there only because the old pattern matcher would complain about the dead cases. It's great you implemented the more general optimization. I'm surprised the compiler does not at least warn about "stupid casts".

AFAIK the optimizer doesn't do any optimizations around conditional jumps, so you'd need to code that from scratch. It'd be worth it (and could also handle object comparisons with null).

@scabug
Copy link
Author

@scabug scabug commented Feb 23, 2013

@retronym said:
Thanks for filling in the history. I guess the backend can't really warn about stupid casts as the compiler can generate them (ie through specialization).

We could certainly warn about them in the type checker. I think the idea has come up before, but the usual answer is that if you want the warnings, you should use a pattern match, rather than an isInstanceOf.

Another approach, albeit less generally useful, would be to avoid emitting the if (x eq null) in the isInstanceOf translation I added to Erasure if the x is trivially known to be non-null.

@scabug
Copy link
Author

@scabug scabug commented Jul 10, 2013

@adriaanm said:
Unassigning as milestone deadline was reached.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants