A new optimizer setting-opt-inline-from limits the classes from which the inliner will copy code. scalac -opt-inline-from:help provides detailed help.
The presentation compiler has improved support for implicit macros, which should improve the experience with libraries based on Shapeless. It also has better support for code completion for dependently typed methods.
The REPL is undergoing renovations and features better positioning of errors and a cleaner internal implementation. multi-line editing and syntax highlighting.
Right associative methods with a by-name parameter no longer eagerly evaluate the operand.
Furthermore, all changes in the 2.12.3 will be part of 2.13.0-M2.
What’s next?
We are planning to make 2.13.x the default branch for pull requests after these releases to help us focus our collective attention on working towards 2.13.0. We’ll still target some changes to 2.12.x that are high reward and low risk, that fix important regressions.
One question: by default is the inliner on 2.12 inlining from any class in the classpath?
The reason I ask is that bazel uses a technique to create interface jars of removing all the code in the classes and leaving only the method signatures. This is so the hash of the interface jars does not change when only private methods change. Clearly, these jars are not suitable for inlining as they lack any code.
However, we also don’t know what classes are in those jars typically. We just know the jar names. So, if by default we inline from everywhere in 2.12, the only safe move will be to set the inliner to only inline from I guess scala.** or something.
For us, the ideal would probably be to allow inlining from the source you are compiling, but none of the jars. Is that the same as limiting the class inlining to scala.** or similar?
No one has implemented 2.12 support in the bazel scala rules yet:
so we have not hit this issue, but we are considering how to do this now.
Firstly, its worth reiterating that we’d like people to move from scalac -optimize to scalac -opt:.... The new option requires an explicit opt-in to an inlining policy.
For backwards compatibility, we deprecated the old option with the following advice:
% scalac -deprecation -optimize sandbox/test.scala
warning: -optimise is deprecated: In 2.12, -optimise enables -opt:l:inline -opt-inline-from:**. Check -opt:help for using the Scala 2.12 optimizer.
Previous releases of 2.12 had a course grained way to select where to inline from. These are now also deprecated in favour of the new option.
l:project [deprecated, use -opt:l:inline, -opt-inline-from] Enable cross-method optimizations within the current project.
l:classpath [deprecated, use -opt:l:inline, -opt-inline-from] Enable cross-method optimizations across the entire classpath.
We no longer to express an inlining rule to mean “the sources in the current compile run”. We left the possibility open to add -opt-inline-from:<sources> for this purpose, but we haven’t implemented it yet.
I think it is preferable to use a package based approach so that scalac A.scala B.scala has the same end result as scalac A.scala B.scala; scalac A.scala, as is common under incremental compilation. I suppose for use cases when you know you are performing a full build, the <sources> option would be more convenient.
Okay. l:project seems like exactly what we would want. But you say that is deprecated, but I guess it will still work. When did it get deprecated? In 2.12.3 (the upcoming release?). That’s a shame since it seems the only convenient way to use optimizations with bazel.
We get incrementalism in bazel by having small targets and having bazel only invoke scalac on the portions of the graph that have changed (since bazel needs to manage all the state of the build in order to give its correctness guarantees).
BTW, unless you’re preserving the @ScalaSignature and @ScalaLongSignature annotations (aka the “pickle”) which contain the Scala-type-system-view of the API, you’ll get some spurious non-compiles.
Here’s an example of a change that is invisible to Java.
% for i in Int Float; do scalac $(f "class Test(a: Option[$i])"); ~/bin/jardiff -g /tmp/diff -q Test.class; done
% (cd /tmp/diff && git show)
commit e7c345ab1e10a0020c5de1e43d9dc9aa8c9180b4 (HEAD -> master)
Author: Jason Zaugg <[email protected]>
Date: Fri Jul 7 19:48:51 2017 +0200
jardiff textified output of: Test.class
diff --git a/Test.class.scalap b/Test.class.scalap
index 2313292..f03499a 100644
--- a/Test.class.scalap
+++ b/Test.class.scalap
@@ -1,3 +1,3 @@
class Test extends scala.AnyRef {
- def this(a: scala.Option[scala.Int]) = { /* compiled code */ }
+ def this(a: scala.Option[scala.Float]) = { /* compiled code */ }
}
Our jardiff tool also compares the output of scalap, which is based on the pickles, and only in that output is the change visible.
The pickle contains more info than you want, notably all private methods are in there. You’d need a diet pickle with just public stuff.
At least for SBT (or Zinc based incremental compilation), the recommendation is to disable the inliner during development builds. If you want to enable it at all, then do so in your CI and release builds. SBT doesn’t make any guarantees about correctness of incremental compilation with it enabled.
We use -opt:l:project everywhere in our libraries because its the optimizer setting that you are meant to use when deploying libraries since it only optimizes what is visible in your project
I would also advice in deprecating it (however it is correct that for local building you shouldn’t be using this setting since it trips up the incremental compilation, you should only use this flag when packaging)
I think not, they are parsing classfile attributes, not annotations, at this point. The ScalaLongSignature is an annotation. AFAIK there was no (need for a) long signature back when the pickle was stored in an attribute.
That doesn’t work: since 2.10 the scala signature is no longer stored in the ScalaSig classfile attribute, but in a RuntimeVisibleAnnotationscala/reflect/ScalaSignature (or ScalaLongSignature for very large pickles).
class C {
def f = 1
}
gives (disassembled with asm)
// class version 52.0 (52)
// access flags 0x21
public class C {
// compiled from: Test.scala
@Lscala/reflect/ScalaSignature;(bytes="\u0006\u0001]1A!\u0001\u0002\u0001\u000b\u0009\u00091IC\u0001\u0004\u0003\u001daT-\u001c9usz\u001a\u0001a\u0005\u0002\u0001\rA\u0011qAC\u0007\u0002\u0011)\u0009\u0011\"A\u0003tG\u0006d\u0017-\u0003\u0002\u000c\u0011\u00091\u0011I\\=SK\u001aDQ!\u0004\u0001\u0005\u00029\u0009a\u0001P5oSRtD#A\u0008\u0011\u0005A\u0001Q\"\u0001\u0002\u0009\u000bI\u0001A\u0011A\n\u0002\u0003\u0019,\u0012\u0001\u0006\u0009\u0003\u000fUI!A\u0006\u0005\u0003\u0007%sG\u000f")
ATTRIBUTE ScalaSig : unknown
ATTRIBUTE ScalaInlineInfo : unknown
...
The ScalaSig attribute is just kept as a marker that the classfile has a Scala signature (stored in the annotation), but the attribute itself is empty.
Though if I understand the ijar code correctly, all RuntimeVisibleAnnotations are kept, so that would include Scala signatures.