Skip to content

Commit

Permalink
WIP Emit trait method bodies in statics
Browse files Browse the repository at this point in the history
And use this as the target of the default methods or
statically resolved super or $init calls.

The call-site change is predicated on `-Yuse-trait-statics`
as a stepping stone for experimentation / bootstrapping.

I have performed this transformation in the backend,
rather than trying to reflect this in the view from
Scala symbols + ASTs.

Once we commit to this change, we can remove the
`lateInterfaces` bookkeeping from the backend, as we no
long will need to add ancestor interfaces as direct
parents to allow use of invokespecial for super calls.

```
> ;scalac sandbox/test.scala ; scala Test

[info] Running scala.tools.nsc.MainGenericRunner -usejavacp Test
T
C
[success] Total time: 2 s, completed 04/05/2016 11:07:13 AM
> eval "javap -classpath . -c -private C".!!
[info] ans: String = Compiled from "test.scala"
[info] public class C implements T {
[info]   public C();
[info]     Code:
[info]        0: aload_0
[info]        1: invokespecial #14                 // Method java/lang/Object."<init>":()V
[info]        4: aload_0
[info]        5: invokespecial #17                 // Method T.$init$:()V
[info]        8: getstatic     #23                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
[info]       11: ldc           #24                 // String C
[info]       13: invokevirtual #28                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
[info]       16: aload_0
[info]       17: invokespecial #32                 // Method T.foo:()I
[info]       20: pop
[info]       21: return
[info] }
> ;scalac -Yuse-trait-statics sandbox/test.scala ; scala Test

[info] Running scala.tools.nsc.MainGenericRunner -usejavacp Test
T
C
[success] Total time: 2 s, completed 04/05/2016 11:07:39 AM
> eval "javap -classpath . -c -private C".!!
[info] ans: String = Compiled from "test.scala"
[info] public class C implements T {
[info]   public C();
[info]     Code:
[info]        0: aload_0
[info]        1: invokespecial #14                 // Method java/lang/Object."<init>":()V
[info]        4: aload_0
[info]        5: invokestatic  #18                 // Method T.$init$:(LT;)V
[info]        8: getstatic     #24                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
[info]       11: ldc           #25                 // String C
[info]       13: invokevirtual #29                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
[info]       16: aload_0
[info]       17: invokestatic  #33                 // Method T.foo:(LT;)I
[info]       20: pop
[info]       21: return
[info] }
> eval "javap -classpath . -c -private T".!!
[info] ans: String = Compiled from "test.scala"
[info] public interface T {
[info]   public static int foo(T);
[info]     Code:
[info]        0: iconst_0
[info]        1: ireturn
[info]
[info]   public int foo();
[info]     Code:
[info]        0: aload_0
[info]        1: invokestatic  #15                 // Method foo:(LT;)I
[info]        4: ireturn
[info]
[info]   public static void $init$(T);
[info]     Code:
[info]        0: getstatic     #24                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
[info]        3: ldc           #25                 // String T
[info]        5: invokevirtual #29                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
[info]        8: return
[info]
[info]   public void $init$();
[info]     Code:
[info]        0: aload_0
[info]        1: invokestatic  #32                 // Method $init$:(LT;)V
[info]        4: return
[info] }
```
  • Loading branch information
retronym committed May 4, 2016
1 parent e82b0a9 commit 3abf414
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 5 deletions.
2 changes: 1 addition & 1 deletion project/ScalaOptionParser.scala
Expand Up @@ -90,7 +90,7 @@ object ScalaOptionParser {
"-Ypresentation-strict", "-Ypresentation-verbose", "-Yquasiquote-debug", "-Yrangepos", "-Yreify-copypaste", "-Yreify-debug", "-Yrepl-class-based",
"-Yrepl-sync", "-Yshow-member-pos", "-Yshow-symkinds", "-Yshow-symowners", "-Yshow-syms", "-Yshow-trees", "-Yshow-trees-compact", "-Yshow-trees-stringified", "-Ytyper-debug",
"-Ywarn-adapted-args", "-Ywarn-dead-code", "-Ywarn-inaccessible", "-Ywarn-infer-any", "-Ywarn-nullary-override", "-Ywarn-nullary-unit", "-Ywarn-numeric-widen", "-Ywarn-unused", "-Ywarn-unused-import", "-Ywarn-value-discard",
"-deprecation", "-explaintypes", "-feature", "-help", "-no-specialization", "-nobootcp", "-nowarn", "-optimise", "-print", "-unchecked", "-uniqid", "-usejavacp", "-usemanifestcp", "-verbose", "-version")
"-deprecation", "-explaintypes", "-feature", "-help", "-no-specialization", "-nobootcp", "-nowarn", "-optimise", "-print", "-unchecked", "-uniqid", "-usejavacp", "-usemanifestcp", "-verbose", "-version", "-Yuse-trait-statics")
private def stringSettingNames = List("-Xgenerate-phase-graph", "-Xmain-class", "-Xpluginsdir", "-Xshow-class", "-Xshow-object", "-Xsource-reader", "-Ydump-classes", "-Ygen-asmp",
"-Ypresentation-log", "-Ypresentation-replay", "-Yrepl-outdir", "-d", "-dependencyfile", "-encoding", "-Xscript")
private def pathSettingNames = List("-bootclasspath", "-classpath", "-extdirs", "-javabootclasspath", "-javaextdirs", "-sourcepath", "-toolcp")
Expand Down
Expand Up @@ -1061,7 +1061,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
val receiverName = internalName(receiverClass)

// super calls are only allowed to direct parents
if (style.isSuper && receiverClass.isTraitOrInterface && !cnode.interfaces.contains(receiverName)) {
if (!settings.YuseTraitStatics.value && style.isSuper && receiverClass.isTraitOrInterface && !cnode.interfaces.contains(receiverName)) {
thisBType.info.get.inlineInfo.lateInterfaces += receiverName
cnode.interfaces.add(receiverName)
}
Expand All @@ -1082,7 +1082,11 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
case Virtual =>
if (needsInterfaceCall(receiverClass)) bc.invokeinterface(receiverName, jname, mdescr, pos)
else bc.invokevirtual (receiverName, jname, mdescr, pos)
case Super => bc.invokespecial (receiverName, jname, mdescr, pos)
case Super =>
if (receiverClass.isTraitOrInterface && settings.YuseTraitStatics.value) {
val staticDesc = MethodBType(typeToBType(method.owner.info) :: method.info.paramTypes.map(typeToBType), typeToBType(method.info.resultType)).descriptor
bc.invokestatic(receiverName, jname, staticDesc, pos)
} else bc.invokespecial (receiverName, jname, mdescr, pos)
}

bmType.returnType
Expand Down
24 changes: 22 additions & 2 deletions src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
Expand Up @@ -489,7 +489,27 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {

case ValDef(mods, name, tpt, rhs) => () // fields are added in `genPlainClass()`, via `addClassFields()`

case dd : DefDef => genDefDef(dd)
case dd : DefDef =>
if (dd.symbol.owner.isTrait && dd.rhs != EmptyTree && !dd.symbol.isPrivate) {
// Split concrete methods in traits (including mixin constructors) into a static method
// with an explicit this parameter, and a non-static forwarder method.
val staticDefDef = {
val staticSym = dd.symbol.cloneSymbol
staticSym.setFlag(Flags.STATIC)
val selfParamSym = staticSym.newSyntheticValueParam(dd.symbol.owner.info, nme.SELF).setInfo(dd.symbol.owner.info)
staticSym.modifyInfo {
case mt @ MethodType(params, res) => copyMethodType(mt, selfParamSym :: params, res)
}
val selfParam = ValDef(selfParamSym)
treeCopy.DefDef(dd, dd.mods, dd.name, dd.tparams, (selfParam :: dd.vparamss.head) :: Nil, dd.tpt, dd.rhs).setSymbol(staticSym)
}
val forwarderDefDef = {
val forwarderBody = Apply(global.gen.mkAttributedRef(staticDefDef.symbol), This(dd.symbol.owner).setType(dd.symbol.owner.info) :: dd.vparamss.head.map(p => global.gen.mkAttributedIdent(p.symbol))).setType(dd.symbol.info.resultType)
deriveDefDef(dd)(_ => global.atPos(dd.pos)(forwarderBody))
}
genDefDef(staticDefDef)
genDefDef(forwarderDefDef)
} else genDefDef(dd)

case Template(_, _, body) => body foreach gen

Expand Down Expand Up @@ -554,7 +574,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {

val isNative = methSymbol.hasAnnotation(definitions.NativeAttr)
val isAbstractMethod = rhs == EmptyTree
val flags = GenBCode.mkFlags(
var flags = GenBCode.mkFlags(
javaFlags(methSymbol),
if (isAbstractMethod) asm.Opcodes.ACC_ABSTRACT else 0,
if (methSymbol.isStrictFP) asm.Opcodes.ACC_STRICT else 0,
Expand Down
1 change: 1 addition & 0 deletions src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
Expand Up @@ -202,6 +202,7 @@ trait ScalaSettings extends AbsScalaSettings
val inferByName = BooleanSetting ("-Yinfer-by-name", "Allow inference of by-name types. This is a temporary option to ease transition. See SI-7899.").withDeprecationMessage(removalIn212)
val YclasspathImpl = ChoiceSetting ("-YclasspathImpl", "implementation", "Choose classpath scanning method.", List(ClassPathRepresentationType.Recursive, ClassPathRepresentationType.Flat), ClassPathRepresentationType.Flat)
val YdisableFlatCpCaching = BooleanSetting ("-YdisableFlatCpCaching", "Do not cache flat classpath representation of classpath elements from jars across compiler instances.")
val YuseTraitStatics = BooleanSetting ("-Yuse-trait-statics", "Emit super calls to trait methods with invokestatic")

val exposeEmptyPackage = BooleanSetting ("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly()
val Ydelambdafy = ChoiceSetting ("-Ydelambdafy", "strategy", "Strategy used for translating lambdas into JVM code.", List("inline", "method"), "method")
Expand Down

0 comments on commit 3abf414

Please sign in to comment.