Skip to content

Commit

Permalink
Make FunctionN into be a functional interface
Browse files Browse the repository at this point in the history
And do the same for the specialized variants.

Tested by a Java source file that uses lambda syntax to create
instances of generic and specialized `Function{0,1}`.

Here's how the interfaces look now:

```
% javap -c -classpath /tmp/function 'scala.Function1'
Compiled from "Function1.scala"
public interface scala.Function1<T1, R> {
  public abstract R apply(T1);

  public <A> scala.Function1<A, R> compose(scala.Function1<A, T1>);
    Code:
       0: aload_0
       1: aload_1
       2: invokestatic  #18                 // Method scala/Function1$class.compose:(Lscala/Function1;Lscala/Function1;)Lscala/Function1;
       5: areturn

  public <A> scala.Function1<T1, A> andThen(scala.Function1<R, A>);
    Code:
       0: aload_0
       1: aload_1
       2: invokestatic  #24                 // Method scala/Function1$class.andThen:(Lscala/Function1;Lscala/Function1;)Lscala/Function1;
       5: areturn

  public abstract java.lang.String toString();

  public int apply$mcII$sp(int);
    Code:
       0: aload_0
       1: iload_1
       2: invokestatic  #110                // Method scala/Function1$class.apply$mcII$sp:(Lscala/Function1;I)I
       5: ireturn

  public long apply$mcJI$sp(int);
    Code:
       0: aload_0
       1: iload_1
       2: invokestatic  #115                // Method scala/Function1$class.apply$mcJI$sp:(Lscala/Function1;I)J
       5: lreturn
    ...
}

% javap -c -classpath /tmp/function 'scala.Function1$mcII$sp'
Compiled from "Function1.scala"
public interface scala.Function1$mcII$sp extends scala.Function1<java.lang.Object, java.lang.Object> {
  public java.lang.Object apply(java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: invokestatic  #16                 // Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
       5: invokeinterface #19,  2           // InterfaceMethod apply:(I)I
      10: invokestatic  #23                 // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
      13: areturn

  public abstract int apply$mcII$sp(int);

  public int apply(int);
    Code:
       0: aload_0
       1: iload_1
       2: invokeinterface #30,  2           // InterfaceMethod apply$mcII$sp:(I)I
       7: ireturn
}
```
  • Loading branch information
retronym committed Jul 14, 2015
1 parent 1a8b9aa commit 29c7c33
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 1 deletion.
37 changes: 36 additions & 1 deletion src/compiler/scala/tools/nsc/transform/Mixin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ package scala.tools.nsc
package transform

import symtab._
import Flags._
import reflect.internal.Flags._
import scala.collection.{ mutable, immutable }

abstract class Mixin extends InfoTransform with ast.TreeDSL {
Expand Down Expand Up @@ -1060,6 +1060,21 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
isOverriddenAccessor(other.getterIn(other.owner), clazz.info.baseClasses)
}

if (isFunctionSymbol(clazz) && clazz.isSpecialized) {
val sym = clazz.info.decl(nme.apply)
// <default> def apply(v1: Object)Object = apply(v1.unbox).box
val functionClass = clazz.baseClasses(1)
val genericApply = functionClass.info.member(nme.apply)
val bridge = genericApply.cloneSymbol(clazz, /*BRIDGE |*/ METHOD | DEFAULTMETHOD | DEFERRED).setPos(sym.pos)
addDefDef(bridge,
Apply(gen.mkAttributedSelect(gen.mkAttributedThis(sym.owner), sym), bridge.paramss.head.map(p => gen.mkAttributedIdent(p))))

// <deferred> def apply$mcII$sp(v1: Int)Int
val specializedApply = specializeTypes.specializedOverloaded(genericApply, exitingSpecialize(clazz.info.baseType(functionClass).typeArgs))
val m2 = specializedApply.cloneSymbol(clazz, METHOD | DEFERRED).setPos(sym.pos)
addDefDef(m2.setPos(sym.pos))
}

// for all symbols `sym` in the class definition, which are mixed in:
for (sym <- clazz.info.decls ; if sym hasFlag MIXEDIN) {
// if current class is a trait interface, add an abstract method for accessor `sym`
Expand Down Expand Up @@ -1167,6 +1182,26 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
case Apply(TypeApply(Select(qual, _), targ :: _), _) if isCastSymbol(sym) && (qual.tpe <:< targ.tpe) =>
qual

case dd @ DefDef(_, _, _, vparamss, _, EmptyTree) if isFunctionSymbol(sym.owner) =>
val addDefault = enteringPhase(currentRun.erasurePhase.prev)(!sym.isDeferred) && sym.name != nme.toString_ // before lateDEFERRED
if (addDefault) {
def implSym = implClass(sym.owner).info.member(sym.name)
sym.setFlag(Flags.DEFAULTMETHOD)
val tree = Apply(staticRef(implSym), gen.mkAttributedThis(sym.owner) :: sym.paramss.head.map(gen.mkAttributedRef))
val app = typedPos(tree.pos)(tree)
copyDefDef(dd)(rhs = app)
} else if (sym.owner.isSpecialized && sym.name == nme.apply) {
val clazz = sym.owner
val functionClass = clazz.baseClasses(1)
val substitutedApply = clazz.info.decl(nme.apply)
val genericApply = functionClass.info.decl(nme.apply)
val specializedApply = specializeTypes.specializedOverloaded(genericApply, exitingSpecialize(clazz.info.baseType(functionClass).typeArgs))
val app = Apply(gen.mkAttributedSelect(gen.mkAttributedThis(clazz), specializedApply), vparamss.head.map(p => gen.mkAttributedIdent(p.symbol)))
dd.symbol.flags = dd.symbol.flags | Flags.DEFAULTMETHOD
copyDefDef(dd)(rhs = typedPos(tree.pos)(app))
} else {
tree
}
case Apply(Select(qual, _), args) =>
/* Changes `qual.m(args)` where m refers to an implementation
* class method to Q.m(S, args) where Q is the implementation module of
Expand Down
5 changes: 5 additions & 0 deletions src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,11 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
}
}

def specializedOverloaded(sym: Symbol, args: List[Type]) = exitingSpecialize {
val env: TypeEnv = TypeEnv.fromSpecialization(sym.owner, args)
overloads(sym).find(_.matchesEnv(env)).map(_.sym).getOrElse(NoSymbol)
}

/** Return the specialized name of 'sym' in the given environment. It
* guarantees the same result regardless of the map order by sorting
* type variables alphabetically.
Expand Down
23 changes: 23 additions & 0 deletions test/files/run/function-defaults/Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
public class Test {
static void assertTrue(boolean b) {
if (!b) throw new AssertionError();
}
@SuppressWarnings("unchecked")
public static void main(String[] args) {
scala.Function0<String> f0_s = (()-> "s");
assertTrue(f0_s.apply().equals("s"));

scala.Function1<String, String> f1_ss = (x -> x);
assertTrue(f1_ss.apply("").equals(""));

scala.Function0$mcI$sp f0_i = () -> 42;
assertTrue(f0_i.apply().equals(42));
assertTrue(f0_i.apply$mcI$sp() == 42);
scala.Function0 f0_raw = f0_i;
assertTrue(f0_raw.apply().equals(Integer.valueOf(42)));

scala.Function1$mcII$sp f1_ii = (x -> -x);
scala.Function1 f1_raw = f1_ii;
assertTrue(f1_raw.apply(1).equals(Integer.valueOf(-1)));
}
}

0 comments on commit 29c7c33

Please sign in to comment.