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

Pattern matcher matches on wrong compound type pattern #5688

Closed
scabug opened this issue Apr 21, 2012 · 5 comments
Closed

Pattern matcher matches on wrong compound type pattern #5688

scabug opened this issue Apr 21, 2012 · 5 comments
Assignees
Labels
Milestone

Comments

@scabug
Copy link

@scabug scabug commented Apr 21, 2012

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.

@scabug

This comment has been minimized.

Copy link
Author

@scabug scabug commented Apr 21, 2012

Imported From: https://issues.scala-lang.org/browse/SI-5688?orig=1
Reporter: Rike-Benjamin Schuppner (debilski)
Affected Versions: 2.9.2, 2.10.0

@scabug

This comment has been minimized.

Copy link
Author

@scabug scabug commented Apr 29, 2012

Rike-Benjamin Schuppner (debilski) said (edited on Apr 29, 2012 11:03:08 PM UTC):
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.

@scabug

This comment has been minimized.

Copy link
Author

@scabug scabug commented Apr 30, 2012

Rike-Benjamin Schuppner (debilski) said:
I tried to add a fix, not sure if this is appropriate: https://github.com/Debilski/scala/tree/ticket/SI-5688

@scabug

This comment has been minimized.

Copy link
Author

@scabug scabug commented May 3, 2012

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

@scabug

This comment has been minimized.

Copy link
Author

@scabug scabug commented May 3, 2012

@scabug scabug closed this May 4, 2012
@scabug scabug added the patmat label Apr 7, 2017
@scabug scabug added this to the 2.10.0-M4 milestone Apr 7, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants
You can’t perform that action at this time.