From 379ad18e8eb3b6df20ae516c8ad95a5d4a3c0dab Mon Sep 17 00:00:00 2001 From: Allan Renucci Date: Tue, 2 Oct 2018 14:42:10 +0200 Subject: [PATCH] Fix #5191: Don't lift the receiver of a call to a method with varargs The receiver of a function is lifted if the call use default arguments. To figure out if a default argument is needed, we compare the number of passed arguments to the expected number of arguments. Repeated arguments do not count as required arguments --- .../dotty/tools/dotc/typer/Applications.scala | 24 ++++++++++++------ .../backend/jvm/DottyBytecodeTests.scala | 25 +++++++++++++++++++ 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 4de90b309307..fcb27c229b35 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -228,9 +228,23 @@ trait Applications extends Compatibility { self: Typer with Dynamic => /** Whether `liftFun` is needed? It is the case if default arguments are used. */ - protected def needLiftFun: Boolean = + protected def needLiftFun: Boolean = { + def requiredArgNum(tp: Type): Int = tp.widen match { + case funType: MethodType => + val paramInfos = funType.paramInfos + val argsNum = paramInfos.size + if (argsNum >= 1 && paramInfos.last.isRepeatedParam) + // Repeated arguments do not count as required arguments + argsNum - 1 + else + argsNum + case funType: PolyType => requiredArgNum(funType.resultType) + case tp => args.size + } + !isJavaAnnotConstr(methRef.symbol) && - args.size < reqiredArgNum(funType) + args.size < requiredArgNum(funType) + } /** A flag signalling that the typechecking the application was so far successful */ private[this] var _ok = true @@ -250,12 +264,6 @@ trait Applications extends Compatibility { self: Typer with Dynamic => case tp => tp //was: funType } - def reqiredArgNum(tp: Type): Int = tp.widen match { - case funType: MethodType => funType.paramInfos.size - case funType: PolyType => reqiredArgNum(funType.resultType) - case tp => args.size - } - lazy val liftedFunType = if (needLiftFun) { liftFun() diff --git a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala index 16b45d11ccb6..8c8d343f6f74 100644 --- a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala +++ b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala @@ -475,4 +475,29 @@ class TestBCode extends DottyBytecodeTest { diffInstructions(testInstructions, refInstructions)) } } + + /** Test that the receiver of a call to a method with varargs is not unnecessarily lifted */ + @Test def i5191 = { + val source = + """class Test { + | def foo(args: String*): String = "" + | def self = this + | + | def test = self.foo() + |} + """.stripMargin + + checkBCode(source) { dir => + val clsIn = dir.lookupName("Test.class", directory = false).input + val clsNode = loadClassNode(clsIn) + val method = getMethod(clsNode, "test") + + val liftReceiver = instructionsFromMethod(method).exists { + case VarOp(Opcodes.ASTORE, _) => true // receiver lifted in local val + case _ => false + } + assertFalse("Receiver of a call to a method with varargs is unnecessarily lifted", + liftReceiver) + } + } }