Toolbox typecheck throws MatchError for higher-kinded path-dependent type

41 views
Skip to first unread message

Daniel Armak

unread,
Jul 28, 2015, 4:54:48 PM7/28/15
to scala-language

Hi,

I encountered this error trying to compile code that uses the shapeless ~?> typeclass with the toolbox, and simplified it to this:


import scala.reflect.runtime.currentMirror
import scala.tools.reflect.ToolBox

val tb = ToolBox(currentMirror).mkToolBox()
val source = """{
  import scala.language.higherKinds

  class Outer[X] {
    class Inner[Y]
  }
  class Other[T[_]]

  new Other[Outer[Int]#Inner]()
}"""

val tree = tb.parse(source)
tb.typecheck(tree).tpe

This fails with:

Exception in thread "main" scala.MatchError: <tree with deferred refcheck> (of class scala.tools.nsc.ast.Trees$TypeTreeWithDeferredRefCheck)
    at scala.reflect.internal.Importers$StandardImporter.recreateTree(Importers.scala:309)

(Full stack trace here)

I’m using scala 2.11.7.

Thanks,

Daniel Armak

Simon Schäfer

unread,
Jul 28, 2015, 7:13:55 PM7/28/15
to scala-l...@googlegroups.com
It is known that the ToolBox is full of bugs. I wouldn't bother with it at all - use a regular instance of Global instead.

Example:

import scala.reflect.internal.util.AbstractFileClassLoader
import scala.tools.nsc.Settings
import scala.reflect.io.PlainDirectory
import scala.reflect.io.VirtualDirectory
import scala.tools.nsc.settings.ScalaVersion
import scala.reflect.internal.util.BatchSourceFile
import scala.tools.nsc.interactive.Global
import scala.tools.nsc.reporters.StoreReporter
import scala.tools.nsc.interactive.Response

object CompilerTest extends App {

  val dir = new VirtualDirectory("<virtualdir>", None)
  // Gives access to all generated class files.
  val classLoader = new AbstractFileClassLoader(dir, getClass.getClassLoader)
  val s = new Settings(err ⇒ Console.err.println(err))
  s.outputDirs.setSingleOutput(dir)
  s.usejavacp.value = true
  s.source.value = ScalaVersion("2.11.7")

  // specify dependencies here
  s.bootclasspath.value = ""
  s.classpath.value = ""

  val reporter = new StoreReporter
  val compiler = new Global(s, reporter)
  val run = new compiler.Run
  val src = s"""
    class Test {
      val x = {
        0
      }
      val y = { x+1 }
    }
  """
  val srcFile = new BatchSourceFile("<memory>", src)
  val resp = new Response[compiler.Tree]
  compiler.askLoadedTyped(srcFile, resp)
  val tree = resp.get.left.get
  import compiler._

  ...
}
--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-languag...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jason Zaugg

unread,
Jul 28, 2015, 7:37:42 PM7/28/15
to scala-l...@googlegroups.com

Thanks for the report.

The bug is in Importers, which copies a tree from one compiler universe into another.

I notice that @mandubian also ran into the same problem.

It fails to account for a special tree, TypeTreeWithDeferredWithRefCheck that exists between the typer and refchecks compiler phases, and is used when typechecking higher kinded type applications.

I’ve lodged your report as SI-9421.

Fixing it would require adding a default case in src/reflect/scala/reflect/internal/Importers.scala importTree that forwarded to an extension hook that could be implemented in nsc.Global. This pattern is already used in TreePrinters, see in particular xprinttree

-jason

Jason Zaugg

unread,
Jul 28, 2015, 7:51:30 PM7/28/15
to scala-l...@googlegroups.com
On Wed, Jul 29, 2015 at 9:13 AM Simon Schäfer <ma...@antoras.de> wrote:
It is known that the ToolBox is full of bugs. I wouldn't bother with it at all - use a regular instance of Global instead.

That's good advice, but your example doesn't quite follow it :)

Here's how to use the regular global (rather than the presentation compiler global)


-jason

Oliver Ruebenacker

unread,
Jul 28, 2015, 8:07:58 PM7/28/15
to scala-l...@googlegroups.com

     Hello,

  Why, is there anything wrong with the presentation compiler? Thanks!

     Best, Oliver

--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-languag...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Oliver Ruebenacker
Senior Software Engineer, Diabetes Portal, Broad Institute

Jason Zaugg

unread,
Jul 28, 2015, 8:12:35 PM7/28/15
to scala-l...@googlegroups.com
On Wed, Jul 29, 2015 at 10:07 AM Oliver Ruebenacker <cur...@gmail.com> wrote:

     Hello,

  Why, is there anything wrong with the presentation compiler? Thanks!

I didn't mean to to pick on the presentation compiler, i was merely suggesting to pick the simplest tool that serves your purpose. In this case, I'd argue that nsc.Global is a simpler option than nsc.interactive.Global.

-jason 

Daniel Armak

unread,
Jul 29, 2015, 4:18:13 AM7/29/15
to scala-l...@googlegroups.com

This example produces an untyped tree (run.units.toList.head.body.tpe is NoType).

Is there a way to use Global to produce typed trees? Otherwise I won’t be able to do effective typechecking when compiling values whose type is subject to erasure.


Daniel Armak

--

Jason Zaugg

unread,
Jul 29, 2015, 8:59:54 AM7/29/15
to scala-l...@googlegroups.com
On Wed, Jul 29, 2015 at 6:18 PM Daniel Armak <dana...@gmail.com> wrote:

This example produces an untyped tree (run.units.toList.head.body.tpe is NoType).

An untyped tree has a null type, NoType is the result of typechecking things like PackageDef / ClassDef / DefDef trees.

I’ve updated the gist to include the output of show(tree, printTypes = true) that shows the types you’ll find deeper in the tree.

-jason

Daniel Armak

unread,
Jul 29, 2015, 9:26:23 AM7/29/15
to scala-l...@googlegroups.com
Thanks Jason!

So, if I only need to compare the compiled code's Types with the host code, and not to export or import Trees, then the ToolBox doesn't do anything that Global can't do better?

Can I use per-thread instances of Global in parallel as I do with ToolBox? The name 'Global' is a bit worrying in that regard.

Daniel Armak

--

Jason Zaugg

unread,
Jul 30, 2015, 4:01:29 AM7/30/15
to scala-l...@googlegroups.com
Toolbox's main claim to fame is that, like runtime reflection, it uses a classpath backed by a Java class loader. With a plain global you need a list of JARs or class directories (in my example we told it to get that from the -classpath arg of the JVM.

Toolbox also makes it convenient to run the code and to work with snippets , rather than complete compilation units.

Toolbox itself extends Global. For both of them, it's fine (and necessary) to have an instance per thread.

Jason

Oliver Ruebenacker

unread,
Jul 30, 2015, 7:10:19 AM7/30/15
to scala-l...@googlegroups.com

     Hello,

  Very roughly, how does treatment of code snippets compare among Toolbox, IMain and the standard REPL (aka Scala Shell)? Thanks!

     Best, Oliver

Jason Zaugg

unread,
Jul 30, 2015, 7:35:08 AM7/30/15
to scala-l...@googlegroups.com
On Thu, Jul 30, 2015 at 9:10 PM Oliver Ruebenacker <cur...@gmail.com> wrote:

     Hello,

  Very roughly, how does treatment of code snippets compare among Toolbox, IMain and the standard REPL (aka Scala Shell)? Thanks!

In the ToolBox, snippets are wrapped in something like:

package wrapper$N { object O { def m: Any = {
  <your code here>
}}

The REPL (aka IMain) has a more elaborate scheme to allow you to refer to symbols defined on previous lines.

-jason
Reply all
Reply to author
Forward
0 new messages