Skip to content

Backend Backports #4624

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

retronym
Copy link
Member

Previously been working on indylambda and the new backend on the 2.11.x branch
and merging the work forward. However, in the last few weeks, we've made a
few changes directly on the 2.12.x branch. The motivation for these was
a) to have Java 8 available to allow testing of the work, and b) eleventh hour
fixups for 2.11.0-M2.

This PR backports the important fixes to 2.11.x. These were low hanging fruit
(few merge conflicts), and low risk (only touching GenBCode / indylambda).

% git log 2.12.x --not 2.11.x --no-merges --since 2.months.ago --oneline -- src/compiler/
3bca6a2 SI-9387 Fix VerifyError introduced by indylambda
d1f2084 Integrate the LMFInvokeDynamic extractor into LambdaMetaFactoryCall
6e273f7 Small refactoring to the closure optimizer
e9c742e Accessibility checks for methods with an InvokeDynamic instruction
4e9be26 Fix bytecode stability
cd87823 Warn when combining settings for the old optimizer with -YGenBCode
c07442d Set -Xsource to 2.12 by default
80295ff Skip mirror class when invoking deserializeLambda
0f35054 Prevent infinite recursion in ProdConsAnalyzer
055a373 SI-9376 don't crash when inlining a closure body that throws.
9fa18cc SI-9377 Update ScalaVersion error text
f187bde SI-9377 ScalaVersion init no longer fails if versionless
c42428d Fix superclass for Java interface symbols created in JavaMirrors
c58f4e4 Include sources for scala-java8-compat instead of jar
d2cffb9 `deserializeLambda` should not use encoded class name
373db1e Java parser: default methods in interfaces are not `DEFERRED`
7a07693 SI-9350 Command option -Xreporter
0a25d8b SI-6613 fixed in GenBCode
c8e7fdc  Default to delambdafy:method and backend:GenBCode
8d2d3c7 Require and target Java 8
bd42db9 SI-9326 Binding to existentials can discard the prefix
09dbf3f SI-9326 Fix regression with existentials in parent types

% git log 2.11.x..head --oneline
d146456 [backport] Prevent infinite recursion in ProdConsAnalyzer
85e0bd9 [backport] SI-9376 don't crash when inlining a closure body that throws.
057d816 [backport] Skip mirror class when invoking deserializeLambda
7e16e2b [backport] Accessibility checks for methods with an InvokeDynamic instruction
5b0eb76 [backport] Fix bytecode stability
7234084 [backport] SI-9387 Fix VerifyError introduced by indylambda
cf173c3 [backport] Fix superclass for Java interface symbols created in JavaMirrors
5d491ed [backport] Java parser: default methods in interfaces are not `DEFERRED`
65ef759 [backport] `deserializeLambda` should not use encoded class name

Review by @SethTisue / @lrytz

lrytz and others added 7 commits July 13, 2015 11:55
`javaBinaryName` returns the internal name of a class.
Also used in BTypesFromsymbols.classBTypeFromSymbol.

Weirdly, this was discovered due to a bizarre osgi bnd error:

```
[bnd] # addAll '/Users/luc/scala/scala/build/pack/lib/scala-library.jar' with :,
[bnd] # addAll '/Users/luc/scala/scala/build/osgi/scala-library.bnd' with ,
[bnd] 1 ERRORS
[bnd]  The default package '.' is not permitted by the Import-Package syntax.
[bnd]  This can be caused by compile errors in Eclipse because Eclipse creates
[bnd] valid class files regardless of compile errors.
[bnd] The following package(s) import from the default package [scala.collection.generic, scala.sys.process, scala.collection.parallel.mutable, scala.util, scala.collection.parallel.immutable, scala.reflect, scala.concurrent.impl, scala.util.hashing, scala.collection.parallel, scala.collection.convert, scala.io, scala, scala.collection.concurrent, scala.util.control, scala.beans, scala.concurrent.duration, scala.collection, scala.runtime, scala.math, scala.collection.mutable, scala.concurrent, scala.sys, scala.collection.immutable, scala.ref, scala.util.matching]
[bnd] /Users/luc/scala/scala/build/osgi/scala-library.bnd: bnd failed
```

Lukas diagnosed it as a problem of the generated `$deserializeLambda$` function:

One example is `scala/App$class`. Its bytecode contains this:
```
  private static synthetic $deserializeLambda$(Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object;
    GETSTATIC scala$divApp$class.$deserializeLambdaCache$ : Ljava/util/Map;
    [...]
```
so it's a static field read of a top-level class.

`$div` should obviously be `/` (which this commit rectifies)

(cherry picked from commit d2cffb9)
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.

(cherry picked from commit 373db1e)
…irrors

According to the spec [1] the superclass of an interface is always
Object.

Restores the tests that were moved to pending in bf951ec,
fixex part of SI-9374.

[1] https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.1

(cherry picked from commit c42428d)
As with regular `Apply`-s, we should compute the generated type
based on the function's type, rather than the expected type.

In the test case, the expected type was void. Now, we correctly
use the generated type of `scala/Function1`, which is enough
to generate a subsequent POP instruction.

The tree shape involved was:

```
arg0 = {
  {
    $anonfun()
  };
  scala.runtime.BoxedUnit.UNIT
}
```

(cherry picked from commit 3bca6a2)
When there are multiple closure allocations and invocations in the
same method, ensure that the callsites are re-written to the body
methods in a consistent order. Otherwsie the bytecode is not stable
(the local variable indices depend on the order in which the calls
are re-written)

(cherry picked from commit 4e9be26)
…truction

Implements the necessary tests to check if a method with an
InvokeDynamic instruction can be inlined into a destination class.

Only InvokeDynamic instructions with LambdaMetaFactory as bootstrap
methods can be inlined. The accessibility checks cannot be implemented
generically, because it depends on what the bootstrap method is doing.
In particular, the bootstrap method receives a Lookup object as
argument which can be used to access private methods of the class
where the InvokeDynamic method is located.

A comment in the inliner explains the details.

(cherry picked from commit e9c742e
 test removed for the 2.11.x backport as it requires Java 8
)
Generate the invocation to LambdaDeserializer.deserializeLambda by
loading the static MODULE$ field instead of calling the static method
in the mirror class.

This is more scala-y. Also, mirror classes don't have an InlineInfo
classfile attribute, so the inliner would yield a warning about
the mirror class callsite.

Also skip the stack map frame instruction - frames are computed by
the ams classfile writer.

(cherry picked from commit 80295ff)
@scala-jenkins scala-jenkins added this to the 2.11.8 milestone Jul 13, 2015
@retronym
Copy link
Member Author

These commits are sourced from:

#4602
#4597
#4599
#4617
#4607

If the closure body method has return type Nothing$, add an `ATHROW`
instruction after the callsite. This is required for computing stack
map frames, as explained in a comment in BCodeBodyBuilder.adapt.

Similar for closure bodies with return type Null$.

(cherry picked from commit 055a373)
@retronym retronym force-pushed the backport/2.12.x-to-2.11.x-20150713 branch from b4cd515 to 12bd178 Compare July 13, 2015 03:30
When an instruction is its own producer or consumer, the
`initialProducer` / `ultimateConsumer` methods would loop.
While loops or @tailrec annotated methods can generate such bytecode.

(cherry picked from commit 0f35054)
@retronym retronym force-pushed the backport/2.12.x-to-2.11.x-20150713 branch from 12bd178 to d146456 Compare July 13, 2015 03:30
retronym added 2 commits July 14, 2015 11:58
A macro in shapeless was generating a tree of the form:

```
{
	class C#2
	new C#2
}.setType(C#1)
```

This happened due to an error in the macro; it used untypecheck
to try to fix the owner-chain consistency problem, but kept a
reference to the previous version of the block-local class symbol
`C` and used this in the resulting tree.

This commit detects the particular situation we encountered, and
avoids the crash by not creating the `NestedInfo` for the
`BType` corresponding to `C#1`. The code comment discusses why I
think this is safe, and suggests a refactoring that would mean
we only ever try to construct `NestedInfo` when we are going to
need them.

(cherry picked from commit fc7cb9a)
These cause a crash in the build of Play. We should try to bring
these back once we have suitable annotation awareness. Perhaps
they should only be `devWarning`-s, though.

(cherry picked from commit 0b19aba)
@retronym
Copy link
Member Author

Added the just-merged commits from #4623 and #4621. That's all for now.

@lrytz
Copy link
Member

lrytz commented Jul 23, 2015

i added a few more backports here: https://github.com/scala/scala/compare/2.11.x...lrytz:backports?expand=1. I'll wait opening a PR because validation currently fails (#4653 (comment)).

There's one more thing I'd like to include: #4638, except for the change to use Long instead of Int for flags.

@lrytz lrytz closed this Jul 23, 2015
@lrytz lrytz mentioned this pull request Jul 24, 2015
@SethTisue SethTisue removed this from the 2.11.8 milestone Feb 23, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants