diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 8ee016117de3..67e8f274cb2e 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -58,6 +58,7 @@ class Compiler { new ByNameClosures, // Expand arguments to by-name parameters to closures new LiftTry, // Put try expressions that might execute on non-empty stacks into their own methods new HoistSuperArgs, // Hoist complex arguments of supercalls to enclosing scope + new SpecializedApplyMethods, // Adds specialized methods to FunctionN new ClassOf), // Expand `Predef.classOf` calls. List(new TryCatchPatterns, // Compile cases in try/catch new PatternMatcher, // Compile pattern matches @@ -65,7 +66,8 @@ class Compiler { new ExplicitSelf, // Make references to non-trivial self types explicit as casts new ShortcutImplicits, // Allow implicit functions without creating closures new CrossCastAnd, // Normalize selections involving intersection types. - new Splitter), // Expand selections involving union types into conditionals + new Splitter, // Expand selections involving union types into conditionals + new SpecializeFunctions), // Specialized Function1 by replacing super with specialized super List(new VCInlineMethods, // Inlines calls to value class methods new IsInstanceOfEvaluator, // Issues warnings when unreachable statements are present in match/if expressions new SeqLiterals, // Express vararg arguments as arrays diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 0920ca946598..8792de6f3d32 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -736,12 +736,17 @@ class Definitions { def isBottomType(tp: Type) = tp.derivesFrom(NothingClass) || tp.derivesFrom(NullClass) - /** Is a function class. + /** Is any function class that satisfies: * - FunctionN for N >= 0 * - ImplicitFunctionN for N >= 0 */ def isFunctionClass(cls: Symbol) = scalaClassName(cls).isFunction + /** Is a function class where + * - FunctionN for N >= 0 + */ + def isPlainFunctionClass(cls: Symbol) = scalaClassName(cls).isPlainFunction + /** Is an implicit function class. * - ImplicitFunctionN for N >= 0 */ @@ -879,9 +884,9 @@ class Definitions { lazy val ScalaNumericValueTypeList = List( ByteType, ShortType, CharType, IntType, LongType, FloatType, DoubleType) - private lazy val ScalaNumericValueTypes: collection.Set[TypeRef] = ScalaNumericValueTypeList.toSet - private lazy val ScalaValueTypes: collection.Set[TypeRef] = ScalaNumericValueTypes + UnitType + BooleanType - private lazy val ScalaBoxedTypes = ScalaValueTypes map (t => boxedTypes(t.name)) + lazy val ScalaNumericValueTypes: collection.Set[TypeRef] = ScalaNumericValueTypeList.toSet + lazy val ScalaValueTypes: collection.Set[TypeRef] = ScalaNumericValueTypes + UnitType + BooleanType + lazy val ScalaBoxedTypes = ScalaValueTypes map (t => boxedTypes(t.name)) val ScalaNumericValueClasses = new PerRun[collection.Set[Symbol]](implicit ctx => ScalaNumericValueTypes.map(_.symbol)) val ScalaValueClasses = new PerRun[collection.Set[Symbol]](implicit ctx => ScalaValueTypes.map(_.symbol)) diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index df29a32c523c..ac3e20af4dc8 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -162,13 +162,17 @@ object NameOps { def functionArity: Int = functionArityFor(str.Function) max functionArityFor(str.ImplicitFunction) - /** Is a function name + /** Is any function name that satisfies * - FunctionN for N >= 0 * - ImplicitFunctionN for N >= 0 - * - false otherwise */ def isFunction: Boolean = functionArity >= 0 + /** Is a function name + * - FunctionN for N >= 0 + */ + def isPlainFunction: Boolean = functionArityFor(str.Function) >= 0 + /** Is a implicit function name * - ImplicitFunctionN for N >= 0 * - false otherwise @@ -209,23 +213,24 @@ object NameOps { case nme.clone_ => nme.clone_ } - def specializedFor(classTargs: List[Types.Type], classTargsNames: List[Name], methodTargs: List[Types.Type], methodTarsNames: List[Name])(implicit ctx: Context): name.ThisName = { - - def typeToTag(tp: Types.Type): Name = { - tp.classSymbol match { - case t if t eq defn.IntClass => nme.specializedTypeNames.Int - case t if t eq defn.BooleanClass => nme.specializedTypeNames.Boolean - case t if t eq defn.ByteClass => nme.specializedTypeNames.Byte - case t if t eq defn.LongClass => nme.specializedTypeNames.Long - case t if t eq defn.ShortClass => nme.specializedTypeNames.Short - case t if t eq defn.FloatClass => nme.specializedTypeNames.Float - case t if t eq defn.UnitClass => nme.specializedTypeNames.Void - case t if t eq defn.DoubleClass => nme.specializedTypeNames.Double - case t if t eq defn.CharClass => nme.specializedTypeNames.Char - case _ => nme.specializedTypeNames.Object - } + private def typeToTag(tp: Types.Type)(implicit ctx: Context): Name = + tp.classSymbol match { + case t if t eq defn.IntClass => nme.specializedTypeNames.Int + case t if t eq defn.BooleanClass => nme.specializedTypeNames.Boolean + case t if t eq defn.ByteClass => nme.specializedTypeNames.Byte + case t if t eq defn.LongClass => nme.specializedTypeNames.Long + case t if t eq defn.ShortClass => nme.specializedTypeNames.Short + case t if t eq defn.FloatClass => nme.specializedTypeNames.Float + case t if t eq defn.UnitClass => nme.specializedTypeNames.Void + case t if t eq defn.DoubleClass => nme.specializedTypeNames.Double + case t if t eq defn.CharClass => nme.specializedTypeNames.Char + case _ => nme.specializedTypeNames.Object } + /** This method is to be used on **type parameters** from a class, since + * this method does sorting based on their names + */ + def specializedFor(classTargs: List[Types.Type], classTargsNames: List[Name], methodTargs: List[Types.Type], methodTarsNames: List[Name])(implicit ctx: Context): name.ThisName = { val methodTags: Seq[Name] = (methodTargs zip methodTarsNames).sortBy(_._2).map(x => typeToTag(x._1)) val classTags: Seq[Name] = (classTargs zip classTargsNames).sortBy(_._2).map(x => typeToTag(x._1)) @@ -234,6 +239,17 @@ object NameOps { classTags.fold(nme.EMPTY)(_ ++ _) ++ nme.specializedTypeNames.suffix) } + /** Use for specializing function names ONLY and use it if you are **not** + * creating specialized name from type parameters. The order of names will + * be: + * + * `<...>` + */ + def specializedFunction(ret: Types.Type, args: List[Types.Type])(implicit ctx: Context): name.ThisName = + name ++ nme.specializedTypeNames.prefix ++ + nme.specializedTypeNames.separator ++ typeToTag(ret) ++ + args.map(typeToTag).fold(nme.EMPTY)(_ ++ _) ++ nme.specializedTypeNames.suffix + /** If name length exceeds allowable limit, replace part of it by hash */ def compactified(implicit ctx: Context): TermName = termName(compactify(name.toString)) diff --git a/compiler/src/dotty/tools/dotc/core/Names.scala b/compiler/src/dotty/tools/dotc/core/Names.scala index 9d6b03cf5afc..d73da4d261b3 100644 --- a/compiler/src/dotty/tools/dotc/core/Names.scala +++ b/compiler/src/dotty/tools/dotc/core/Names.scala @@ -37,10 +37,10 @@ object Names { * in a name table. A derived term name adds a tag, and possibly a number * or a further simple name to some other name. */ - abstract class Name extends DotClass with PreName { + abstract class Name extends DotClass with PreName { self => /** A type for names of the same kind as this name */ - type ThisName <: Name + type ThisName <: Name { type ThisName = self.ThisName } /** Is this name a type name? */ def isTypeName: Boolean diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala new file mode 100644 index 000000000000..d41772067cbd --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala @@ -0,0 +1,190 @@ +package dotty.tools.dotc +package transform + +import TreeTransforms.{ MiniPhaseTransform, TransformerInfo } +import ast.Trees._, ast.tpd, core._ +import Contexts.Context, Types._, Decorators._, Symbols._, DenotTransformers._ +import SymDenotations._, Scopes._, StdNames._, NameOps._, Names._ + +import scala.collection.mutable + +/** Specializes classes that inherit from `FunctionN` where there exists a + * specialized form. + */ +class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer { + import ast.tpd._ + val phaseName = "specializeFunctions" + + private[this] var _blacklistedSymbols: List[Symbol] = _ + + private def blacklistedSymbols(implicit ctx: Context): List[Symbol] = { + if (_blacklistedSymbols eq null) _blacklistedSymbols = List( + ctx.getClassIfDefined("scala.math.Ordering").asClass.membersNamed("Ops".toTypeName).first.symbol + ) + + _blacklistedSymbols + } + + /** Transforms the type to include decls for specialized applys and replace + * the class parents with specialized versions. + */ + def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context) = tp match { + case tp: ClassInfo if !sym.is(Flags.Package) && (tp.decls ne EmptyScope) => { + var newApplys = Map.empty[Name, Symbol] + + val newParents = tp.parents.mapConserve { parent => + List(0, 1, 2, 3).flatMap { arity => + val func = defn.FunctionClass(arity) + if (!parent.derivesFrom(func)) Nil + else { + val typeParams = tp.typeRef.baseArgInfos(func) + val interface = specInterface(typeParams) + + if (interface.exists) { + if (tp.decls.lookup(nme.apply).exists) { + val specializedMethodName = nme.apply.specializedFunction(typeParams.last, typeParams.init) + newApplys = newApplys + (specializedMethodName -> interface) + } + + if (parent.isRef(func)) List(interface.typeRef) + else Nil + } + else Nil + } + } + .headOption + .getOrElse(parent) + } + + def newDecls = + if (newApplys.isEmpty) tp.decls + else + newApplys.toList.map { case (name, interface) => + ctx.newSymbol( + sym, + name, + Flags.Override | Flags.Method, + interface.info.decls.lookup(name).info + ) + } + .foldLeft(tp.decls.cloneScope) { + (scope, sym) => scope.enter(sym); scope + } + + tp.derivedClassInfo( + classParents = newParents, + decls = newDecls + ) + } + + case _ => tp + } + + /** Transforms the `Template` of the classes to contain forwarders from the + * generic applys to the specialized ones. Also replaces parents of the + * class on the tree level and inserts the specialized applys in the + * template body. + */ + override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = { + val applyBuf = new mutable.ListBuffer[Tree] + val newBody = tree.body.mapConserve { + case dt: DefDef if dt.name == nme.apply && dt.vparamss.length == 1 => { + val specName = nme.apply.specializedFunction( + dt.tpe.widen.finalResultType, + dt.vparamss.head.map(_.symbol.info) + ) + + val specializedApply = tree.symbol.enclosingClass.info.decls.lookup(specName)//member(specName).symbol + //val specializedApply = tree.symbol.enclosingClass.info.member(specName).symbol + + if (false) { + println(tree.symbol.enclosingClass.show) + println("'" + specName.show + "'") + println(specializedApply) + println(specializedApply.exists) + } + + + if (specializedApply.exists) { + val apply = specializedApply.asTerm + val specializedDecl = + polyDefDef(apply, trefs => vrefss => { + dt.rhs + .changeOwner(dt.symbol, apply) + .subst(dt.vparamss.flatten.map(_.symbol), vrefss.flatten.map(_.symbol)) + }) + applyBuf += specializedDecl + + // create a forwarding to the specialized apply + cpy.DefDef(dt)(rhs = { + tpd + .ref(apply) + .appliedToArgs(dt.vparamss.head.map(vparam => ref(vparam.symbol))) + }) + } else dt + } + case x => x + } + + val missing: List[TypeTree] = List(0, 1, 2, 3).flatMap { arity => + val func = defn.FunctionClass(arity) + val tr = tree.symbol.enclosingClass.typeRef + + if (!tr.parents.exists(_.isRef(func))) Nil + else { + val typeParams = tr.baseArgInfos(func) + val interface = specInterface(typeParams) + + if (interface.exists) List(interface.info) + else Nil + } + }.map(TypeTree) + + cpy.Template(tree)( + parents = tree.parents ++ missing, + body = applyBuf.toList ++ newBody + ) + } + + /** Dispatch to specialized `apply`s in user code when available */ + override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) = + tree match { + case app @ Apply(fun, args) + if fun.symbol.name == nme.apply && + fun.symbol.owner.derivesFrom(defn.FunctionClass(args.length)) + => { + val params = (fun.tpe.widen.firstParamTypes :+ tree.tpe).map(_.widenSingleton.dealias) + val specializedApply = specializedName(nme.apply, params) + + if (!params.exists(_.isInstanceOf[ExprType]) && fun.symbol.owner.info.decls.lookup(specializedApply).exists) { + val newSel = fun match { + case Select(qual, _) => + qual.select(specializedApply) + case _ => { + (fun.tpe: @unchecked) match { + case TermRef(prefix: ThisType, name) => + tpd.This(prefix.cls).select(specializedApply) + case TermRef(prefix: NamedType, name) => + tpd.ref(prefix).select(specializedApply) + } + } + } + + newSel.appliedToArgs(args) + } + else tree + } + case _ => tree + } + + @inline private def specializedName(name: Name, args: List[Type])(implicit ctx: Context) = + name.specializedFor(args, args.map(_.typeSymbol.name), Nil, Nil) + + @inline private def specInterface(typeParams: List[Type])(implicit ctx: Context) = { + val specName = + ("JFunction" + (typeParams.length - 1)).toTermName + .specializedFunction(typeParams.last, typeParams.init) + + ctx.getClassIfDefined("scala.compat.java8.".toTermName ++ specName) + } +} diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala b/compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala new file mode 100644 index 000000000000..2afee5a397ec --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/SpecializedApplyMethods.scala @@ -0,0 +1,96 @@ +package dotty.tools.dotc +package transform + +import TreeTransforms.{ MiniPhaseTransform, TransformerInfo } +import ast.Trees._, ast.tpd, core._ +import Contexts.Context, Types._, Decorators._, Symbols._, DenotTransformers._ +import SymDenotations._, Scopes._, StdNames._, NameOps._, Names._ + +/** This phase synthesizes specialized methods for FunctionN, this is done + * since there are no scala signatures in the bytecode for the specialized + * methods. + * + * We know which specializations exist for the different arities, therefore we + * can hardcode them. This should, however be removed once we're using a + * different standard library. + */ +class SpecializedApplyMethods extends MiniPhaseTransform with InfoTransformer { + import ast.tpd._ + + val phaseName = "specializedApplyMethods" + + private[this] var func0Applys: List[Symbol] = _ + private[this] var func1Applys: List[Symbol] = _ + private[this] var func2Applys: List[Symbol] = _ + private[this] var func0: Symbol = _ + private[this] var func1: Symbol = _ + private[this] var func2: Symbol = _ + + private def init()(implicit ctx: Context): Unit = if (func0Applys eq null) { + val definitions = ctx.definitions + import definitions._ + + def specApply(sym: Symbol, args: List[Type], ret: Type)(implicit ctx: Context) = { + val name = nme.apply.specializedFunction(ret, args) + ctx.newSymbol(sym, name, Flags.Method, MethodType(args, ret)) + } + + func0 = FunctionClass(0) + func0Applys = for (r <- ScalaValueTypes.toList) yield specApply(func0, Nil, r) + + func1 = FunctionClass(1) + func1Applys = for { + r <- List(UnitType, BooleanType, IntType, FloatType, LongType, DoubleType) + t1 <- List(IntType, LongType, FloatType, DoubleType) + } yield specApply(func1, List(t1), r) + + func2 = FunctionClass(2) + func2Applys = for { + r <- List(UnitType, BooleanType, IntType, FloatType, LongType, DoubleType) + t1 <- List(IntType, LongType, DoubleType) + t2 <- List(IntType, LongType, DoubleType) + } yield specApply(func2, List(t1, t2), r) + } + + /** Add symbols for specialized methods to FunctionN */ + def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context) = tp match { + case tp: ClassInfo if defn.isPlainFunctionClass(sym) => { + init() + val newDecls = sym.name.functionArity match { + case 0 => func0Applys.foldLeft(tp.decls.cloneScope) { + (decls, sym) => decls.enter(sym); decls + } + case 1 => func1Applys.foldLeft(tp.decls.cloneScope) { + (decls, sym) => decls.enter(sym); decls + } + case 2 => func2Applys.foldLeft(tp.decls.cloneScope) { + (decls, sym) => decls.enter(sym); decls + } + case _ => tp.decls + } + + tp.derivedClassInfo(decls = newDecls) + } + case _ => tp + } + + /** Create bridge methods for FunctionN with specialized applys */ + override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = { + val owner = tree.symbol.owner + val additionalSymbols = + if (owner eq func0) func0Applys + else if (owner eq func1) func1Applys + else if (owner eq func2) func2Applys + else Nil + + if (additionalSymbols eq Nil) tree + else cpy.Template(tree)(body = tree.body ++ additionalSymbols.map { apply => + DefDef(apply.asTerm, { vparamss => + This(owner.asClass) + .select(nme.apply) + .appliedToArgss(vparamss) + .ensureConforms(apply.info.finalResultType) + }) + }) + } +} diff --git a/compiler/src/dotty/tools/dotc/transform/TreeTransform.scala b/compiler/src/dotty/tools/dotc/transform/TreeTransform.scala index b0bd40578c17..efc41dd8001d 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeTransform.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeTransform.scala @@ -92,6 +92,9 @@ object TreeTransforms { def prepareForPackageDef(tree: PackageDef)(implicit ctx: Context) = this def prepareForStats(trees: List[Tree])(implicit ctx: Context) = this + /** Prepare for _compilation_ Unit, overriding this can be used as setup + * for transformation + */ def prepareForUnit(tree: Tree)(implicit ctx: Context) = this def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = tree diff --git a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTest.scala b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTest.scala index c423089d07a4..2a24f6b4716d 100644 --- a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTest.scala +++ b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTest.scala @@ -5,7 +5,7 @@ import dotc.core.Contexts.{Context, ContextBase} import dotc.core.Phases.Phase import dotc.Compiler -import scala.reflect.io.{VirtualDirectory => Directory} +import dotty.tools.io.{VirtualDirectory => Directory} import scala.tools.asm import asm._ import asm.tree._ @@ -17,6 +17,8 @@ import scala.tools.asm.{ClassWriter, ClassReader} import scala.tools.asm.tree._ import java.io.{File => JFile, InputStream} +import org.junit.Assert._ + class TestGenBCode(val outDir: String) extends GenBCode { override def phaseName: String = "testGenBCode" val virtualDir = new Directory(outDir, None) @@ -89,6 +91,14 @@ trait DottyBytecodeTest extends DottyTest { cn } + /** Finds a class with `cls` as name in `dir`, throws if it can't find it */ + def findClass(cls: String, dir: Directory) = { + val clsIn = dir.lookupName(s"$cls.class", directory = false).input + val clsNode = loadClassNode(clsIn) + assert(clsNode.name == cls, s"inspecting wrong class: ${clsNode.name}") + clsNode + } + protected def getMethod(classNode: ClassNode, name: String): MethodNode = classNode.methods.asScala.find(_.name == name) getOrElse sys.error(s"Didn't find method '$name' in class '${classNode.name}'") @@ -205,4 +215,41 @@ trait DottyBytecodeTest extends DottyTest { s"Wrong number of null checks ($actualChecks), expected: $expectedChecks" ) } + + def assertBoxing(nodeName: String, methods: java.lang.Iterable[MethodNode])(implicit source: String): Unit = + methods.asScala.find(_.name == nodeName) + .map { node => + val (ins, boxed) = boxingInstructions(node) + if (!boxed) fail("No boxing in:\n" + boxingError(ins, source)) + } + .getOrElse(fail("Could not find constructor for object `Test`")) + + private def boxingError(ins: List[_], source: String) = + s"""|---------------------------------- + |${ins.mkString("\n")} + |---------------------------------- + |From code: + |$source + |----------------------------------""".stripMargin + + + protected def assertNoBoxing(nodeName: String, methods: java.lang.Iterable[MethodNode])(implicit source: String): Unit = + methods.asScala.find(_.name == nodeName) + .map { node => + val (ins, boxed) = boxingInstructions(node) + if (boxed) fail(boxingError(ins, source)) + } + .getOrElse(fail("Could not find constructor for object `Test`")) + + protected def boxingInstructions(method: MethodNode): (List[_], Boolean) = { + val ins = instructionsFromMethod(method) + val boxed = ins.exists { + case Invoke(op, owner, name, desc, itf) => + owner.toLowerCase.contains("box") || name.toLowerCase.contains("box") + case _ => false + } + + (ins, boxed) + } + } diff --git a/compiler/test/dotty/tools/dotc/transform/SpecializeFunctionsTests.scala b/compiler/test/dotty/tools/dotc/transform/SpecializeFunctionsTests.scala new file mode 100644 index 000000000000..9605c8fbfdd7 --- /dev/null +++ b/compiler/test/dotty/tools/dotc/transform/SpecializeFunctionsTests.scala @@ -0,0 +1,142 @@ +package dotty.tools +package dotc +package transform + +import org.junit.Assert._ +import org.junit.Test + +import dotty.tools.backend.jvm.DottyBytecodeTest + +class SpecializeFunctionsTests extends DottyBytecodeTest { + + import dotty.tools.backend.jvm.ASMConverters._ + import dotty.tools.backend.jvm.AsmNode._ + import scala.collection.JavaConverters._ + import scala.tools.asm.tree.MethodNode + + @Test def specializeParentIntToInt = { + val source = """ + |class Foo extends Function1[Int, Int] { + | def apply(i: Int) = i + |} + """.stripMargin + + checkBCode(source) { dir => + val applys = + findClass("Foo", dir).methods.asScala.collect { + case m if m.name == "apply$mcII$sp" => m + case m if m.name == "apply" => m + } + .map(_.name) + .toList + + assert( + // there should be two "apply", one generic and the one overwritten and + // then the specialized one + applys.length == 3, + s"Wrong number of specialized applys, actual length: ${applys.length} $applys" + ) + assert(applys.contains("apply"), "Foo did not contain `apply` forwarder method") + assert(applys.contains("apply$mcII$sp"), "Foo did not contain specialized apply") + } + } + + @Test def specializeFunction2Applys = { + val source = + """|class Func2 extends Function2[Int, Int, Int] { + | def apply(i: Int, j: Int): Int = i + j + |}""".stripMargin + + checkBCode(source) { dir => + val apps = + findClass("Func2", dir).methods.asScala.collect { + case m if m.name == "apply$mcIII$sp" => m + case m if m.name == "apply" => m + } + .map(_.name) + .toList + + assert( + apps.length == 3, + s"Wrong number of specialized applys, actual length: ${apps.length} - $apps" + ) + assert(apps.contains("apply"), "Func3 did not contain `apply` forwarder method") + assert(apps.contains("apply$mcIII$sp"), "Func3 did not contain specialized apply") + } + } + + @Test def noBoxingSpecFunction0 = { + implicit val source: String = + """|object Test { + | class Func0 extends Function0[Int] { + | def apply() = 1337 + | } + | + | (new Func0: Function0[Int])() + |}""".stripMargin + + checkBCode(source) { dir => + assertNoBoxing("", findClass("Test$", dir).methods) + } + } + + @Test def boxingFunction1 = { + implicit val source: String = + """|object Test { + | class Func1 extends Function1[Char, Int] { + | def apply(c: Char) = c.toInt + | } + | + | (new Func1: Function1[Char, Int])('c') + |}""".stripMargin + + checkBCode(source) { dir => + assertBoxing("", findClass("Test$", dir).methods) + } + } + + @Test def noBoxingSpecFunction1 = { + implicit val source: String = + """|object Test { + | class Func1 extends Function1[Int, Int] { + | def apply(i: Int) = i + 1 + | } + | + | (new Func1: Function1[Int, Int])(1) + |}""".stripMargin + + checkBCode(source) { dir => + assertNoBoxing("", findClass("Test$", dir).methods) + } + } + + @Test def noBoxingSpecFunction2 = { + implicit val source: String = + """|object Test { + | class Func2 extends Function2[Int, Int, Int] { + | def apply(i: Int, j: Int) = i + j + | } + | + | (new Func2: Function2[Int, Int, Int])(1300, 37) + |}""".stripMargin + + checkBCode(source) { dir => + assertNoBoxing("", findClass("Test$", dir).methods) + } + } + + @Test def boxingFunction2 = { + implicit val source: String = + """|object Test { + | class Func2 extends Function2[Char, Char, Char] { + | def apply(c1: Char, c2: Char) = c1 + | } + | + | (new Func2: Function2[Char, Char, Char])('c', 'd') + |}""".stripMargin + + checkBCode(source) { dir => + assertBoxing("", findClass("Test$", dir).methods) + } + } +} diff --git a/tests/run/t2857.scala b/tests/run/t2857.scala index 5df2d440eac5..794e651ec3d1 100644 --- a/tests/run/t2857.scala +++ b/tests/run/t2857.scala @@ -5,5 +5,3 @@ object Test extends dotty.runtime.LegacyApp { m.removeBinding(6, "Foo") println(m.contains(6)) } - -