diff options
27 files changed, 454 insertions, 140 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 847c4cb2d1..94a6a0e4e2 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -394,15 +394,18 @@ class Global(var currentSettings: Settings, var reporter: Reporter) if (settings.debug && (settings.verbose || currentRun.size < 5)) inform("[running phase " + name + " on " + unit + "]") + if (!cancelled(unit)) { + currentRun.informUnitStarting(this, unit) + try withCurrentUnitNoLog(unit)(task) + finally currentRun.advanceUnit() + } + } + final def withCurrentUnitNoLog(unit: CompilationUnit)(task: => Unit) { val unit0 = currentUnit try { currentRun.currentUnit = unit - if (!cancelled(unit)) { - currentRun.informUnitStarting(this, unit) - task - } - currentRun.advanceUnit() + task } finally { //assert(currentUnit == unit) currentRun.currentUnit = unit0 diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala b/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala index 01206aa6eb..4287c24dc8 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala @@ -42,15 +42,15 @@ object BackendReporting { def assertionError(message: String): Nothing = throw new AssertionError(message) implicit class RightBiasedEither[A, B](val v: Either[A, B]) extends AnyVal { - def map[U](f: B => U) = v.right.map(f) - def flatMap[BB](f: B => Either[A, BB]) = v.right.flatMap(f) + def map[C](f: B => C): Either[A, C] = v.right.map(f) + def flatMap[C](f: B => Either[A, C]): Either[A, C] = v.right.flatMap(f) def withFilter(f: B => Boolean)(implicit empty: A): Either[A, B] = v match { case Left(_) => v case Right(e) => if (f(e)) v else Left(empty) // scalaz.\/ requires an implicit Monoid m to get m.empty } - def foreach[U](f: B => U) = v.right.foreach(f) + def foreach[U](f: B => U): Unit = v.right.foreach(f) - def getOrElse[BB >: B](alt: => BB): BB = v.right.getOrElse(alt) + def getOrElse[C >: B](alt: => C): C = v.right.getOrElse(alt) /** * Get the value, fail with an assertion if this is an error. @@ -101,11 +101,14 @@ object BackendReporting { else "" } - case MethodNotFound(name, descriptor, ownerInternalName, missingClasses) => - val (javaDef, others) = missingClasses.partition(_.definedInJavaSource) - s"The method $name$descriptor could not be found in the class $ownerInternalName or any of its parents." + - (if (others.isEmpty) "" else others.map(_.internalName).mkString("\nNote that the following parent classes could not be found on the classpath: ", ", ", "")) + - (if (javaDef.isEmpty) "" else javaDef.map(_.internalName).mkString("\nNote that the following parent classes are defined in Java sources (mixed compilation), no bytecode is available: ", ",", "")) + case MethodNotFound(name, descriptor, ownerInternalName, missingClass) => + val missingClassWarning = missingClass match { + case None => "" + case Some(c) => + if (c.definedInJavaSource) s"\nNote that the parent class ${c.internalName} is defined in a Java source (mixed compilation), no bytecode is available." + else s"\nNote that the parent class ${c.internalName} could not be found on the classpath." + } + s"The method $name$descriptor could not be found in the class $ownerInternalName or any of its parents." + missingClassWarning case FieldNotFound(name, descriptor, ownerInternalName, missingClass) => s"The field node $name$descriptor could not be found because the classfile $ownerInternalName cannot be found on the classpath." + @@ -127,7 +130,7 @@ object BackendReporting { } case class ClassNotFound(internalName: InternalName, definedInJavaSource: Boolean) extends MissingBytecodeWarning - case class MethodNotFound(name: String, descriptor: String, ownerInternalNameOrArrayDescriptor: InternalName, missingClasses: List[ClassNotFound]) extends MissingBytecodeWarning { + case class MethodNotFound(name: String, descriptor: String, ownerInternalNameOrArrayDescriptor: InternalName, missingClass: Option[ClassNotFound]) extends MissingBytecodeWarning { def isArrayMethod = ownerInternalNameOrArrayDescriptor.charAt(0) == '[' } case class FieldNotFound(name: String, descriptor: String, ownerInternalName: InternalName, missingClass: Option[ClassNotFound]) extends MissingBytecodeWarning diff --git a/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala index 0b53ea2fb1..1feca56923 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala @@ -107,6 +107,7 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) { lazy val juHashMapRef : ClassBType = classBTypeFromSymbol(JavaUtilHashMap) // java/util/HashMap lazy val sbScalaBeanInfoRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.beans.ScalaBeanInfo]) lazy val jliSerializedLambdaRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.SerializedLambda]) + lazy val jliMethodHandleRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.MethodHandle]) lazy val jliMethodHandlesRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.MethodHandles]) lazy val jliMethodHandlesLookupRef : ClassBType = classBTypeFromSymbol(exitingPickler(getRequiredClass("java.lang.invoke.MethodHandles.Lookup"))) // didn't find a reliable non-stringly-typed way that works for inner classes in the backend lazy val jliMethodTypeRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.MethodType]) @@ -320,6 +321,7 @@ trait CoreBTypesProxyGlobalIndependent[BTS <: BTypes] { def jliCallSiteRef : ClassBType def jliMethodTypeRef : ClassBType def jliSerializedLambdaRef : ClassBType + def jliMethodHandleRef : ClassBType def jliMethodHandlesLookupRef : ClassBType def srBoxesRunTimeRef : ClassBType def srBoxedUnitRef : ClassBType @@ -383,6 +385,7 @@ final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: def juHashMapRef : ClassBType = _coreBTypes.juHashMapRef def sbScalaBeanInfoRef : ClassBType = _coreBTypes.sbScalaBeanInfoRef def jliSerializedLambdaRef : ClassBType = _coreBTypes.jliSerializedLambdaRef + def jliMethodHandleRef : ClassBType = _coreBTypes.jliMethodHandleRef def jliMethodHandlesRef : ClassBType = _coreBTypes.jliMethodHandlesRef def jliMethodHandlesLookupRef : ClassBType = _coreBTypes.jliMethodHandlesLookupRef def jliMethodTypeRef : ClassBType = _coreBTypes.jliMethodTypeRef diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala index 340fdc849a..3520d57599 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala @@ -135,7 +135,7 @@ abstract class GenBCode extends BCodeSyncAndTry { return } else { - try { withCurrentUnit(item.cunit)(visit(item)) } + try { withCurrentUnitNoLog(item.cunit)(visit(item)) } catch { case ex: Throwable => ex.printStackTrace() diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala index eaf82f5c65..5bf57823ca 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala @@ -10,7 +10,7 @@ package opt import scala.tools.asm import asm.tree._ import scala.collection.JavaConverters._ -import scala.collection.concurrent +import scala.collection.{concurrent, mutable} import scala.tools.asm.Attribute import scala.tools.nsc.backend.jvm.BackendReporting._ import scala.tools.nsc.io.AbstractFile @@ -132,38 +132,135 @@ class ByteCodeRepository[BT <: BTypes](val classPath: ClassFileLookup[AbstractFi * The method node for a method matching `name` and `descriptor`, accessed in class `ownerInternalNameOrArrayDescriptor`. * The declaration of the method may be in one of the parents. * - * TODO: make sure we always return the right method, the one being invoked. write tests. - * - if there's an abstract and a concrete one. could possibly somehow the abstract be returned? - * - with traits and default methods, if there is more than one default method inherited and - * no override: what should be returned? We should not just inline one of the two. + * Note that the JVM spec performs method lookup in two steps: resolution and selection. + * + * Method resolution, defined in jvms-5.4.3.3 and jvms-5.4.3.4, is the first step and is identical + * for all invocation styles (virtual, interface, special, static). If C is the receiver class + * in the invocation instruction: + * 1 find a matching method (name and descriptor) in C + * 2 then in C's superclasses + * 3 then find the maximally-specific matching superinterface methods, succeed if there's a + * single non-abstract one. static and private methods in superinterfaces are not considered. + * 4 then pick a random non-static, non-private superinterface method. + * 5 then fail. + * + * Note that for an `invokestatic` instruction, a method reference `B.m` may resolve to `A.m`, if + * class `B` doesn't specify a matching method `m`, but the parent `A` does. + * + * Selection depends on the invocation style and is defined in jvms-6.5. + * - invokestatic: invokes the resolved method + * - invokevirtual / invokeinterface: searches for an override of the resolved method starting + * at the dynamic receiver type. the search procedure is basically the same as in resolution, + * but it fails at 4 instead of picking a superinterface method at random. + * - invokespecial: if C is the receiver in the invocation instruction, searches for an override + * of the resolved method starting at + * - the superclass of the current class, if C is a superclass of the current class + * - C otherwise + * again, the search procedure is the same. + * + * In the method here we implement method *resolution*. Whether or not the returned method is + * actually invoked at runtime depends on the invocation instruction and the class hierarchy, so + * the users (e.g. the inliner) have to be aware of method selection. + * + * Note that the returned method may be abstract (ACC_ABSTRACT), native (ACC_NATIVE) or signature + * polymorphic (methods `invoke` and `invokeExact` in class `MehtodHandles`). * * @return The [[MethodNode]] of the requested method and the [[InternalName]] of its declaring - * class, or an error message if the method could not be found. + * class, or an error message if the method could not be found. An error message is also + * returned if method resolution results in multiple default methods. */ def methodNode(ownerInternalNameOrArrayDescriptor: String, name: String, descriptor: String): Either[MethodNotFound, (MethodNode, InternalName)] = { - // on failure, returns a list of class names that could not be found on the classpath - def methodNodeImpl(ownerInternalName: InternalName): Either[List[ClassNotFound], (MethodNode, InternalName)] = { - classNode(ownerInternalName) match { - case Left(e) => Left(List(e)) - case Right(c) => - c.methods.asScala.find(m => m.name == name && m.desc == descriptor) match { - case Some(m) => Right((m, ownerInternalName)) - case None => findInParents(Option(c.superName) ++: c.interfaces.asScala.toList, Nil) - } + def findMethod(c: ClassNode): Option[MethodNode] = c.methods.asScala.find(m => m.name == name && m.desc == descriptor) + + // https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.9: "In Java SE 8, the only + // signature polymorphic methods are the invoke and invokeExact methods of the class MethodHandle. + def isSignaturePolymorphic(owner: InternalName) = owner == coreBTypes.jliMethodHandleRef.internalName && (name == "invoke" || name == "invokeExact") + + // Note: if `owner` is an interface, in the first iteration we search for a matching member in the interface itself. + // If that fails, the recursive invocation checks in the superclass (which is Object) with `publicInstanceOnly == true`. + // This is specified in jvms-5.4.3.4: interface method resolution only returns public, non-static methods of Object. + def findInSuperClasses(owner: ClassNode, publicInstanceOnly: Boolean = false): Either[ClassNotFound, Option[(MethodNode, InternalName)]] = { + findMethod(owner) match { + case Some(m) if !publicInstanceOnly || (isPublicMethod(m) && !isStaticMethod(m)) => Right(Some((m, owner.name))) + case None => + if (isSignaturePolymorphic(owner.name)) Right(Some((owner.methods.asScala.find(_.name == name).get, owner.name))) + else if (owner.superName == null) Right(None) + else classNode(owner.superName).flatMap(findInSuperClasses(_, isInterface(owner))) } } - // find the MethodNode in one of the parent classes - def findInParents(parents: List[InternalName], failedClasses: List[ClassNotFound]): Either[List[ClassNotFound], (MethodNode, InternalName)] = parents match { - case x :: xs => methodNodeImpl(x).left.flatMap(failed => findInParents(xs, failed ::: failedClasses)) - case Nil => Left(failedClasses) + def findInInterfaces(initialOwner: ClassNode): Either[ClassNotFound, Option[(MethodNode, InternalName)]] = { + val visited = mutable.Set.empty[InternalName] + val found = mutable.ListBuffer.empty[(MethodNode, ClassNode)] + + def findIn(owner: ClassNode): Option[ClassNotFound] = { + for (i <- owner.interfaces.asScala if !visited(i)) classNode(i) match { + case Left(e) => return Some(e) + case Right(c) => + visited += i + // abstract and static methods are excluded, see jvms-5.4.3.3 + for (m <- findMethod(c) if !isPrivateMethod(m) && !isStaticMethod(m)) found += ((m, c)) + val recusionResult = findIn(c) + if (recusionResult.isDefined) return recusionResult + } + None + } + + findIn(initialOwner) + + val result = + if (found.size <= 1) found.headOption + else { + val maxSpecific = found.filterNot({ + case (method, owner) => + isAbstractMethod(method) || { + val ownerTp = classBTypeFromClassNode(owner) + found exists { + case (other, otherOwner) => + (other ne method) && { + val otherTp = classBTypeFromClassNode(otherOwner) + otherTp.isSubtypeOf(ownerTp).get + } + } + } + }) + // (*) note that if there's no single, non-abstract, maximally-specific method, the jvm + // method resolution (jvms-5.4.3.3) returns any of the non-private, non-static parent + // methods at random (abstract or concrete). + // we chose not to do this here, to prevent the inliner from potentially inlining the + // wrong method. in other words, we guarantee that a concrete method is only returned if + // it resolves deterministically. + // however, there may be multiple abstract methods inherited. in this case we *do* want + // to return a result to allow performing accessibility checks in the inliner. note that + // for accessibility it does not matter which of these methods is return, as they are all + // non-private (i.e., public, protected is not possible, jvms-4.1). + // the remaining case (when there's no max-specific method, but some non-abstract one) + // does not occur in bytecode generated by scalac or javac. we return no result in this + // case. this may at worst prevent some optimizations from happening. + if (maxSpecific.size == 1) maxSpecific.headOption + else if (found.forall(p => isAbstractMethod(p._1))) found.headOption // (*) + else None + } + Right(result.map(p => (p._1, p._2.name))) } // In a MethodInsnNode, the `owner` field may be an array descriptor, for example when invoking `clone`. We don't have a method node to return in this case. - if (ownerInternalNameOrArrayDescriptor.charAt(0) == '[') - Left(MethodNotFound(name, descriptor, ownerInternalNameOrArrayDescriptor, Nil)) - else - methodNodeImpl(ownerInternalNameOrArrayDescriptor).left.map(MethodNotFound(name, descriptor, ownerInternalNameOrArrayDescriptor, _)) + if (ownerInternalNameOrArrayDescriptor.charAt(0) == '[') { + Left(MethodNotFound(name, descriptor, ownerInternalNameOrArrayDescriptor, None)) + } else { + def notFound(cnf: Option[ClassNotFound]) = Left(MethodNotFound(name, descriptor, ownerInternalNameOrArrayDescriptor, cnf)) + val res: Either[ClassNotFound, Option[(MethodNode, InternalName)]] = classNode(ownerInternalNameOrArrayDescriptor).flatMap(c => + findInSuperClasses(c) flatMap { + case None => findInInterfaces(c) + case res => Right(res) + } + ) + res match { + case Left(e) => notFound(Some(e)) + case Right(None) => notFound(None) + case Right(Some(res)) => Right(res) + } + } } private def parseClass(internalName: InternalName): Either[ClassNotFound, ClassNode] = { diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala index f8c16e34bd..63906d80e5 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala @@ -99,6 +99,10 @@ object BytecodeUtils { methodNode.name == INSTANCE_CONSTRUCTOR_NAME || methodNode.name == CLASS_CONSTRUCTOR_NAME } + def isPublicMethod(methodNode: MethodNode): Boolean = (methodNode.access & ACC_PUBLIC) != 0 + + def isPrivateMethod(methodNode: MethodNode): Boolean = (methodNode.access & ACC_PRIVATE) != 0 + def isStaticMethod(methodNode: MethodNode): Boolean = (methodNode.access & ACC_STATIC) != 0 def isAbstractMethod(methodNode: MethodNode): Boolean = (methodNode.access & ACC_ABSTRACT) != 0 @@ -107,10 +111,12 @@ object BytecodeUtils { def isNativeMethod(methodNode: MethodNode): Boolean = (methodNode.access & ACC_NATIVE) != 0 - def hasCallerSensitiveAnnotation(methodNode: MethodNode) = methodNode.visibleAnnotations != null && methodNode.visibleAnnotations.asScala.exists(_.desc == "Lsun/reflect/CallerSensitive;") + def hasCallerSensitiveAnnotation(methodNode: MethodNode): Boolean = methodNode.visibleAnnotations != null && methodNode.visibleAnnotations.asScala.exists(_.desc == "Lsun/reflect/CallerSensitive;") def isFinalClass(classNode: ClassNode): Boolean = (classNode.access & ACC_FINAL) != 0 + def isInterface(classNode: ClassNode): Boolean = (classNode.access & ACC_INTERFACE) != 0 + def isFinalMethod(methodNode: MethodNode): Boolean = (methodNode.access & (ACC_FINAL | ACC_PRIVATE | ACC_STATIC)) != 0 def isStrictfpMethod(methodNode: MethodNode): Boolean = (methodNode.access & ACC_STRICT) != 0 diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala index 156c80d5a1..d241acf7b1 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala @@ -131,19 +131,19 @@ class CallGraph[BT <: BTypes](val btypes: BT) { (method, declarationClass) <- byteCodeRepository.methodNode(call.owner, call.name, call.desc): Either[OptimizerWarning, (MethodNode, InternalName)] (declarationClassNode, source) <- byteCodeRepository.classNodeAndSource(declarationClass): Either[OptimizerWarning, (ClassNode, Source)] } yield { - val declarationClassBType = classBTypeFromClassNode(declarationClassNode) - val info = analyzeCallsite(method, declarationClassBType, call, source) - import info._ - Callee( - callee = method, - calleeDeclarationClass = declarationClassBType, - safeToInline = safeToInline, - canInlineFromSource = canInlineFromSource, - annotatedInline = annotatedInline, - annotatedNoInline = annotatedNoInline, - samParamTypes = info.samParamTypes, - calleeInfoWarning = warning) - } + val declarationClassBType = classBTypeFromClassNode(declarationClassNode) + val info = analyzeCallsite(method, declarationClassBType, call, source) + import info._ + Callee( + callee = method, + calleeDeclarationClass = declarationClassBType, + safeToInline = safeToInline, + canInlineFromSource = canInlineFromSource, + annotatedInline = annotatedInline, + annotatedNoInline = annotatedNoInline, + samParamTypes = info.samParamTypes, + calleeInfoWarning = warning) + } val argInfos = computeArgInfos(callee, call, prodCons) @@ -388,12 +388,11 @@ class CallGraph[BT <: BTypes](val btypes: BT) { * @param calleeInfoWarning An inliner warning if some information was not available while * gathering the information about this callee. */ - final case class Callee( - callee: MethodNode, calleeDeclarationClass: btypes.ClassBType, - safeToInline: Boolean, canInlineFromSource: Boolean, - annotatedInline: Boolean, annotatedNoInline: Boolean, - samParamTypes: IntMap[btypes.ClassBType], - calleeInfoWarning: Option[CalleeInfoWarning]) { + final case class Callee(callee: MethodNode, calleeDeclarationClass: btypes.ClassBType, + safeToInline: Boolean, canInlineFromSource: Boolean, + annotatedInline: Boolean, annotatedNoInline: Boolean, + samParamTypes: IntMap[btypes.ClassBType], + calleeInfoWarning: Option[CalleeInfoWarning]) { override def toString = s"Callee($calleeDeclarationClass.${callee.name})" } diff --git a/src/compiler/scala/tools/nsc/settings/Warnings.scala b/src/compiler/scala/tools/nsc/settings/Warnings.scala index 310025a336..7ef606b6ef 100644 --- a/src/compiler/scala/tools/nsc/settings/Warnings.scala +++ b/src/compiler/scala/tools/nsc/settings/Warnings.scala @@ -59,6 +59,7 @@ trait Warnings { val PackageObjectClasses = LintWarning("package-object-classes", "Class or object defined in package object.") val UnsoundMatch = LintWarning("unsound-match", "Pattern match may not be typesafe.") val StarsAlign = LintWarning("stars-align", "Pattern sequence wildcard must align with sequence component.") + val Constant = LintWarning("constant", "Evaluation of a constant arithmetic expression results in an error.") def allLintWarnings = values.toSeq.asInstanceOf[Seq[LintWarning]] } @@ -80,6 +81,7 @@ trait Warnings { def warnPackageObjectClasses = lint contains PackageObjectClasses def warnUnsoundMatch = lint contains UnsoundMatch def warnStarsAlign = lint contains StarsAlign + def warnConstant = lint contains Constant // Lint warnings that are currently -Y, but deprecated in that usage @deprecated("Use warnAdaptedArgs", since="2.11.2") diff --git a/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala b/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala index 828a79ac4f..2cd4785fbf 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala @@ -40,9 +40,10 @@ abstract class ConstantFolder { if ((x ne null) && x.tag != UnitTag) tree setType ConstantType(x) else tree } catch { - case _: ArithmeticException => tree // the code will crash at runtime, - // but that is better than the - // compiler itself crashing + case e: ArithmeticException => + if (settings.warnConstant) + warning(tree.pos, s"Evaluation of a constant expression results in an arithmetic error: ${e.getMessage}") + tree } private def foldUnop(op: Name, x: Constant): Constant = (op, x.tag) match { @@ -158,7 +159,7 @@ abstract class ConstantFolder { else if (x.isNumeric && y.isNumeric) math.max(x.tag, y.tag) else NoTag - try optag match { + optag match { case BooleanTag => foldBooleanOp(op, x, y) case ByteTag | ShortTag | CharTag | IntTag => foldSubrangeOp(op, x, y) case LongTag => foldLongOp(op, x, y) @@ -167,8 +168,5 @@ abstract class ConstantFolder { case StringTag if op == nme.ADD => Constant(x.stringValue + y.stringValue) case _ => null } - catch { - case _: ArithmeticException => null - } } } diff --git a/src/eclipse/partest/.classpath b/src/eclipse/partest/.classpath index 0b98dc67da..ab9ede5a19 100644 --- a/src/eclipse/partest/.classpath +++ b/src/eclipse/partest/.classpath @@ -9,6 +9,6 @@ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry combineaccessrules="false" kind="src" path="/scala-compiler"/> <classpathentry combineaccessrules="false" kind="src" path="/scala-library"/> - <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/partest/scala-partest_2.12.0_M4-1.0.13.jar"/> + <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/partest/scala-partest_2.12.0-M4-1.0.13.jar"/> <classpathentry kind="output" path="build-quick-partest-extras"/> </classpath> diff --git a/src/eclipse/scaladoc/.classpath b/src/eclipse/scaladoc/.classpath index 870d1da61d..40387983b0 100644 --- a/src/eclipse/scaladoc/.classpath +++ b/src/eclipse/scaladoc/.classpath @@ -6,8 +6,8 @@ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry combineaccessrules="false" kind="src" path="/scala-compiler"/> <classpathentry combineaccessrules="false" kind="src" path="/scala-library"/> - <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/scaladoc/scala-xml_2.12.0_M4-1.0.5.jar"/> - <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/scaladoc/scala-parser-combinators_2.12.0_M4-1.0.4.jar"/> - <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/partest/scala-partest_2.12.0_M4-1.0.13.jar"/> + <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/scaladoc/scala-xml_2.12.0-M4-1.0.5.jar"/> + <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/scaladoc/scala-parser-combinators_2.12.0-M4-1.0.4.jar"/> + <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/partest/scala-partest_2.12.0-M4-1.0.13.jar"/> <classpathentry kind="output" path="build-quick-scaladoc"/> </classpath> diff --git a/src/eclipse/test-junit/.classpath b/src/eclipse/test-junit/.classpath index 881b2b79ca..3635c85112 100644 --- a/src/eclipse/test-junit/.classpath +++ b/src/eclipse/test-junit/.classpath @@ -10,7 +10,7 @@ <classpathentry combineaccessrules="false" kind="src" path="/repl"/> <classpathentry combineaccessrules="false" kind="src" path="/partest-extras"/> <classpathentry combineaccessrules="false" kind="src" path="/scaladoc"/> - <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/scaladoc/scala-xml_2.12.0_M4-1.0.5.jar"/> + <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/scaladoc/scala-xml_2.12.0-M4-1.0.5.jar"/> <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/> <classpathentry kind="output" path="build-test-junit"/> </classpath> diff --git a/src/intellij/scala.ipr.SAMPLE b/src/intellij/scala.ipr.SAMPLE index 8184b0e45b..79ad2808f6 100644 --- a/src/intellij/scala.ipr.SAMPLE +++ b/src/intellij/scala.ipr.SAMPLE @@ -77,7 +77,7 @@ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-asm/bundles/scala-asm-5.0.4-scala-3.jar!/" /> <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-xml_2.12.0-M3-dc9effe/bundles/scala-xml_2.12.0-M3-dc9effe-1.0.5.jar!/" /> <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-parser-combinators_2.12.0-M3-dc9effe/bundles/scala-parser-combinators_2.12.0-M3-dc9effe-1.0.4.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/jline/jline/jars/jline-2.12.1.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/jline/jline/jars/jline-2.14.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES /> @@ -100,7 +100,7 @@ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-asm/bundles/scala-asm-5.0.4-scala-3.jar!/" /> <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-xml_2.12.0-M3-dc9effe/bundles/scala-xml_2.12.0-M3-dc9effe-1.0.5.jar!/" /> <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-parser-combinators_2.12.0-M3-dc9effe/bundles/scala-parser-combinators_2.12.0-M3-dc9effe-1.0.4.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/jline/jline/jars/jline-2.12.1.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/jline/jline/jars/jline-2.14.1.jar!/" /> <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-partest_2.12.0-M3-dc9effe/jars/scala-partest_2.12.0-M3-dc9effe-1.0.13.jar!/" /> <root url="jar://$USER_HOME$/.ivy2/cache/com.googlecode.java-diff-utils/diffutils/jars/diffutils-1.3.0.jar!/" /> <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/test-interface/jars/test-interface-1.0.jar!/" /> @@ -126,7 +126,7 @@ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-asm/bundles/scala-asm-5.0.4-scala-3.jar!/" /> <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-xml_2.12.0-M3-dc9effe/bundles/scala-xml_2.12.0-M3-dc9effe-1.0.5.jar!/" /> <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-parser-combinators_2.12.0-M3-dc9effe/bundles/scala-parser-combinators_2.12.0-M3-dc9effe-1.0.4.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/jline/jline/jars/jline-2.12.1.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/jline/jline/jars/jline-2.14.1.jar!/" /> <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-partest_2.12.0-M3-dc9effe/jars/scala-partest_2.12.0-M3-dc9effe-1.0.13.jar!/" /> <root url="jar://$USER_HOME$/.ivy2/cache/com.googlecode.java-diff-utils/diffutils/jars/diffutils-1.3.0.jar!/" /> <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/test-interface/jars/test-interface-1.0.jar!/" /> @@ -159,7 +159,7 @@ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-asm/bundles/scala-asm-5.0.4-scala-3.jar!/" /> <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-xml_2.12.0-M3-dc9effe/bundles/scala-xml_2.12.0-M3-dc9effe-1.0.5.jar!/" /> <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-parser-combinators_2.12.0-M3-dc9effe/bundles/scala-parser-combinators_2.12.0-M3-dc9effe-1.0.4.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/jline/jline/jars/jline-2.12.1.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/jline/jline/jars/jline-2.14.1.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES /> @@ -303,7 +303,7 @@ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-asm/bundles/scala-asm-5.0.4-scala-3.jar!/" /> <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-xml_2.12.0-M3-dc9effe/bundles/scala-xml_2.12.0-M3-dc9effe-1.0.5.jar!/" /> <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-parser-combinators_2.12.0-M3-dc9effe/bundles/scala-parser-combinators_2.12.0-M3-dc9effe-1.0.4.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/jline/jline/jars/jline-2.12.1.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/jline/jline/jars/jline-2.14.1.jar!/" /> <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-partest_2.12.0-M3-dc9effe/jars/scala-partest_2.12.0-M3-dc9effe-1.0.13.jar!/" /> <root url="jar://$USER_HOME$/.ivy2/cache/com.googlecode.java-diff-utils/diffutils/jars/diffutils-1.3.0.jar!/" /> <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/test-interface/jars/test-interface-1.0.jar!/" /> @@ -312,4 +312,4 @@ <SOURCES /> </library> </component> -</project>
\ No newline at end of file +</project> diff --git a/src/library/scala/util/control/Exception.scala b/src/library/scala/util/control/Exception.scala index 8aa4073a51..64f491d7f0 100644 --- a/src/library/scala/util/control/Exception.scala +++ b/src/library/scala/util/control/Exception.scala @@ -13,21 +13,136 @@ package control import scala.reflect.{ ClassTag, classTag } import scala.language.implicitConversions - /** Classes representing the components of exception handling. - * Each class is independently composable. Some example usages: + * + * Each class is independently composable. + * + * This class differs from [[scala.util.Try]] in that it focuses on composing exception handlers rather than + * composing behavior. All behavior should be composed first and fed to a [[Catch]] object using one of the + * `opt`, `either` or `withTry` methods. Taken together the classes provide a DSL for composing catch and finally + * behaviors. + * + * === Examples === + * + * Create a `Catch` which handles specified exceptions. * {{{ * import scala.util.control.Exception._ * import java.net._ * * val s = "http://www.scala-lang.org/" - * val x1 = catching(classOf[MalformedURLException]) opt new URL(s) - * val x2 = catching(classOf[MalformedURLException], classOf[NullPointerException]) either new URL(s) + * + * // Some(http://www.scala-lang.org/) + * val x1: Option[URL] = catching(classOf[MalformedURLException]) opt new URL(s) + * + * // Right(http://www.scala-lang.org/) + * val x2: Either[Throwable,URL] = + * catching(classOf[MalformedURLException], classOf[NullPointerException]) either new URL(s) + * + * // Success(http://www.scala-lang.org/) + * val x3: Try[URL] = catching(classOf[MalformedURLException], classOf[NullPointerException]) withTry new URL(s) + * + * val defaultUrl = new URL("http://example.com") + * // URL(http://example.com) because htt/xx throws MalformedURLException + * val x4: URL = failAsValue(classOf[MalformedURLException])(defaultUrl)(new URL("htt/xx")) + * }}} + * + * Create a `Catch` which logs exceptions using `handling` and `by`. + * {{{ + * def log(t: Throwable): Unit = t.printStackTrace + * + * val withThrowableLogging: Catch[Unit] = handling(classOf[MalformedURLException]) by (log) + * + * def printUrl(url: String) : Unit = { + * val con = new URL(url) openConnection() + * val source = scala.io.Source.fromInputStream(con.getInputStream()) + * source.getLines.foreach(println) + * } + * + * val badUrl = "htt/xx" + * // Prints stacktrace, + * // java.net.MalformedURLException: no protocol: htt/xx + * // at java.net.URL.<init>(URL.java:586) + * withThrowableLogging { printUrl(badUrl) } + * + * val goodUrl = "http://www.scala-lang.org/" + * // Prints page content, + * // <!DOCTYPE html> + * // <html> + * withThrowableLogging { printUrl(goodUrl) } + * }}} + * + * Use `unwrapping` to create a `Catch` that unwraps exceptions before rethrowing. + * {{{ + * class AppException(cause: Throwable) extends RuntimeException(cause) + * + * val unwrappingCatch: Catch[Nothing] = unwrapping(classOf[AppException]) + * + * def calcResult: Int = throw new AppException(new NullPointerException) + * + * // Throws NPE not AppException, + * // java.lang.NullPointerException + * // at .calcResult(<console>:17) + * val result = unwrappingCatch(calcResult) * }}} * - * This class differs from `scala.util.Try` in that it focuses on composing exception handlers rather than - * composing behavior. All behavior should be composed first and fed to a `Catch` object using one of the - * `opt` or `either` methods. + * Use `failAsValue` to provide a default when a specified exception is caught. + * + * {{{ + * val inputDefaulting: Catch[Int] = failAsValue(classOf[NumberFormatException])(0) + * val candidatePick = "seven" // scala.io.StdIn.readLine() + * + * // Int = 0 + * val pick = inputDefaulting(candidatePick.toInt) + * }}} + * + * Compose multiple `Catch`s with `or` to build a `Catch` that provides default values varied by exception. + * {{{ + * val formatDefaulting: Catch[Int] = failAsValue(classOf[NumberFormatException])(0) + * val nullDefaulting: Catch[Int] = failAsValue(classOf[NullPointerException])(-1) + * val otherDefaulting: Catch[Int] = nonFatalCatch withApply(_ => -100) + * + * val combinedDefaulting: Catch[Int] = formatDefaulting or nullDefaulting or otherDefaulting + * + * def p(s: String): Int = s.length * s.toInt + * + * // Int = 0 + * combinedDefaulting(p("tenty-nine")) + * + * // Int = -1 + * combinedDefaulting(p(null: String)) + * + * // Int = -100 + * combinedDefaulting(throw new IllegalStateException) + * + * // Int = 22 + * combinedDefaulting(p("11")) + * }}} + * + * @groupname composition-catch Catch behavior composition + * @groupprio composition-catch 10 + * @groupdesc composition-catch Build Catch objects from exception lists and catch logic + * + * @groupname composition-finally Finally behavior composition + * @groupprio composition-finally 20 + * @groupdesc composition-finally Build Catch objects from finally logic + * + * @groupname canned-behavior General purpose catch objects + * @groupprio canned-behavior 30 + * @groupdesc canned-behavior Catch objects with predefined behavior. Use combinator methods to compose additional behavior. + * + * @groupname dsl DSL behavior composition + * @groupprio dsl 40 + * @groupdesc dsl Expressive Catch behavior composition + * + * @groupname composition-catch-promiscuously Promiscuous Catch behaviors + * @groupprio composition-catch-promiscuously 50 + * @groupdesc composition-catch-promiscuously Useful if catching `ControlThrowable` or `InterruptedException` is required. + * + * @groupname logic-container Logic Containers + * @groupprio logic-container 60 + * @groupdesc logic-container Containers for catch and finally behavior. + * + * @define protectedExceptions `ControlThrowable` or `InterruptedException` * * @author Paul Phillips */ @@ -51,6 +166,7 @@ object Exception { /** !!! Not at all sure of every factor which goes into this, * and/or whether we need multiple standard variations. + * @return true if `x` is $protectedExceptions otherwise false. */ def shouldRethrow(x: Throwable): Boolean = x match { case _: ControlThrowable => true @@ -70,7 +186,9 @@ object Exception { override def toString() = name + "(" + desc + ")" } - /** A container class for finally code. */ + /** A container class for finally code. + * @group logic-container + */ class Finally private[Exception](body: => Unit) extends Described { protected val name = "Finally" @@ -87,6 +205,7 @@ object Exception { * @param pf Partial function used when applying catch logic to determine result value * @param fin Finally logic which if defined will be invoked after catch logic * @param rethrow Predicate on throwables determining when to rethrow a caught [[Throwable]] + * @group logic-container */ class Catch[+T]( val pf: Catcher[T], @@ -153,23 +272,30 @@ object Exception { final def nonFatalCatcher[T]: Catcher[T] = mkThrowableCatcher({ case NonFatal(_) => true; case _ => false }, throw _) final def allCatcher[T]: Catcher[T] = mkThrowableCatcher(_ => true, throw _) - /** The empty `Catch` object. */ + /** The empty `Catch` object. + * @group canned-behavior + **/ final val noCatch: Catch[Nothing] = new Catch(nothingCatcher) withDesc "<nothing>" - /** A `Catch` object which catches everything. */ + /** A `Catch` object which catches everything. + * @group canned-behavior + **/ final def allCatch[T]: Catch[T] = new Catch(allCatcher[T]) withDesc "<everything>" - /** A `Catch` object which catches non-fatal exceptions. */ + /** A `Catch` object which catches non-fatal exceptions. + * @group canned-behavior + **/ final def nonFatalCatch[T]: Catch[T] = new Catch(nonFatalCatcher[T]) withDesc "<non-fatal>" /** Creates a `Catch` object which will catch any of the supplied exceptions. * Since the returned `Catch` object has no specific logic defined and will simply - * rethrow the exceptions it catches, you will typically want to call `opt` or - * `either` on the return value, or assign custom logic by calling "withApply". + * rethrow the exceptions it catches, you will typically want to call `opt`, + * `either` or `withTry` on the return value, or assign custom logic by calling "withApply". * * Note that `Catch` objects automatically rethrow `ControlExceptions` and others * which should only be caught in exceptional circumstances. If you really want * to catch exactly what you specify, use `catchingPromiscuously` instead. + * @group composition-catch */ def catching[T](exceptions: Class[_]*): Catch[T] = new Catch(pfFromExceptions(exceptions : _*)) withDesc (exceptions map (_.getName) mkString ", ") @@ -178,42 +304,56 @@ object Exception { /** Creates a `Catch` object which will catch any of the supplied exceptions. * Unlike "catching" which filters out those in shouldRethrow, this one will - * catch whatever you ask of it: `ControlThrowable`, `InterruptedException`, - * `OutOfMemoryError`, you name it. + * catch whatever you ask of it including $protectedExceptions. + * @group composition-catch-promiscuously */ def catchingPromiscuously[T](exceptions: Class[_]*): Catch[T] = catchingPromiscuously(pfFromExceptions(exceptions : _*)) def catchingPromiscuously[T](c: Catcher[T]): Catch[T] = new Catch(c, None, _ => false) - /** Creates a `Catch` object which catches and ignores any of the supplied exceptions. */ + /** Creates a `Catch` object which catches and ignores any of the supplied exceptions. + * @group composition-catch + */ def ignoring(exceptions: Class[_]*): Catch[Unit] = catching(exceptions: _*) withApply (_ => ()) - /** Creates a `Catch` object which maps all the supplied exceptions to `None`. */ + /** Creates a `Catch` object which maps all the supplied exceptions to `None`. + * @group composition-catch + */ def failing[T](exceptions: Class[_]*): Catch[Option[T]] = catching(exceptions: _*) withApply (_ => None) - /** Creates a `Catch` object which maps all the supplied exceptions to the given value. */ + /** Creates a `Catch` object which maps all the supplied exceptions to the given value. + * @group composition-catch + */ def failAsValue[T](exceptions: Class[_]*)(value: => T): Catch[T] = catching(exceptions: _*) withApply (_ => value) + class By[T,R](f: T => R) { + def by(x: T): R = f(x) + } + /** Returns a partially constructed `Catch` object, which you must give - * an exception handler function as an argument to `by`. Example: + * an exception handler function as an argument to `by`. + * @example * {{{ - * handling(ex1, ex2) by (_.printStackTrace) + * handling(classOf[MalformedURLException], classOf[NullPointerException]) by (_.printStackTrace) * }}} + * @group dsl */ - class By[T,R](f: T => R) { - def by(x: T): R = f(x) - } + // TODO: Add return type def handling[T](exceptions: Class[_]*) = { def fun(f: Throwable => T) = catching(exceptions: _*) withApply f new By[Throwable => T, Catch[T]](fun _) } - /** Returns a `Catch` object with no catch logic and the argument as `Finally`. */ + /** Returns a `Catch` object with no catch logic and the argument as the finally logic. + * @group composition-finally + */ def ultimately[T](body: => Unit): Catch[T] = noCatch andFinally body - /** Creates a `Catch` object which unwraps any of the supplied exceptions. */ + /** Creates a `Catch` object which unwraps any of the supplied exceptions. + * @group composition-catch + */ def unwrapping[T](exceptions: Class[_]*): Catch[T] = { def unwrap(x: Throwable): Throwable = if (wouldMatch(x, exceptions) && x.getCause != null) unwrap(x.getCause) diff --git a/src/reflect/scala/reflect/internal/transform/Erasure.scala b/src/reflect/scala/reflect/internal/transform/Erasure.scala index c069e2c198..412c49f571 100644 --- a/src/reflect/scala/reflect/internal/transform/Erasure.scala +++ b/src/reflect/scala/reflect/internal/transform/Erasure.scala @@ -113,7 +113,8 @@ trait Erasure { def apply(tp: Type): Type = tp match { case ConstantType(ct) => - if (ct.tag == ClazzTag) ConstantType(Constant(apply(ct.typeValue))) + // erase classOf[List[_]] to classOf[List]. special case for classOf[Unit], avoid erasing to classOf[BoxedUnit]. + if (ct.tag == ClazzTag && ct.typeValue.typeSymbol != UnitClass) ConstantType(Constant(apply(ct.typeValue))) else tp case st: ThisType if st.sym.isPackageClass => tp @@ -165,7 +166,7 @@ trait Erasure { /** The erasure |T| of a type T. This is: * - * - For a constant type, itself. + * - For a constant type classOf[T], classOf[|T|], unless T is Unit. For any other constant type, itself. * - For a type-bounds structure, the erasure of its upper bound. * - For every other singleton type, the erasure of its supertype. * - For a typeref scala.Array+[T] where T is an abstract type, AnyRef. diff --git a/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineReader.scala b/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineReader.scala index 1f2b0952e7..95964e18d9 100644 --- a/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineReader.scala +++ b/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineReader.scala @@ -11,10 +11,9 @@ import java.util.{Collection => JCollection, List => JList} import _root_.jline.{console => jconsole} import jline.console.ConsoleReader -import jline.console.completer.{CompletionHandler, Completer} +import jline.console.completer.{CandidateListCompletionHandler, Completer, CompletionHandler} import jconsole.history.{History => JHistory} - import scala.tools.nsc.interpreter import scala.tools.nsc.interpreter.{Completion, NoCompletion} import scala.tools.nsc.interpreter.Completion.Candidates @@ -133,32 +132,15 @@ private class JLineConsoleReader extends jconsole.ConsoleReader with interpreter newCursor } } + getCompletionHandler match { + case clch: CandidateListCompletionHandler => clch.setPrintSpaceAfterFullCompletion(false) + } completion match { case NoCompletion => () case _ => this addCompleter completer } - // This is a workaround for https://github.com/jline/jline2/issues/208 - // and should not be necessary once we upgrade to JLine 2.13.1 - /// - // Test by: - // scala> {" ".char}<LEFT><TAB> - // - // And checking we don't get an extra } on the line. - /// - val handler = getCompletionHandler - setCompletionHandler(new CompletionHandler { - override def complete(consoleReader: ConsoleReader, list: JList[CharSequence], i: Int): Boolean = { - try { - handler.complete(consoleReader, list, i) - } finally if (getCursorBuffer.cursor != getCursorBuffer.length()) { - print(" ") - getCursorBuffer.write(' ') - backspace() - } - } - }) setAutoprintThreshold(400) // max completion candidates without warning } } diff --git a/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala b/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala index 8cd8a7ee09..d3b4bf8ff5 100644 --- a/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala +++ b/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala @@ -295,7 +295,7 @@ trait CommentFactoryBase { this: MemberLookupBase => } case line :: ls if (lastTagKey.isDefined) => { - val newtags = if (!line.isEmpty) { + val newtags = if (!line.isEmpty || inCodeBlock) { val key = lastTagKey.get val value = ((tags get key): @unchecked) match { diff --git a/test/files/pos/constant-warning.check b/test/files/pos/constant-warning.check new file mode 100644 index 0000000000..f7df2165d1 --- /dev/null +++ b/test/files/pos/constant-warning.check @@ -0,0 +1,4 @@ +constant-warning.scala:2: warning: Evaluation of a constant expression results in an arithmetic error: / by zero + val fails = 1 + 2 / (3 - 2 - 1) + ^ +one warning found diff --git a/test/files/pos/constant-warning.flags b/test/files/pos/constant-warning.flags new file mode 100644 index 0000000000..d00cbbe77b --- /dev/null +++ b/test/files/pos/constant-warning.flags @@ -0,0 +1 @@ +-Xlint:constant diff --git a/test/files/pos/constant-warning.scala b/test/files/pos/constant-warning.scala new file mode 100644 index 0000000000..c8ca8823e7 --- /dev/null +++ b/test/files/pos/constant-warning.scala @@ -0,0 +1,3 @@ +object Test { + val fails = 1 + 2 / (3 - 2 - 1) +} diff --git a/test/junit/scala/issues/RunTest.scala b/test/junit/scala/issues/RunTest.scala index 781f2ef343..0605947e63 100644 --- a/test/junit/scala/issues/RunTest.scala +++ b/test/junit/scala/issues/RunTest.scala @@ -147,4 +147,16 @@ class RunTest extends ClearAfterClass { assertEquals(run[String](definitions("Object") + runCode), "hi" * 9) assertEquals(run[String](definitions("String") + runCode), "hi" * 9) // bridge method for clone generated } + + @Test + def classOfUnitConstant(): Unit = { + val code = + """abstract class A { def f: Class[_] } + |class C extends A { final val f = classOf[Unit] } + |val c = new C + |(c.f, (c: A).f) + """.stripMargin + val u = Void.TYPE + assertEquals(run[(Class[_], Class[_])](code), (u, u)) + } } diff --git a/test/junit/scala/tools/nsc/backend/jvm/CodeGenTools.scala b/test/junit/scala/tools/nsc/backend/jvm/CodeGenTools.scala index fe43ed2f6a..389e5b2ead 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/CodeGenTools.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/CodeGenTools.scala @@ -206,6 +206,12 @@ object CodeGenTools { assert(actual == expected, s"\nFound : ${quote(actual)}\nExpected: ${quote(expected)}") } + def assertNoIndy(m: Method): Unit = assertNoIndy(m.instructions) + def assertNoIndy(l: List[Instruction]) = { + val indy = l collect { case i: InvokeDynamic => i } + assert(indy.isEmpty, indy) + } + def getSingleMethod(classNode: ClassNode, name: String): Method = convertMethod(classNode.methods.asScala.toList.find(_.name == name).get) diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala index 5090e9c83b..1597c75a7e 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala @@ -103,12 +103,12 @@ class InlineWarningTest extends ClearAfterClass { val warns = List( """failed to determine if bar should be inlined: |The method bar()I could not be found in the class A or any of its parents. - |Note that the following parent classes are defined in Java sources (mixed compilation), no bytecode is available: A""".stripMargin, + |Note that the parent class A is defined in a Java source (mixed compilation), no bytecode is available.""".stripMargin, """B::flop()I is annotated @inline but could not be inlined: |Failed to check if B::flop()I can be safely inlined to B without causing an IllegalAccessError. Checking instruction INVOKESTATIC A.bar ()I failed: |The method bar()I could not be found in the class A or any of its parents. - |Note that the following parent classes are defined in Java sources (mixed compilation), no bytecode is available: A""".stripMargin) + |Note that the parent class A is defined in a Java source (mixed compilation), no bytecode is available.""".stripMargin) var c = 0 val List(b) = compile(scalaCode, List((javaCode, "A.java")), allowMessage = i => {c += 1; warns.tail.exists(i.msg contains _)}) diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala index 1765a355fd..e2a495fb2b 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala @@ -428,7 +428,7 @@ class InlinerTest extends ClearAfterClass { """B::flop()I is annotated @inline but could not be inlined: |Failed to check if B::flop()I can be safely inlined to B without causing an IllegalAccessError. Checking instruction INVOKESTATIC A.bar ()I failed: |The method bar()I could not be found in the class A or any of its parents. - |Note that the following parent classes are defined in Java sources (mixed compilation), no bytecode is available: A""".stripMargin + |Note that the parent class A is defined in a Java source (mixed compilation), no bytecode is available.""".stripMargin var c = 0 val List(b) = compile(scalaCode, List((javaCode, "A.java")), allowMessage = i => {c += 1; i.msg contains warn}) @@ -833,7 +833,7 @@ class InlinerTest extends ClearAfterClass { val warn = """failed to determine if <init> should be inlined: |The method <init>()V could not be found in the class A$Inner or any of its parents. - |Note that the following parent classes could not be found on the classpath: A$Inner""".stripMargin + |Note that the parent class A$Inner could not be found on the classpath.""".stripMargin var c = 0 @@ -955,18 +955,12 @@ class InlinerTest extends ClearAfterClass { val List(c, _, _) = compile(code) val t1 = getSingleMethod(c, "t1") - assert(t1.instructions forall { // indy is eliminated by push-pop - case _: InvokeDynamic => false - case _ => true - }) + assertNoIndy(t1) // the indy call is inlined into t, and the closure elimination rewrites the closure invocation to the body method assertInvoke(t1, "C", "C$$$anonfun$2") val t2 = getSingleMethod(c, "t2") - assert(t2.instructions forall { // indy is eliminated by push-pop - case _: InvokeDynamic => false - case _ => true - }) + assertNoIndy(t2) assertInvoke(t2, "M$", "M$$$anonfun$1") } @@ -1492,4 +1486,31 @@ class InlinerTest extends ClearAfterClass { // the forwarder C.f is inlined, so there's no invocation assertSameSummary(getSingleMethod(c, "f"), List(ICONST_1, IRETURN)) } + + @Test + def sd140(): Unit = { + val code = + """trait T { @inline def f = 0 } + |trait U extends T { @inline override def f = 1 } + |trait V extends T { def m = 0 } + |final class K extends V with U { override def m = super[V].m } + |class C { def t = (new K).f } + """.stripMargin + val c :: _ = compile(code) + assertSameSummary(getSingleMethod(c, "t"), List(NEW, "<init>", ICONST_1, IRETURN)) // ICONST_1, U.f is inlined (not T.f) + } + + @Test + def inlineArrayForeach(): Unit = { + val code = + """class C { + | def consume(x: Int) = () + | def t(a: Array[Int]): Unit = a foreach consume + |} + """.stripMargin + val List(c) = compile(code) + val t = getSingleMethod(c, "t") + assertNoIndy(t) + assertInvoke(t, "C", "C$$$anonfun$1") + } } diff --git a/test/scaladoc/run/t9752.check b/test/scaladoc/run/t9752.check new file mode 100644 index 0000000000..daeafb8ecc --- /dev/null +++ b/test/scaladoc/run/t9752.check @@ -0,0 +1,5 @@ +List(Body(List(Paragraph(Chain(List(Summary(Text())))), Code(class A + + +class B)))) +Done. diff --git a/test/scaladoc/run/t9752.scala b/test/scaladoc/run/t9752.scala new file mode 100644 index 0000000000..b11c7f5c32 --- /dev/null +++ b/test/scaladoc/run/t9752.scala @@ -0,0 +1,28 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + + override def code = s""" + /** + * Foo + * + * @example + * {{{ + * class A + * + * + * class B + * }}} + */ + object Foo + """ + + def scaladocSettings = "" + + def testModel(root: Package) = { + import access._ + val obj = root._object("Foo") + println(obj.comment.get.example) + } +} diff --git a/versions.properties b/versions.properties index d4112325d2..2ab66086fb 100644 --- a/versions.properties +++ b/versions.properties @@ -26,7 +26,7 @@ scala-xml.version.number=1.0.5 scala-parser-combinators.version.number=1.0.4 scala-swing.version.number=2.0.0-M2 scala-swing.version.osgi=2.0.0.M2 -jline.version=2.12.1 +jline.version=2.14.1 scala-asm.version=5.0.4-scala-3 # external modules, used internally (not shipped) |