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

Pattern matcher matches on wrong compound type pattern

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: Scala 2.9.2, Scala 2.10.0
    • Fix Version/s: Scala 2.10.0-M4
    • Component/s: Pattern Matcher
    • Labels:
      None
    • Environment:

      Scala version 2.10.0-20120419-125451-756dbbc254 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_31). (OS X Lion)

      Description

      If I have the following inheritance structure

      trait T
      
      trait TA
      trait TB
      
      class A extends T with TA
      class B extends T with TB
      class AB extends T with TA with TB
      

      Matching on _: TA with TB

      val li: Vector[T] = Vector(new A, new B, new AB)
      
      val matched = (for (l <- li) yield {
        l match {
          case _: TA with TB => "tab"
          case _: TA => "ta"
          case _: TB => "tb"
        }
      })
      
      println(matched)
      // prints
      // Vector(tab, tb, tab)
      

      Matching on _: T with TA with TB

      val matched2 = (for (l <- li) yield {
        l match {
          case _: T with TA with TB => "tab"
          case _: T with TA => "ta"
          case _: T with TB => "tb"
        }
      })
      
      println(matched2)
      // prints
      // Vector(ta, tb, tab)
      
      // For comparison: This works correctly
      println((new A).isInstanceOf[TA with TB]) // prints false
      println((new B).isInstanceOf[TA with TB]) // prints false
      println((new AB).isInstanceOf[TA with TB]) // prints true
      

      Running with -Xprint:typer, both match statements are identical (and the isInstanceOf clause is inferred to be [T with TA with TB] in both cases)

      private[this] val matched2: scala.collection.immutable.Vector[String] = $anon.this.li.map[String, scala.collection.immutable.Vector[String]]({
                  @SerialVersionUID(0) final <synthetic> class $anonfun extends scala.runtime.AbstractFunction1[this.T,String] with Serializable {
                    def <init>(): anonymous class $anonfun = {
                      $anonfun.super.<init>();
                      ()
                    };
                    final def apply(l: this.T): String = {
                      case <synthetic> val x1: this.T = l;
                      case6(){
                        if (x1.isInstanceOf[this.T with this.TA with this.TB])
                          {
                            val x2: this.T with this.TA with this.TB = (x1.asInstanceOf[this.T with this.TA with this.TB]: this.T with this.TA with this.TB);
                            matchEnd5("tab")
                          }
                        else
                          case7()
                      };
                      case7(){
                        if (x1.isInstanceOf[this.T with this.TA])
                          {
                            val x3: this.T with this.TA = (x1.asInstanceOf[this.T with this.TA]: this.T with this.TA);
                            matchEnd5("ta")
                          }
                        else
                          case8()
                      };
                      case8(){
                        if (x1.isInstanceOf[this.T with this.TB])
                          {
                            val x4: this.T with this.TB = (x1.asInstanceOf[this.T with this.TB]: this.T with this.TB);
                            matchEnd5("tb")
                          }
                        else
                          case9()
                      };
                      case9(){
                        matchEnd5(throw new MatchError(x1))
                      };
                      matchEnd5(x: String){
                        x
                      }
                    }
                  };
                  new anonymous class $anonfun()
                })(immutable.this.Vector.canBuildFrom[String]);
      

      -Xprint:erasure however shows the difference:

      matched:

      final def apply(l: T): String = {
            case <synthetic> val x1: T = l;
            case6(){
              if (x1.$isInstanceOf[TA]())
                {
                  val x2: TA = (x1.$asInstanceOf[TA](): TA);
                  matchEnd5("tab")
                }
              else
                case7()
            };
            case7(){
              if (x1.$isInstanceOf[TA]())
                {
                  val x3: T = (x1.$asInstanceOf[T](): T);
                  matchEnd5("ta")
                }
              else
                case8()
            };
            case8(){
              if (x1.$isInstanceOf[TB]())
                {
                  val x4: T = (x1.$asInstanceOf[T](): T);
                  matchEnd5("tb")
                }
              else
                case9()
            };
            case9(){
              matchEnd5(throw new MatchError(x1))
            };
            matchEnd5(x: String){
              x
            }
          };
      

      matched2:

      final def apply(l: T): String = {
            case <synthetic> val x1: T = l;
            case6(){
              if (x1.$isInstanceOf[TA]().&&(x1.$isInstanceOf[TB]()))
                {
                  val x2: T = (x1.$asInstanceOf[T](): T);
                  matchEnd5("tab")
                }
              else
                case7()
            };
            case7(){
              if (x1.$isInstanceOf[TA]())
                {
                  val x3: T = (x1.$asInstanceOf[T](): T);
                  matchEnd5("ta")
                }
              else
                case8()
            };
            case8(){
              if (x1.$isInstanceOf[TB]())
                {
                  val x4: T = (x1.$asInstanceOf[T](): T);
                  matchEnd5("tb")
                }
              else
                case9()
            };
            case9(){
              matchEnd5(throw new MatchError(x1))
            };
            matchEnd5(x: String){
              x
            }
          };
      

      So, the check against TB is somehow missing when one writes _: TA with TB but is included if it is written as _: T with TA with TB.

        Activity

        Hide
        Rike-Benjamin Schuppner added a comment - - edited

        The problem seems to be that in the broken case a nested RefinedType is generated (OOO being an outer class):

        tree.tpe: OOO.this.T with OOO.this.TA with OOO.this.TB
        RefinedType(
            RefinedType( TypeRef( ThisType(OOO),  TB,  [ ]),  TypeRef( ThisType(OOO),  TA,  [ ]), ), 
            TypeRef( ThisType(OOO),  T,  [ ]), )
        

        Whereas in the good case

        tree.tpe: OOO.this.T with OOO.this.TA with OOO.this.TB
        RefinedType(
            TypeRef( ThisType(OOO),  TB,  [ ]), 
            TypeRef( ThisType(OOO),  TA,  [ ]), 
            TypeRef( ThisType(OOO),  T,  [ ]), )
        

        On erasure when generating the isInstanceOf[TA] && isInstanceOf[TB] cascades, an inner RefinedType is not handled and thus the [TA with TB] type stays there until TB is eventually erased.

        Show
        Rike-Benjamin Schuppner added a comment - - edited The problem seems to be that in the broken case a nested RefinedType is generated (OOO being an outer class): tree.tpe: OOO.this.T with OOO.this.TA with OOO.this.TB RefinedType( RefinedType( TypeRef( ThisType(OOO), TB, [ ]), TypeRef( ThisType(OOO), TA, [ ]), ), TypeRef( ThisType(OOO), T, [ ]), ) Whereas in the good case tree.tpe: OOO.this.T with OOO.this.TA with OOO.this.TB RefinedType( TypeRef( ThisType(OOO), TB, [ ]), TypeRef( ThisType(OOO), TA, [ ]), TypeRef( ThisType(OOO), T, [ ]), ) On erasure when generating the isInstanceOf [TA] && isInstanceOf [TB] cascades, an inner RefinedType is not handled and thus the [TA with TB] type stays there until TB is eventually erased.
        Hide
        Rike-Benjamin Schuppner added a comment -

        I tried to add a fix, not sure if this is appropriate: https://github.com/Debilski/scala/tree/ticket/SI-5688

        Show
        Rike-Benjamin Schuppner added a comment - I tried to add a fix, not sure if this is appropriate: https://github.com/Debilski/scala/tree/ticket/SI-5688
        Hide
        Adriaan Moors added a comment -

        fix is in the works based on your suggestion (but I'm flattening in def normalize in class RefinedType)

        Show
        Adriaan Moors added a comment - fix is in the works based on your suggestion (but I'm flattening in def normalize in class RefinedType)
        Show
        Adriaan Moors added a comment - fix in https://github.com/adriaanm/scala/commit/7a5aaa9e23a98d60343cc0c4411b3fc395faa3ab

          People

          • Assignee:
            Adriaan Moors
            Reporter:
            Rike-Benjamin Schuppner
          • Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development