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

Macro error when expanding partial function #19

Open
smithjessk opened this issue May 28, 2016 · 7 comments
Open

Macro error when expanding partial function #19

smithjessk opened this issue May 28, 2016 · 7 comments

Comments

@smithjessk
Copy link
Collaborator

smithjessk commented May 28, 2016

Demo code: https://git.io/vrHmh

As recommended in #15, I'm porting some of the tests from Async into Coroutines. I translated this test and discovered a NullPointerException during compilation. A truncated SBT dump is at the bottom of this issue.
I believe this may be related to @retronym's comment in #15. I will investigate this more soon; just wanted to go ahead and bring the issue up.

> test
[info] Compiling 1 Scala source to /home/jess/projects/coroutines/target/scala-2.11/test-classes...
[info] Run completed in 20 milliseconds.
[info] Total number of tests run: 0
[info] Suites: completed 0, aborted 0
[info] Tests: succeeded 0, failed 0, canceled 0, ignored 0, pending 0
[info] No tests were executed.
[error] /home/jess/projects/coroutines/src/test/scala/org/coroutines/async-await-tests.scala:65: exception during macro expansion: 
[error] java.lang.NullPointerException
[error]     at org.coroutines.Analyzer$class.isCoroutineDefMarker(Analyzer.scala:328)
[error]     at org.coroutines.Synthesizer.isCoroutineDefMarker(Synthesizer.scala:15)
[error]     at org.coroutines.ThreeAddressFormTransformation$NestedContextValidator.traverse(ThreeAddressFormTransformation.scala:48)
[error]     at scala.reflect.internal.Trees$class.traverseComponents$1(Trees.scala:1232)
[error]     at scala.reflect.internal.Trees$class.itraverse(Trees.scala:1323)
[error]     at scala.reflect.internal.SymbolTable.itraverse(SymbolTable.scala:16)
[error]     at scala.reflect.internal.SymbolTable.itraverse(SymbolTable.scala:16)
[error]     at scala.reflect.api.Trees$Traverser.traverse(Trees.scala:2475)
[error]     at org.coroutines.ThreeAddressFormTransformation$NestedContextValidator.traverse(ThreeAddressFormTransformation.scala:62)
[error]     at scala.reflect.api.Trees$Traverser.traverseTrees(Trees.scala:2484)
[error]     at scala.reflect.api.Trees$Traverser.traverseCases(Trees.scala:2487)
[error]     at scala.reflect.internal.Trees$class.traverseComponents$1(Trees.scala:1258)
[error]     at scala.reflect.internal.Trees$class.itraverse(Trees.scala:1323)
[error]     at scala.reflect.internal.SymbolTable.itraverse(SymbolTable.scala:16)
[error]     at scala.reflect.internal.SymbolTable.itraverse(SymbolTable.scala:16)
@smithjessk
Copy link
Collaborator Author

This call to isCoroutineDefMarker is problematic because typer.typeOf(co) == null. Thus, I doubt that the issue is related to @retronym's comment.

@smithjessk
Copy link
Collaborator Author

smithjessk commented Jun 25, 2016

This test generates this problematic rawlambda:

raw = (() => {
  val f: PartialFunction[Int,Int] = (({
    class $anonfun extends scala.runtime.AbstractPartialFunction[Int,Int] with Serializable {
      def <init>(): <$anon: Int => Int> = {
        $anonfun.super.<init>();
        ()
      };
      final override def applyOrElse[A1 <: Int, B1 >: Int](x1: A1, default: A1 => B1): B1 = ((x1.asInstanceOf[Int]: Int): Int @unchecked) match {
        case (x @ _) => x.+(1)
        case (defaultCase$ @ _) => default.apply(x1)
      };
      final def isDefinedAt(x1: Int): Boolean = ((x1.asInstanceOf[Int]: Int): Int @unchecked) match {
        case (x @ _) => true
        case (defaultCase$ @ _) => false
      }
    };
    new $anonfun()
  }: PartialFunction[Int,Int]): PartialFunction[Int,Int]);
  AsyncAwaitTest.await[Int].apply[(scala.concurrent.Future[Int], org.coroutines.AsyncAwaitTest.Cell[Int]), Int](scala.concurrent.Future.apply[Int](f.apply(2))(scala.concurrent.ExecutionContext.Implicits.global))(Predef.this.=:=.tpEquals[((scala.concurrent.Future[Int], org.coroutines.AsyncAwaitTest.Cell[Int]), Int)])
})

Canonicalization fails when going over default.apply(x1).

typer.typeOf(co) == null because default is never added to typer.treeMapping and co.tpe == null. default is never added because this case does not cause traversal of the second cases inside the above partial functions.

@axel22
Copy link
Member

axel22 commented Jun 26, 2016

Can you extend traverser to correctly cover the other cases?

@smithjessk
Copy link
Collaborator Author

Yes! I'm working on it in this branch.

@smithjessk
Copy link
Collaborator Author

smithjessk commented Jul 6, 2016

It is not the second case that is dropped by the quasiquotes. Rather, it is the default case, e.g.:

case (defaultCase$ @ _) => default.apply(x1)

For instance, see that this partial function:

val f = { 
  case x: Int => x + 1
  case x: String => x.length 
}: PartialFunction[Any, Int]

has the following values for cs0 and cs1, respectively:

List(case (x @ (_: scala.Int)) => x.+(1), case (x @ (_: scala.this.Predef.String)) => x.length())
List(case (x @ (_: Int)) => x.+(1), case (x @ (_: String)) => x.length())

Thus, we might want to stop canonicalization for the default cases. What do you think?

@axel22
Copy link
Member

axel22 commented Jul 9, 2016

No, that's not the problem.
Add a println statement to case _ in TraverserUtil. The anonymous partial function class was not traversed by it for some reason. Find out why, fix TraverserUtil, and this should work.

@smithjessk
Copy link
Collaborator Author

I think I'm misunderstanding your statement.

The partial function case matches on the anonymous partial function. This call to traverse is caused by the ascription case, in which e0 is the anonymous class that extends AbstractPartialFunction.

println("t0 = " + t0) inside the PF case prints the following output:

t0 = ({
  final <synthetic> class $anonfun extends scala.runtime.AbstractPartialFunction[Int,Int] with Serializable {
    def <init>() = {
      super.<init>();
      ()
    };
    final override def applyOrElse[A1 <: Int, B1 >: Int](x1: A1, default: scala.this.Function1[A1, B1]) = ((x1.asInstanceOf[Int]: Int): (x1.asInstanceOf[Int]: Int): @scala.unchecked) match {
      case (x @ _) => x.+(1)
      case (defaultCase$ @ _) => default.apply(x1)
    };
    final def isDefinedAt(x1: Int): Boolean = ((x1.asInstanceOf[Int]: Int): (x1.asInstanceOf[Int]: Int): @scala.unchecked) match {
      case (x @ _) => true
      case (defaultCase$ @ _) => false
    }
  };
  new $anonfun()
}: PartialFunction[Int,Int])

However, defaultCase is not a part of cs0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants