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

name-based pattern matching could lead to undefined behaviour in runtime #9003

Closed
scabug opened this issue Nov 25, 2014 · 10 comments
Closed
Assignees
Labels
Milestone

Comments

@scabug
Copy link

scabug commented Nov 25, 2014

trait DeconstructorCommon[T >: Null <: Tree] {
    var field: T = null
    def get: this.type = this
    def isEmpty: Boolean = field eq null
    def unapply(s: T): this.type ={
      field = s
      this
    }
  }
trait BlockDeconstructor extends DeconstructorCommon[Block]{
    def _1: List[Tree] = field.stats
    def _2: Tree = field.expr
  }
val Block = new BlockDeconstructor{}

now if used in example

def foo(tree: Tree) =  tree match {
 case Block(e, e1) =>
    foo(tree)
    e1
 case _ => tree 
}

All references to e1 in this block will get rewritten to {code}Block._1{code}. But what happens if one calls foo on Block(Block(A, B), C). The value returned would be B instead of A.

The problem manifests itself only in run-time and does it silently. So marking this critical.

@scabug
Copy link
Author

scabug commented Nov 25, 2014

Imported From: https://issues.scala-lang.org/browse/SI-9003?orig=1
Reporter: @DarkDimius
Affected Versions: 2.11.4
See #5158, #6070

@scabug
Copy link
Author

scabug commented Nov 25, 2014

@retronym said (edited on Nov 25, 2014 1:39:57 PM UTC):
Could you please provide self contained, minimal examples? While it isn't hard to fill in the blanks, the onus should be on the reporter.

See val mutableBinders = in MatchTranslation.scala for the spot where we force creation of temp vals for patterns bound to var fields of case classes. I guess this needs to be extended for name-based patmat.

Relates to #5158, #6070

@scabug
Copy link
Author

scabug commented Nov 25, 2014

@DarkDimius said:
While minimizing example(that is actually part of scala/scala#4136) down to

package scala.tools.nsc.backend.jvm

object test {
  object TestInt {
    type Tree = Product2[Any, Int]

    object Block {
      def _1: Any = field._1
      def _2: Int = field._2
      var field: Tree = null
      def get: this.type = this
      def isEmpty: Boolean = field == null
      def isDefined = !isEmpty
      def unapply(s: Tree): this.type ={
        field = s
        this
      }
    }
  }

  def genBlock(tree: Any): Unit = tree match{
    case TestInt.Block(a, b) =>
      genBlock(a)
      print(b)
    case _ =>
  }

  def main(args: Array[String]): Unit = {
    genBlock(((1,2), 3))
  }

}

if this code is compiled with ant build it will output 23, which is correct.
But if compiled with
ant -Dscalac.args.optimise=-optimise -Dextra.repo.url=http://private-repo.typesafe.com/typesafe/scala-pr-validation-snapshots/ -Dstarr.version=2.11.5-2d0104d-SNAPSHOT -Dlocker.skip=1 -Dstarr.use.released=1 it will output 33. Which is incorrect.

@scabug
Copy link
Author

scabug commented Nov 25, 2014

@gkossakowski said:
Have you considered using scala bisector https://gist.github.com/retronym/9160186?

@scabug
Copy link
Author

scabug commented Nov 26, 2014

@retronym said:
Without -optimize, the pattern matcher always create temporaries for pattern binders, other than ones it identifies as unstable.

Here's a minimal repro:

object NamedBasedPatMat {
  var i = 0
  def isEmpty = false
  def get = i
  def unapply(a: NamedBasedPatMat.type) = this
}

object Test {
  def main(args: Array[String]): Unit = {
    NamedBasedPatMat match {
      case NamedBasedPatMat(i) =>
        NamedBasedPatMat.i = 1
        assert(i == 0) // fails under -optimize
    }
  }
}

@scabug
Copy link
Author

scabug commented Nov 26, 2014

@retronym said:
Actually, we've got the same bug in regular sequence extractors:

scala-hash v2.11.4 -optimize
Welcome to Scala version 2.11.4-20141023-110636-d783face36 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_25).
Type in expressions to have them evaluated.
Type :help for more information.

scala> val b = Buffer(1, 2, 3)
b: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1, 2, 3)

scala> b match { case Buffer(1, i, 3) => b(1) = 42; i }
res0: Int = 42

@scabug
Copy link
Author

scabug commented Nov 26, 2014

@retronym said:
scala/scala#4164

@scabug
Copy link
Author

scabug commented Nov 26, 2014

@DarkDimius said:
Thanks for fast reaction Jason!
Do I get it right that in order to get my PR compiled with fixed compiler I need to rebase?

@scabug
Copy link
Author

scabug commented Nov 26, 2014

@gkossakowski said:
Hi Dmitry,

We're building everything using stable version of the compiler. At the moment it's set to 2.11.2: https://github.com/scala/scala/blob/2.11.x/versions.properties#L7

I'll update it to point at 2.11.4.

@scabug
Copy link
Author

scabug commented Dec 3, 2014

@adriaanm said:
For your development, you could set starr.version to 2.11.5-SNAPSHOT until 2.11.5 is out.

@scabug scabug closed this as completed Dec 3, 2014
@scabug scabug added this to the 2.11.5 milestone Apr 7, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants