Skip to content

Commit a5e9c86

Browse files
retronymadriaanm
authored andcommitted
Make FunctionN into be a functional interface
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 scala#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 scala#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 scala#30, 2 // InterfaceMethod apply$mcII$sp:(I)I 7: ireturn } ```
1 parent 5f9b84d commit a5e9c86

File tree

3 files changed

+64
-1
lines changed

3 files changed

+64
-1
lines changed

src/compiler/scala/tools/nsc/transform/Mixin.scala

+36-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ package scala.tools.nsc
77
package transform
88

99
import symtab._
10-
import Flags._
10+
import reflect.internal.Flags._
1111
import scala.collection.{ mutable, immutable }
1212

1313
abstract class Mixin extends InfoTransform with ast.TreeDSL {
@@ -1060,6 +1060,21 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
10601060
isOverriddenAccessor(other.getterIn(other.owner), clazz.info.baseClasses)
10611061
}
10621062

1063+
if (isFunctionSymbol(clazz) && clazz.isSpecialized) {
1064+
val sym = clazz.info.decl(nme.apply)
1065+
// <default> def apply(v1: Object)Object = apply(v1.unbox).box
1066+
val functionClass = clazz.baseClasses(1)
1067+
val genericApply = functionClass.info.member(nme.apply)
1068+
val bridge = genericApply.cloneSymbol(clazz, /*BRIDGE |*/ METHOD | DEFAULTMETHOD | DEFERRED).setPos(sym.pos)
1069+
addDefDef(bridge,
1070+
Apply(gen.mkAttributedSelect(gen.mkAttributedThis(sym.owner), sym), bridge.paramss.head.map(p => gen.mkAttributedIdent(p))))
1071+
1072+
// <deferred> def apply$mcII$sp(v1: Int)Int
1073+
val specializedApply = specializeTypes.specializedOverloaded(genericApply, exitingSpecialize(clazz.info.baseType(functionClass).typeArgs))
1074+
val m2 = specializedApply.cloneSymbol(clazz, METHOD | DEFERRED).setPos(sym.pos)
1075+
addDefDef(m2.setPos(sym.pos))
1076+
}
1077+
10631078
// for all symbols `sym` in the class definition, which are mixed in:
10641079
for (sym <- clazz.info.decls ; if sym hasFlag MIXEDIN) {
10651080
// if current class is a trait interface, add an abstract method for accessor `sym`
@@ -1167,6 +1182,26 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
11671182
case Apply(TypeApply(Select(qual, _), targ :: _), _) if isCastSymbol(sym) && (qual.tpe <:< targ.tpe) =>
11681183
qual
11691184

1185+
case dd @ DefDef(_, _, _, vparamss, _, EmptyTree) if isFunctionSymbol(sym.owner) =>
1186+
val addDefault = enteringPhase(currentRun.erasurePhase.prev)(!sym.isDeferred) && sym.name != nme.toString_ // before lateDEFERRED
1187+
if (addDefault) {
1188+
def implSym = implClass(sym.owner).info.member(sym.name)
1189+
sym.setFlag(Flags.DEFAULTMETHOD)
1190+
val tree = Apply(staticRef(implSym), gen.mkAttributedThis(sym.owner) :: sym.paramss.head.map(gen.mkAttributedRef))
1191+
val app = typedPos(tree.pos)(tree)
1192+
copyDefDef(dd)(rhs = app)
1193+
} else if (sym.owner.isSpecialized && sym.name == nme.apply) {
1194+
val clazz = sym.owner
1195+
val functionClass = clazz.baseClasses(1)
1196+
val substitutedApply = clazz.info.decl(nme.apply)
1197+
val genericApply = functionClass.info.decl(nme.apply)
1198+
val specializedApply = specializeTypes.specializedOverloaded(genericApply, exitingSpecialize(clazz.info.baseType(functionClass).typeArgs))
1199+
val app = Apply(gen.mkAttributedSelect(gen.mkAttributedThis(clazz), specializedApply), vparamss.head.map(p => gen.mkAttributedIdent(p.symbol)))
1200+
dd.symbol.flags = dd.symbol.flags | Flags.DEFAULTMETHOD
1201+
copyDefDef(dd)(rhs = typedPos(tree.pos)(app))
1202+
} else {
1203+
tree
1204+
}
11701205
case Apply(Select(qual, _), args) =>
11711206
/* Changes `qual.m(args)` where m refers to an implementation
11721207
* class method to Q.m(S, args) where Q is the implementation module of

src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala

+5
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,11 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
314314
}
315315
}
316316

317+
def specializedOverloaded(sym: Symbol, args: List[Type]) = exitingSpecialize {
318+
val env: TypeEnv = TypeEnv.fromSpecialization(sym.owner, args)
319+
overloads(sym).find(_.matchesEnv(env)).map(_.sym).getOrElse(NoSymbol)
320+
}
321+
317322
/** Return the specialized name of 'sym' in the given environment. It
318323
* guarantees the same result regardless of the map order by sorting
319324
* type variables alphabetically.
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
public class Test {
2+
static void assertTrue(boolean b) {
3+
if (!b) throw new AssertionError();
4+
}
5+
@SuppressWarnings("unchecked")
6+
public static void main(String[] args) {
7+
scala.Function0<String> f0_s = (()-> "s");
8+
assertTrue(f0_s.apply().equals("s"));
9+
10+
scala.Function1<String, String> f1_ss = (x -> x);
11+
assertTrue(f1_ss.apply("").equals(""));
12+
13+
scala.Function0$mcI$sp f0_i = () -> 42;
14+
assertTrue(f0_i.apply().equals(42));
15+
assertTrue(f0_i.apply$mcI$sp() == 42);
16+
scala.Function0 f0_raw = f0_i;
17+
assertTrue(f0_raw.apply().equals(Integer.valueOf(42)));
18+
19+
scala.Function1$mcII$sp f1_ii = (x -> -x);
20+
scala.Function1 f1_raw = f1_ii;
21+
assertTrue(f1_raw.apply(1).equals(Integer.valueOf(-1)));
22+
}
23+
}

0 commit comments

Comments
 (0)