sirthias / parboiled2

A macro-based PEG parser generator for Scala 2.10+
Other
717 stars 86 forks source link

ScalaReflectionException under Scala 2.11 when scala-reflect is not on the classpath #81

Closed sirthias closed 10 years ago

sirthias commented 10 years ago

Adding a dependency onto scala-reflect makes the problem go away, but adding this dependency shouldn't be required.

See this thread: https://groups.google.com/d/msg/parboiled-user/6351b1AQKCU/PKofnvtMTLYJ

jrudolph commented 10 years ago

I can reproduce it (check the gist):

Parser.scala:

import org.parboiled2._

sealed trait Command
case class Connect(host: String, port: Int) extends Command
case object Disconnect extends Command

class CommandParser(val input: ParserInput) extends Parser {

  def CommandLine = rule { WhiteSpace ~ Command ~ WhiteSpace ~ EOI }

  def Command = rule { DisconnectCommand | ConnectCommand }

  def ConnectCommand = rule { "connect" ~ (HostPort | Host) ~> Connect }

  def Host: Rule2[String, Int] = rule { capture(oneOrMore(CharPredicate.AlphaNum)) ~ push(9300) }

  def HostPort: Rule2[String, Int] = rule {
    capture(oneOrMore(CharPredicate.AlphaNum)) ~ WhiteSpace ~ capture(oneOrMore(CharPredicate.Digit)) ~> ((port: String) ⇒ port.toInt)
  }

  def DisconnectCommand = rule { "disconnect" ~ push(Disconnect) }

  def WhiteSpace = rule { zeroOrMore(' ') }

  implicit def wspStr(s: String): Rule0 = rule {
    str(s) ~ WhiteSpace
  }

}

build.sbt:

libraryDependencies ++= Seq(
  "org.parboiled" %% "parboiled" % "2.0.0"
  //,"org.scala-lang" % "scala-reflect" % "2.11.1" // fixes it
)

scalaVersion := "2.11.1"

Full stacktrace:

scala.ScalaReflectionException: object scala.reflect.macros.blackbox.Context in compiler mirror not found.  
    at scala.reflect.internal.Mirrors$RootsBase.staticClass(Mirrors.scala:123)                                 
    at scala.reflect.internal.Mirrors$RootsBase.staticClass(Mirrors.scala:22)                                  
    at org.parboiled2.support.OpTreeContext$Action$$typecreator28$1.apply(OpTreeContext.scala:560)             
    at scala.reflect.api.TypeTags$WeakTypeTagImpl.tpe$lzycompute(TypeTags.scala:231)                           
    at scala.reflect.api.TypeTags$WeakTypeTagImpl.tpe(TypeTags.scala:231)                                      
    at scala.reflect.api.TypeTags$class.typeOf(TypeTags.scala:335)                                             
    at scala.reflect.api.Universe.typeOf(Universe.scala:61)                                                    
    at org.parboiled2.support.OpTreeContext$Action.rewrite$2(OpTreeContext.scala:560)                          
    at org.parboiled2.support.OpTreeContext$Action.actionBody$2(OpTreeContext.scala:563)                       
    at org.parboiled2.support.OpTreeContext$Action.renderInner(OpTreeContext.scala:566)                        
    at org.parboiled2.support.OpTreeContext$OpTree.render(OpTreeContext.scala:42)                              
    at org.parboiled2.support.OpTreeContext$Sequence$$anonfun$renderInner$1.apply(OpTreeContext.scala:117)     
    at org.parboiled2.support.OpTreeContext$Sequence$$anonfun$renderInner$1.apply(OpTreeContext.scala:117)     
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:245)                        
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:245)                        
    at scala.collection.immutable.List.foreach(List.scala:383)                                                 
    at scala.collection.TraversableLike$class.map(TraversableLike.scala:245)                                   
    at scala.collection.immutable.List.map(List.scala:286)                                                     
    at org.parboiled2.support.OpTreeContext$Sequence.renderInner(OpTreeContext.scala:117)                      
    at org.parboiled2.support.OpTreeContext$OpTree.render(OpTreeContext.scala:42)                              
    at org.parboiled2.support.OpTreeContext$OpTree.renderRule(OpTreeContext.scala:33)                          
    at org.parboiled2.ParserMacros$$treecreator1$1.apply(Parser.scala:482)                                     
    at scala.reflect.api.Exprs$ExprImpl.tree$lzycompute(Exprs.scala:145)                                       
    at scala.reflect.api.Exprs$ExprImpl.tree(Exprs.scala:145)                                                  
    at scala.tools.nsc.typechecker.Macros$class.macroExpandWithRuntime(Macros.scala:779)                       

The line (OpTreeContext.scala:560) triggering it in parboiled2 is this:

case x if actionType.last <:< typeOf[Rule[_, _]] ⇒ expand(x, wrapped)

Any idea what could be going on, @xeno-by? Maybe typeOf[Rule] is triggering loading of Context because Rule contains macro definitions itself? The question is if scala-reflect then shouldn't be part of the mirror at macro expansion time.

xeno-by commented 10 years ago

Interestingly enough, it works if I compile the file using console invocation of scalac, adding parboiled and shapeless to the classpath.

xeno-by commented 10 years ago

The problem is in code that is generated in typeOf for actionType.last <:< typeOf[Rule[_, _]]. Inner workings of how existential types are represented in scalac lead to typeOf reifying more than necessary and accidentally creating a dependency on scala-reflect.

The fix would be to replace actionType.last <:< typeOf[Rule[_, _]] with an equivalent that doesn't involve typeOf, e.g. actionType.last.baseClasses.contains(c.mirror.staticClass("org.parboiled2.Rule")).

sirthias commented 10 years ago

Cool, thanks, Eugene! I'll give that a spin.

sirthias commented 10 years ago

Thanks again, problem solved.

fehmicansaglam commented 10 years ago

Thanks guys.