Skip to content

Commit

Permalink
[backport] Java parser: default methods in interfaces are not DEFERRED
Browse files Browse the repository at this point in the history
The Java parser should not set the `DEFERRED` flag for
default methods or static methods in interfaces.
Their bytecode doesn't have it either.

Also tightens parsing of Java abstract methods to
disallow a method body.

Here's the log of how Lukas diagnosed this:

```
quick.bin:
...
BUILD FAILED
/Users/luc/scala/scala/build.xml:69: The following error occurred while executing this line:
...
/Users/luc/scala/scala/build-ant-macros.xml:350: Could not create type mk-bin due to
java.lang.BootstrapMethodError: call site initialization exception
    at java.lang.invoke.CallSite.makeSite(CallSite.java:341)
    at java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:307)
    at java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:297)
    at scala.sys.BooleanProp$.keyExists(BooleanProp.scala:72)
    at scala.sys.SystemProperties$.bool(SystemProperties.scala:78)
    at scala.sys.SystemProperties$.noTraceSupression$lzycompute(SystemProperties.scala:89)
    at scala.sys.SystemProperties$.noTraceSupression(SystemProperties.scala:89)
    at scala.util.control.NoStackTrace$.<init>(NoStackTrace.scala:31)
    at scala.util.control.NoStackTrace$.<clinit>(NoStackTrace.scala)
    at scala.util.control.NoStackTrace$class.fillInStackTrace(NoStackTrace.scala:22)
    at scala.util.control.BreakControl.fillInStackTrace(Breaks.scala:94)
    at java.lang.Throwable.<init>(Throwable.java:250)
    at scala.util.control.BreakControl.<init>(Breaks.scala:94)
    at scala.util.control.Breaks.<init>(Breaks.scala:29)
    at scala.collection.Traversable$.<init>(Traversable.scala:95)
    at scala.collection.Traversable$.<clinit>(Traversable.scala)
    at scala.package$.<init>(package.scala:40)
    at scala.package$.<clinit>(package.scala)
    at scala.Predef$.<init>(Predef.scala:89)
    at scala.Predef$.<clinit>(Predef.scala)
    at scala.tools.ant.ScalaTool.<init>(ScalaTool.scala:58)
[...]
Caused by: java.lang.invoke.LambdaConversionException:
Incorrect number of parameters for static method invokeStatic
scala.sys.BooleanProp$.scala$sys$BooleanProp$$$anonfun$2$adapted:(String)Object;
0 captured parameters, 0 functional interface method parameters, 1 implementation parameters
    at java.lang.invoke.AbstractValidatingLambdaMetafactory.validateMetafactoryArgs(AbstractValidatingLambdaMetafactory.java:193)
    at java.lang.invoke.LambdaMetafactory.altMetafactory(LambdaMetafactory.java:473)
    at java.lang.invoke.CallSite.makeSite(CallSite.java:325)
```

[source code](https://github.com/scala/scala/blob/2.11.x/src/library/scala/sys/BooleanProp.scala#L72):

```
s => s == "" || s.equalsIgnoreCase("true")
```

bytecode:

```
    INVOKEDYNAMIC $init$()Lscala/compat/java8/JFunction1; [
      // handle kind 0x6 : INVOKESTATIC
      java/lang/invoke/LambdaMetafactory.altMetafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
      // arguments:
      ()V,
      // handle kind 0x6 : INVOKESTATIC
      scala/sys/BooleanProp$.scala$sys$BooleanProp$$$anonfun$2$adapted(Ljava/lang/String;)Ljava/lang/Object;,
      (Ljava/lang/String;)Ljava/lang/Object;,
      3,
      1,
      Lscala/Serializable;.class,
      0
    ]
    CHECKCAST scala/Function1
```

The mistake seems to be that the Scala compiler incorrectly selects `$init$`
([which is a default method](https://github.com/scala/scala/blob/640ffe7fceb5d573b2c12a7c7da09bfd751036a0/src/library/scala/compat/java8/JFunction1.java#L10))
as the abstract method of `JFunction1`, whereas it should be `apply` (inherited from `Function1`).

Since we're doing mixed compilation, this is almost certainly a problem of the Java parser.
  • Loading branch information
lrytz committed Jul 23, 2015
1 parent 44e2761 commit 8bafa8e
Show file tree
Hide file tree
Showing 2 changed files with 6 additions and 4 deletions.
8 changes: 5 additions & 3 deletions src/compiler/scala/tools/nsc/javac/JavaParsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -489,8 +489,8 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
val vparams = formalParams()
if (!isVoid) rtpt = optArrayBrackets(rtpt)
optThrows()
val isStatic = mods hasFlag Flags.STATIC
val bodyOk = !inInterface || ((mods hasFlag Flags.DEFAULTMETHOD) || isStatic)
val isConcreteInterfaceMethod = !inInterface || (mods hasFlag Flags.DEFAULTMETHOD) || (mods hasFlag Flags.STATIC)
val bodyOk = !(mods1 hasFlag Flags.DEFERRED) && isConcreteInterfaceMethod
val body =
if (bodyOk && in.token == LBRACE) {
methodBody()
Expand All @@ -509,7 +509,9 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
EmptyTree
}
}
if (inInterface && !isStatic) mods1 |= Flags.DEFERRED
// for abstract methods (of classes), the `DEFERRED` flag is alredy set.
// here we also set it for interface methods that are not static and not default.
if (!isConcreteInterfaceMethod) mods1 |= Flags.DEFERRED
List {
atPos(pos) {
DefDef(mods1, name.toTermName, tparams, List(vparams), rtpt, body)
Expand Down
2 changes: 1 addition & 1 deletion test/files/neg/t6013/Base.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ abstract public class Base {
// This must considered to be overridden by Abstract#foo based
// on the erased signatures. This special case is handled by
// `javaErasedOverridingSym` in `RefChecks`.
public abstract void bar(java.util.List<java.lang.String> foo) { return; }
public void bar(java.util.List<java.lang.String> foo) { return; }

// But, a concrete method in a Java superclass must not excuse
// a deferred method in the Java subclass!
Expand Down

0 comments on commit 8bafa8e

Please sign in to comment.