Compiler issue with inner class constructors?

132 views
Skip to first unread message

Adam Lewandowski

unread,
Feb 19, 2015, 2:55:18 PM2/19/15
to scala-i...@googlegroups.com
Hi all,
I ran into an issue yesterday trying to do some interop between scala and groovy. I think this may be a compiler issue (with a relatively simple fix), but wanted to check here before creating an issue/PR.

It looks like the scala classfile parser (in parseMethod() ) always expects that a constructor of a non-private, non-static inner class has an initial formal parameter that is the immediate enclosing instance. It asserts as much, and does some work to strip that parameter off. This is reasonable in light of JLS sections 8.8.1 and 8.8.9 which make this behavior clear (at least in java 8, these implicit parameters aren't mentioned in the java 7 and earlier specs). But that is the Java Language Spec, not the JVM spec. I have some Groovy classes which apparently don't conform to this specification, and when the scala compiler encounters these I get the following when it tries to take the head of the empty parameter list:
   [scalac] error: error while loading 1, class file '.../classes/GroovyClass$1.class' is broken
   [scalac] (class java.util.NoSuchElementException/head of empty list)

I readily admit that this can be categorized as an issue with Groovy (they're likely to have more interop problems if they don't comply with the expectations of the JLS), but it is still a valid JVM class and I think should be usable by scala. Is this a legitimate issue, and if I open a JIRA issue on this (with proposed fix), is it likely to be accepted?

Thanks, 
Adam Lewandowski

Jason Zaugg

unread,
Feb 20, 2015, 2:05:58 AM2/20/15
to scala-i...@googlegroups.com, scala-i...@googlegroups.com
Hi Adam, 

Great analysis. How does javac interop with that classfile?

-Jason


Sent from Mailbox


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

Adam Lewandowski

unread,
Feb 20, 2015, 10:17:45 AM2/20/15
to scala-i...@googlegroups.com
Javac doesn't seem to have a problem with it, but I don't know that javac would ever see it (I'm not familiar with how the compiler deals with referenced classes). It's a synthetic class that is never directly used by anything other than groovy internals, so I'm not sure that javac would ever have cause to run into this. Having looked at the class further, I think this is a problem with the groovy compiler and scalac is processing it correctly. The class is nested inside of an interface, but it is not marked as static (as javac does). I've opened an issue there (GROOVY-7312). 

I don't know if there's any harm, however, in making scalac resilient against it. Since the parsing code that causes the error is simply removing the first parameter, I think it's reasonable to bypass if there are in fact no parameters. It would just behave the same as if it were a 'normal' inner class constructor (without the implied parameter).

- Adam

Jason Zaugg

unread,
Feb 27, 2015, 9:09:50 PM2/27/15
to scala-i...@googlegroups.com, scala-i...@googlegroups.com
I’d be interested to find out why scalac is bothering to parse that class at all. Compiling with `-Ydebug` should offer a stack trace at the point of that exception.

Can you create a test project that reproduces the error?

-jason


Sent from Mailbox


On Fri, Feb 20, 2015 at 5:55 AM, Adam Lewandowski <adam.lew...@gmail.com> wrote:

--

Adam Lewandowski

unread,
Mar 1, 2015, 9:06:35 PM3/1/15
to scala-i...@googlegroups.com
There is a simple test project at https://github.com/alewando/scala_groovy_interop. Run the default ant target to reproduce the error. It requires SCALA_HOME and GROOVY_HOME (expects 2.4.0, change) set.

The generated stacktrace:
    java.util.NoSuchElementException: head of empty list
      at scala.collection.immutable.Nil$.head(List.scala:420)
      at scala.collection.immutable.Nil$.head(List.scala:417)
      at scala.tools.nsc.symtab.classfile.ClassfileParser.parseMethod(ClassfileParser.scala:575)
      at scala.tools.nsc.symtab.classfile.ClassfileParser.scala$tools$nsc$symtab$classfile$ClassfileParser$$queueLoad$1(ClassfileParser.scala:478)
      at scala.tools.nsc.symtab.classfile.ClassfileParser$$anonfun$parseClass$1.apply$mcV$sp(ClassfileParser.scala:488)
      at scala.tools.nsc.symtab.classfile.ClassfileParser.parseClass(ClassfileParser.scala:493)
      at scala.tools.nsc.symtab.classfile.ClassfileParser.parse(ClassfileParser.scala:134)
      at scala.tools.nsc.symtab.SymbolLoaders$ClassfileLoader$$anonfun$doComplete$2.apply$mcV$sp(SymbolLoaders.scala:309)
      at scala.tools.nsc.symtab.SymbolLoaders$ClassfileLoader$$anonfun$doComplete$2.apply(SymbolLoaders.scala:309)
      at scala.tools.nsc.symtab.SymbolLoaders$ClassfileLoader$$anonfun$doComplete$2.apply(SymbolLoaders.scala:309)
      at scala.reflect.internal.SymbolTable.enteringPhase(SymbolTable.scala:235)
      at scala.tools.nsc.symtab.SymbolLoaders$ClassfileLoader.doComplete(SymbolLoaders.scala:309)
      at scala.tools.nsc.symtab.SymbolLoaders$SymbolLoader.complete(SymbolLoaders.scala:211)
      at scala.reflect.internal.Symbols$Symbol.info(Symbols.scala:1481)
      at scala.tools.nsc.transform.AddInterfaces.scala$tools$nsc$transform$AddInterfaces$$isInterfaceMember(AddInterfaces.scala:44)
      at scala.tools.nsc.transform.AddInterfaces$$anonfun$6$$anonfun$apply$6.apply(AddInterfaces.scala:217)
      at scala.tools.nsc.transform.AddInterfaces$$anonfun$6$$anonfun$apply$6.apply(AddInterfaces.scala:216)
      at scala.collection.LinearSeqOptimized$class.forall(LinearSeqOptimized.scala:69)
      at scala.collection.immutable.List.forall(List.scala:83)
      at scala.reflect.internal.Scopes$Scope.filter(Scopes.scala:380)
      at scala.tools.nsc.transform.AddInterfaces$$anonfun$6.apply(AddInterfaces.scala:216)
      at scala.tools.nsc.transform.AddInterfaces$$anonfun$6.apply(AddInterfaces.scala:216)
      at scala.reflect.internal.Scopes$class.scopeTransform(Scopes.scala:453)
      at scala.reflect.internal.SymbolTable.scopeTransform(SymbolTable.scala:16)
      at scala.tools.nsc.transform.AddInterfaces.transformMixinInfo(AddInterfaces.scala:215)
      at scala.tools.nsc.transform.Erasure.transformInfo(Erasure.scala:348)
      at scala.tools.nsc.transform.InfoTransform$Phase$$anon$1.transform(InfoTransform.scala:38)
      at scala.reflect.internal.Symbols$Symbol.rawInfo(Symbols.scala:1571)
      at scala.reflect.internal.Symbols$Symbol.info(Symbols.scala:1491)
      at scala.reflect.internal.Types$TypeRef.thisInfo(Types.scala:2194)
      at scala.reflect.internal.Types$TypeRef.baseClasses(Types.scala:2199)
      at scala.reflect.internal.Types$class.computeBaseClasses(Types.scala:1439)
      at scala.reflect.internal.SymbolTable.computeBaseClasses(SymbolTable.scala:16)
      at scala.reflect.internal.Types$$anonfun$defineBaseClassesOfCompoundType$1.apply(Types.scala:1560)
      at scala.reflect.internal.Types$$anonfun$defineBaseClassesOfCompoundType$1.apply(Types.scala:1560)
      at scala.reflect.internal.Types$CompoundType.updateCache$1(Types.scala:1387)
      at scala.reflect.internal.Types$CompoundType.memo(Types.scala:1396)
      at scala.reflect.internal.Types$class.defineBaseClassesOfCompoundType(Types.scala:1560)
      at scala.reflect.internal.Types$class.define$1(Types.scala:1527)
      at scala.reflect.internal.Types$class.defineBaseClassesOfCompoundType(Types.scala:1528)
      at scala.reflect.internal.SymbolTable.defineBaseClassesOfCompoundType(SymbolTable.scala:16)
      at scala.reflect.internal.Types$CompoundType.baseClasses(Types.scala:1370)
      at scala.reflect.internal.Types$TypeRef.baseClasses(Types.scala:2199)
      at scala.reflect.internal.tpe.FindMembers$FindMemberBase.<init>(FindMembers.scala:17)
      at scala.reflect.internal.tpe.FindMembers$FindMember.<init>(FindMembers.scala:219)
      at scala.reflect.internal.Types$Type.scala$reflect$internal$Types$Type$$findMemberInternal$1(Types.scala:1014)
      at scala.reflect.internal.Types$Type.findMember(Types.scala:1016)
      at scala.reflect.internal.Types$Type.memberBasedOnName(Types.scala:631)
      at scala.reflect.internal.Types$Type.member(Types.scala:600)
      at scala.tools.nsc.backend.icode.GenICode$ICodePhase.findHostClass(GenICode.scala:78)
      at scala.tools.nsc.backend.icode.GenICode$ICodePhase.genLoadApply6$1(GenICode.scala:786)
      at scala.tools.nsc.backend.icode.GenICode$ICodePhase.scala$tools$nsc$backend$icode$GenICode$ICodePhase$$genLoad(GenICode.scala:809)
      at scala.tools.nsc.backend.icode.GenICode$ICodePhase.scala$tools$nsc$backend$icode$GenICode$ICodePhase$$genStat(GenICode.scala:181)
      at scala.tools.nsc.backend.icode.GenICode$ICodePhase$$anonfun$genStat$1.apply(GenICode.scala:155)
      at scala.tools.nsc.backend.icode.GenICode$ICodePhase$$anonfun$genStat$1.apply(GenICode.scala:155)
      at scala.collection.LinearSeqOptimized$class.foldLeft(LinearSeqOptimized.scala:110)
      at scala.collection.immutable.List.foldLeft(List.scala:83)
      at scala.tools.nsc.backend.icode.GenICode$ICodePhase.scala$tools$nsc$backend$icode$GenICode$ICodePhase$$genLoad(GenICode.scala:915)
      at scala.tools.nsc.backend.icode.GenICode$ICodePhase.scala$tools$nsc$backend$icode$GenICode$ICodePhase$$genLoad(GenICode.scala:916)
      at scala.tools.nsc.backend.icode.GenICode$ICodePhase.gen(GenICode.scala:123)
      at scala.tools.nsc.backend.icode.GenICode$ICodePhase.gen(GenICode.scala:71)
      at scala.tools.nsc.backend.icode.GenICode$ICodePhase.gen(GenICode.scala:148)
      at scala.tools.nsc.backend.icode.GenICode$ICodePhase.gen(GenICode.scala:98)
      at scala.tools.nsc.backend.icode.GenICode$ICodePhase.gen(GenICode.scala:71)
      at scala.tools.nsc.backend.icode.GenICode$ICodePhase.gen(GenICode.scala:89)
      at scala.tools.nsc.backend.icode.GenICode$ICodePhase.gen(GenICode.scala:67)
      at scala.tools.nsc.backend.icode.GenICode$ICodePhase.apply(GenICode.scala:63)
      at scala.tools.nsc.Global$GlobalPhase$$anonfun$applyPhase$1.apply$mcV$sp(Global.scala:428)
      at scala.tools.nsc.Global$GlobalPhase.withCurrentUnit(Global.scala:419)
      at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:428)
      at scala.tools.nsc.Global$GlobalPhase$$anonfun$run$1.apply(Global.scala:386)
      at scala.tools.nsc.Global$GlobalPhase$$anonfun$run$1.apply(Global.scala:386)
      at scala.collection.Iterator$class.foreach(Iterator.scala:743)
      at scala.collection.AbstractIterator.foreach(Iterator.scala:1195)
      at scala.tools.nsc.Global$GlobalPhase.run(Global.scala:386)
      at scala.tools.nsc.backend.icode.GenICode$ICodePhase.run(GenICode.scala:55)
      at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1338)
      at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1325)
      at scala.tools.nsc.Global$Run.compileSources(Global.scala:1320)
      at scala.tools.nsc.Global$Run.compile(Global.scala:1418)
      at scala.tools.ant.Scalac.executeInternal(Scalac.scala:686)
      at scala.tools.ant.Scalac.execute(Scalac.scala:634)
      at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:292)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:483)
      at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
      at org.apache.tools.ant.Task.perform(Task.java:348)
      at org.apache.tools.ant.Target.execute(Target.java:435)
      at org.apache.tools.ant.Target.performTasks(Target.java:456)
      at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1393)
      at org.apache.tools.ant.Project.executeTarget(Project.java:1364)
      at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:41)
      at org.apache.tools.ant.Project.executeTargets(Project.java:1248)
      at org.apache.tools.ant.Main.runBuild(Main.java:851)
      at org.apache.tools.ant.Main.startAnt(Main.java:235)
      at org.apache.tools.ant.launch.Launcher.run(Launcher.java:280)
      at org.apache.tools.ant.launch.Launcher.main(Launcher.java:109)
    java.io.IOException: class file '/private/tmp/scala_groovy_interop/classes/GroovyInterface$1.class' is broken

Jason Zaugg

unread,
Mar 2, 2015, 2:12:09 AM3/2/15
to scala-i...@googlegroups.com
On Mon, Mar 2, 2015 at 12:06 PM, Adam Lewandowski <adam.lew...@gmail.com> wrote:
There is a simple test project at https://github.com/alewando/scala_groovy_interop. Run the default ant target to reproduce the error. It requires SCALA_HOME and GROOVY_HOME (expects 2.4.0, change) set.

Hi Adam,

Thanks for creating the test project. It was really easy to use and reproduce the error.

I’ve prototyped a fix that helps in your situation; we simply had to change part of the compiler backend that is supposed to split a Scala traits into a interface / implementation class pair to ignore non-Scala defined classes. This stops it from ever needing to initialize the symbol the represents the inner class that Groovy emits.

I searched our bug database and found a closely related issue. In that bug, groovyc generates an InnerClass attribute in the enclosing class, but seems not to emit the other classfile at all. I have prototyped a fix for that that uses “stub” symbols in the Scala compiler to represent inner classes without corresponding bytecode.

Would you be interested in taking my prototype, testing it out further, and submitting it is a pull request? I’m more than happy to help you out with the process, but I’m a bit short of time this month to submit the patch myself.

I think we can argue that these changes aren't too "groovy specific", given that they bring us up to parity with javac.

Let’s continue the discussion over on the comments of SI-7741. Alternatively, feel free to ping me on https://gitter.im/scala/scala for help with the process.

-jason

Gerrit Jansen van Vuuren

unread,
Mar 2, 2015, 12:59:27 PM3/2/15
to scala-i...@googlegroups.com
Hi Adam,

I've had a related issue with groovy interop https://issues.scala-lang.org/browse/SI-7741

Regards,
 Gerrit

Adam Lewandowski

unread,
Mar 2, 2015, 1:29:58 PM3/2/15
to scala-i...@googlegroups.com
Thanks Jason and Gerrit. I'll take a look at Jason's branch this evening. If it resolves both of our issues and passes tests, I'll submit as a PR.
Reply all
Reply to author
Forward
0 new messages