0

I took this Scala quasiquote example from the book "Programming Scala" (2nd Edition)

I am getting this error: https://issues.scala-lang.org/browse/SI-9711

The type inference says "Trees#Tree", but the type inference is off.

import scala.reflect.api.Trees // For Trees#Tree (TreeNode)
import scala.reflect.macros.blackbox._
import scala.reflect.runtime.universe._ // To use Scala runtime reflection

/**
  * Represents a macro invariant which is checked over the corresponding statements.
  * Example:
  * '''
  * var mustBeHello = "Hello"
  * invariant.execute(mustBeHello.equals("Hello")) {
  *   mustBeHello = "Goodbye"
  * }
  * // Throws invariant.InvariantFailure
  * '''
  */
object invariant {
  case class InvariantFailure(message: String) extends RuntimeException(message)

  type SyntaxTree = scala.reflect.runtime.universe.Tree

  type TreeNode = Trees#Tree // a syntax tree node that is in and of itself a tree

  // These two methods are the same, but one is a function call and the other is a macro function call
  def execute[RetType]              (myPredicate: => Boolean)(block: => RetType): RetType = macro executeMacro
  def executeMacro(context: Context)(myPredicate: SyntaxTree)(block: SyntaxTree) = {

    val predicateString: String = showCode(myPredicate) // turn this predicate into a String
    val q"..$statements" = block // make the block into a sequence of statements
    val myStatements: Seq[TreeNode] = statements // the statements are a sequence of SyntaxTreeNodes, each node a little Tree
    val invariantStatements = statements.flatMap { statement =>
        // Error here:
        val statementString: String = showCode(statement) /* Type mismatch, expected Tree, actual Trees#Tree */

        val message: String =
            s"FAILURE! $predicateString == false, for statement: " + statementString
        val tif: SyntaxTree =
            q"throw new metaprogramming.invariant.InvariantFailure($message)"
        val predicate2: SyntaxTree =
            q"if (false == $myPredicate) $tif"
        val toReturn: List[SyntaxTree] =
            List(q"{ val temp = $myStatements; $predicate2; temp };")
        toReturn
      }
    val tif: SyntaxTree =
        q"throw new metaprogramming.invariant.InvariantFailure($predicateString)"
    val predicate: SyntaxTree =
        q"if (false == $predicate) $tif"
    val toReturn: SyntaxTree =
        q"$predicate; ..$invariantStatements"
    toReturn
  }
}

^ The documentation should be self explanatory. The type inference says Tree#Tree, but adding ":Tree#Tree" to the example code kills compilation with error:

[info] Compiling 2 Scala sources to /home/johnreed/sbtProjects/scala-trace-debug/target/scala-2.11/test-classes...
[error] /home/johnreed/sbtProjects/scala-trace-debug/src/test/scala/mataprogramming/invariant2.scala:30: type mismatch;
[error] found : TreeNode
error scala.reflect.api.Trees#Tree
[error] required: context.universe.Tree
[error] val exceptionMessage = s"FAILURE! $predicateAsString == false, for statement: " + showCode(statement)

I'm getting "Type mismatch, expected Tree, actual Trees#Tree" in IntelliJ

Michael Lafayette
  • 2,972
  • 3
  • 20
  • 54
  • Where does the code come from? It's not in the repo. The types are path-dependent, which is why you use `c.Tree` etc. The repo compiles. – som-snytt Mar 20 '16 at 23:43
  • @som-snytt - This code is modified somewhat. The unmodified code from the repo compiles, but the types are off. See https://issues.scala-lang.org/browse/SI-9711 – Michael Lafayette Mar 21 '16 at 04:41
  • Your code isn't supposed to compile. You can't use a type project A#B when you mean a.B. I'll try your ticket code. – som-snytt Mar 21 '16 at 04:59

2 Answers2

0
[info] Compiling 2 Scala sources to /home/johnreed/sbtProjects/scala-trace-debug/target/scala-2.11/test-classes...
[error] /home/johnreed/sbtProjects/scala-trace-debug/src/test/scala/mataprogramming/invariant2.scala:30: type mismatch;
[error] found : TreeNode
error scala.reflect.api.Trees#Tree
[error] required: context.universe.Tree
[error] val exceptionMessage = s"FAILURE! $predicateAsString == false, for statement: " + showCode(statement)

There's something really funky with the types. Either IntelliJ is messing up the types or the notion escapes me.

Michael Lafayette
  • 2,972
  • 3
  • 20
  • 54
0

The "normal" way to discover an inferred type is one of:

  • ask the REPL using :type

  • assign to a bad type and observe the error message

  • call a function that displays a TypeTag of the thing

For example,

[error] /home/apm/clones/prog-scala-2nd-ed-code-examples/src/main/scala/progscala2/metaprogramming/invariant2.scala:25: type mismatch;
[error]  found   : List[context.universe.Tree]
[error]  required: Int
[error]     val foo: Int = statements
[error]                    ^

This shows that the Tree is path-dependent in the context universe.

You can't feed it just any old tree.

Similar issue at this question.

Community
  • 1
  • 1
som-snytt
  • 39,429
  • 2
  • 47
  • 129