diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala index d5c4b5e20161..d4b536f841df 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala @@ -1074,16 +1074,14 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { import InvokeStyle._ if (style == Super) { assert(receiverClass == methodOwner, s"for super call, expecting $receiverClass == $methodOwner") - if (receiverClass.isTrait && !receiverClass.isJavaDefined) { - val staticDesc = MethodBType(typeToBType(method.owner.info) :: bmType.argumentTypes, bmType.returnType).descriptor - val staticName = traitImplMethodName(method).toString - bc.invokestatic(receiverName, staticName, staticDesc, isInterface, pos) - } else { - if (receiverClass.isTraitOrInterface) { - // An earlier check in Mixin reports an error in this case, so it doesn't reach the backend - assert(cnode.interfaces.contains(receiverName), s"cannot invokespecial $receiverName.$jname, the interface is not a direct parent.") - } + if (method.isClassConstructor) { bc.invokespecial(receiverName, jname, mdescr, isInterface, pos) + } else if (method.isMixinConstructor) { + val staticName = method.javaSimpleName.toString + bc.invokestatic(receiverName, staticName, mdescr, isInterface, pos) + } else { + def staticDesc = MethodBType(typeToBType(method.owner.info) :: bmType.argumentTypes, bmType.returnType).descriptor + bc.jmethod.visitInvokeDynamicInsn(jname, staticDesc, coreBTypes.invokeExactBootstrapHandle, classBTypeFromSymbol(methodOwner).toASMType) } } else { val opc = style match { diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala index d779490ba84e..7167db95b6ad 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala @@ -50,14 +50,6 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { } } - def needsStaticImplMethod(sym: Symbol) = sym.hasAttachment[global.mixer.NeedStaticImpl.type] - - final def traitImplMethodName(sym: Symbol): Name = { - val name = sym.javaSimpleName - if (sym.isMixinConstructor) name - else name.append(nme.NAME_JOIN_STRING) - } - /** * True if `classSym` is an anonymous class or a local class. I.e., false if `classSym` is a * member class. This method is used to decide if we should emit an EnclosingMethod attribute. diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala index 1bff8519eca3..de5e75855bc2 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala @@ -490,19 +490,8 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { case dd : DefDef => val sym = dd.symbol - if (needsStaticImplMethod(sym)) { - val staticDefDef = global.gen.mkStatic(dd, traitImplMethodName(sym), _.cloneSymbol) - val forwarderDefDef = { - val forwarderBody = Apply(global.gen.mkAttributedRef(staticDefDef.symbol), This(sym.owner).setType(sym.owner.typeConstructor) :: dd.vparamss.head.map(p => global.gen.mkAttributedIdent(p.symbol))).setType(sym.info.resultType) - // we don't want to the optimizer to inline the static method into the forwarder. Instead, - // the backend has a special case to transitively inline into a callsite of the forwarder - // when the forwarder itself is inlined. - forwarderBody.updateAttachment(NoInlineCallsiteAttachment) - deriveDefDef(dd)(_ => global.atPos(dd.pos)(forwarderBody)) - } - genDefDef(staticDefDef) - if (!sym.isMixinConstructor) - genDefDef(forwarderDefDef) + if (sym.isMixinConstructor) { + genDefDef(global.gen.mkStatic(dd, sym.javaSimpleName, sym => sym)) } else genDefDef(dd) case Template(_, _, body) => body foreach gen diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala index 7b2686e7a9be..ae713ee7c514 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala @@ -11,7 +11,7 @@ import scala.collection.{concurrent, mutable} import scala.collection.concurrent.TrieMap import scala.reflect.internal.util.Position import scala.tools.asm -import asm.Opcodes +import asm.{Opcodes, Type} import scala.tools.asm.tree._ import scala.tools.nsc.backend.jvm.BTypes.{InlineInfo, MethodInlineInfo} import scala.tools.nsc.backend.jvm.BackendReporting._ @@ -1093,7 +1093,9 @@ abstract class BTypes { } } - final case class MethodBType(argumentTypes: List[BType], returnType: BType) extends BType + final case class MethodBType(argumentTypes: List[BType], returnType: BType) extends BType { + def toAsmType: Type = asm.Type.getMethodType(returnType.toASMType, argumentTypes.map(_.toASMType): _*) + } /* Some definitions that are required for the implementation of BTypes. They are abstract because * initializing them requires information from types / symbols, which is not accessible here in diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala index 383347a0d321..a7599f7c573b 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala @@ -584,23 +584,7 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { annotatedInline = methodSym.hasAnnotation(ScalaInlineClass), annotatedNoInline = methodSym.hasAnnotation(ScalaNoInlineClass)) - if (needsStaticImplMethod(methodSym)) { - val staticName = traitImplMethodName(methodSym).toString - val selfParam = methodSym.newSyntheticValueParam(methodSym.owner.typeConstructor, nme.SELF) - val staticMethodType = methodSym.info match { - case mt @ MethodType(params, res) => copyMethodType(mt, selfParam :: params, res) - } - val staticMethodSignature = staticName + methodBTypeFromMethodType(staticMethodType, isConstructor = false) - val staticMethodInfo = MethodInlineInfo( - effectivelyFinal = true, - annotatedInline = info.annotatedInline, - annotatedNoInline = info.annotatedNoInline) - if (methodSym.isMixinConstructor) - List((staticMethodSignature, staticMethodInfo)) - else - List((signature, info), (staticMethodSignature, staticMethodInfo)) - } else - List((signature, info)) + List((signature, info)) } }).toMap diff --git a/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala index c2010d282828..c2b61733d740 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala @@ -103,6 +103,7 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) { lazy val jlCloneableRef : ClassBType = classBTypeFromSymbol(JavaCloneableClass) // java/lang/Cloneable lazy val jiSerializableRef : ClassBType = classBTypeFromSymbol(JavaSerializableClass) // java/io/Serializable lazy val jlClassCastExceptionRef : ClassBType = classBTypeFromSymbol(ClassCastExceptionClass) // java/lang/ClassCastException + lazy val jlClassRef : ClassBType = classBTypeFromSymbol(ClassClass) // java/lang/Class lazy val juMapRef : ClassBType = classBTypeFromSymbol(JavaUtilMap) // java/util/Map lazy val juHashMapRef : ClassBType = classBTypeFromSymbol(JavaUtilHashMap) // java/util/HashMap lazy val sbScalaBeanInfoRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.beans.ScalaBeanInfo]) @@ -117,6 +118,7 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) { lazy val srSymbolLiteral : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.SymbolLiteral]) lazy val srStructuralCallSite : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.StructuralCallSite]) lazy val srLambdaDeserialize : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.LambdaDeserialize]) + lazy val srInvokeExact : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.InvokeExact]) lazy val srBoxedUnitRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.BoxedUnit]) private def methodNameAndType(cls: Symbol, name: Name, static: Boolean = false, filterOverload: Symbol => Boolean = _ => true): MethodNameAndType = { @@ -288,6 +290,20 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) { coreBTypes.jliCallSiteRef ).descriptor, /* itf = */ coreBTypes.srLambdaDeserialize.isInterface.get) + + lazy val invokeExactBootstrapHandle = + new scala.tools.asm.Handle(scala.tools.asm.Opcodes.H_INVOKESTATIC, + coreBTypes.srInvokeExact.internalName, sn.Bootstrap.toString, + MethodBType( + List( + coreBTypes.jliMethodHandlesLookupRef, + coreBTypes.StringRef, + coreBTypes.jliMethodTypeRef, + coreBTypes.jlClassRef + ), + coreBTypes.jliCallSiteRef + ).descriptor, + /* itf = */ coreBTypes.srInvokeExact.isInterface.get) } /** @@ -377,6 +393,7 @@ final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: def jlThrowableRef : ClassBType = _coreBTypes.jlThrowableRef def jlCloneableRef : ClassBType = _coreBTypes.jlCloneableRef def jiSerializableRef : ClassBType = _coreBTypes.jiSerializableRef + def jlClassRef : ClassBType = _coreBTypes.jlClassRef def jlClassCastExceptionRef : ClassBType = _coreBTypes.jlClassCastExceptionRef def juMapRef : ClassBType = _coreBTypes.juMapRef def juHashMapRef : ClassBType = _coreBTypes.juHashMapRef @@ -410,6 +427,7 @@ final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: def srSymbolLiteral : ClassBType = _coreBTypes.srSymbolLiteral def srStructuralCallSite : ClassBType = _coreBTypes.srStructuralCallSite def srLambdaDeserialize : ClassBType = _coreBTypes.srLambdaDeserialize + def srInvokeExact : ClassBType = _coreBTypes.srInvokeExact def typeOfArrayOp: Map[Int, BType] = _coreBTypes.typeOfArrayOp @@ -427,4 +445,5 @@ final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: def lambdaMetaFactoryMetafactoryHandle : asm.Handle = _coreBTypes.lambdaMetaFactoryMetafactoryHandle def lambdaMetaFactoryAltMetafactoryHandle : asm.Handle = _coreBTypes.lambdaMetaFactoryAltMetafactoryHandle def lambdaDeserializeBootstrapHandle : asm.Handle = _coreBTypes.lambdaDeserializeBootstrapHandle + def invokeExactBootstrapHandle : asm.Handle = _coreBTypes.invokeExactBootstrapHandle } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala index 447ee209b593..a88f58126fd2 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala @@ -7,10 +7,9 @@ package scala.tools.nsc package backend.jvm package opt -import scala.annotation.{tailrec, switch} - +import scala.annotation.{switch, tailrec} import scala.tools.asm.Type -import scala.tools.asm.tree.analysis.Frame +import scala.tools.asm.tree.analysis.{AnalyzerException, Frame} import scala.tools.asm.Opcodes._ import scala.tools.asm.tree._ import scala.collection.mutable @@ -201,7 +200,7 @@ class LocalOpt[BT <: BTypes](val btypes: BT) { * * Returns `true` if the bytecode of `method` was changed. */ - def methodOptimizations(method: MethodNode, ownerClassName: InternalName): Boolean = { + def methodOptimizations(method: MethodNode, ownerClassName: InternalName): Boolean = try { if (method.instructions.size == 0) return false // fast path for abstract methods // unreachable-code also removes unused local variable nodes and empty exception handlers. @@ -383,6 +382,9 @@ class LocalOpt[BT <: BTypes](val btypes: BT) { assert(nullOrEmpty(method.invisibleLocalVariableAnnotations), method.invisibleLocalVariableAnnotations) nullnessDceBoxesCastsCopypropPushpopOrJumpsChanged || localsRemoved || lineNumbersRemoved || labelsRemoved + } catch { + case ex: AnalyzerException => + throw new AnalyzerException(ex.node, s"${ex.getMessage} + While optimizing: ${AsmUtils.textify(method)}", ex.getCause) } /** diff --git a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala index 855e53710bed..5ae32a6d564e 100644 --- a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala +++ b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala @@ -284,7 +284,6 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre // delambdafy targets are excluded as they are made static by `transformFunction`. if (!dd.symbol.hasFlag(STATIC) && !methodReferencesThis(dd.symbol)) { dd.symbol.setFlag(STATIC) - dd.symbol.removeAttachment[mixer.NeedStaticImpl.type] } super.transform(tree) case Apply(fun, outer :: rest) if shouldElideOuterArg(fun.symbol, outer) => diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index d62b77dac26e..1470fee75f41 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -130,10 +130,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { */ def addMember(clazz: Symbol, member: Symbol): Symbol = { debuglog(s"mixing into $clazz: ${member.defString}") - // This attachment is used to instruct the backend about which methids in traits require - // a static trait impl method. We remove this from the new symbol created for the method - // mixed into the subclass. - member.removeAttachment[NeedStaticImpl.type] clazz.info.decls enter member setFlag MIXEDIN resetFlag JAVA_DEFAULTMETHOD } def cloneAndAddMember(mixinClass: Symbol, mixinMember: Symbol, clazz: Symbol): Symbol = @@ -1017,11 +1013,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { } // add all new definitions to current class or interface val body1 = addNewDefs(currentOwner, bodyEmptyAccessors) - body1 foreach { - case dd: DefDef if isTraitMethodRequiringStaticImpl(dd) => - dd.symbol.updateAttachment(NeedStaticImpl) - case _ => - } treeCopy.Template(tree, parents1, self, body1) case Select(qual, name) if sym.owner.isTrait && !sym.isMethod => @@ -1056,14 +1047,4 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { finally localTyper = saved } } - - private def isTraitMethodRequiringStaticImpl(dd: DefDef): Boolean = { - val sym = dd.symbol - dd.rhs.nonEmpty && - sym.owner.isTrait && - !sym.isPrivate && // no need to put implementations of private methods into a static method - !sym.hasFlag(Flags.STATIC) - } - - case object NeedStaticImpl extends PlainAttachment } diff --git a/src/library/scala/runtime/InvokeExact.java b/src/library/scala/runtime/InvokeExact.java new file mode 100644 index 000000000000..e90a753cd940 --- /dev/null +++ b/src/library/scala/runtime/InvokeExact.java @@ -0,0 +1,25 @@ +package scala.runtime; + +import java.lang.invoke.*; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public class InvokeExact { + private static MethodHandles.Lookup TRUSTED = getTrustedLookup(); + + public static CallSite bootstrap(MethodHandles.Lookup lookup, String invokedName, MethodType invokedType, + Class fromSite) throws Throwable { + Method method = fromSite.getMethod(invokedName, invokedType.dropParameterTypes(0, 1).parameterArray()); + return new ConstantCallSite(TRUSTED.unreflectSpecial(method, fromSite)); + } + + private static MethodHandles.Lookup getTrustedLookup() { + try { + Field field = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP"); + field.setAccessible(true); + return (MethodHandles.Lookup) field.get(null); + } catch (ReflectiveOperationException e) { + return null; + } + } +} diff --git a/test/files/run/sd143.scala b/test/files/run/sd143.scala new file mode 100644 index 000000000000..5e003954c7f1 --- /dev/null +++ b/test/files/run/sd143.scala @@ -0,0 +1,19 @@ +class A { + def m = 1 +} + +class B extends A { + override def m = 2 +} + +trait T extends A + +class C extends B with T { + override def m = super[T].m +} + +object Test { + def main(args: Array[String]): Unit = { + println((new C).m) + } +}