diff options
51 files changed, 596 insertions, 147 deletions
@@ -10,6 +10,7 @@ Aleksandar Prokopec <aleksandar.prokopec@epfl.ch> <aleksandar@lampmac14.epfl.ch> Aleksandar Prokopec <aleksandar.prokopec@epfl.ch> <axel22@gmail.com> Alex Cruise <alex@cluonflux.com> Alex Cruise <alex@cluonflux.com> <alex@metaforsoftware.com> +A. P. Marki <som.snytt@gmail.com> Antonio Cunei <antonio.cunei@typesafe.com> Antonio Cunei <antonio.cunei@typesafe.com> <antonio.cunei@epfl.ch> Buraq Emir <buraq@epfl.ch> @@ -71,5 +72,6 @@ Unknown Committer <lost.soul@typesafe.com> <USER@epfl.ch> Unknown Committer <lost.soul@typesafe.com> <noreply@epfl.ch> Viktor Klang <viktor.klang@gmail.com> Vincent Cremet <cremet@epfl.ch> +Vladimir Nikolaev <vladimir.nikolaev9@gmail.com> Vojin Jovanovic <vojin.jovanovic@epfl.ch> Vojin Jovanovic <vojin.jovanovic@epfl.ch> <gvojin@gmail.com> diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 23a2e0b37f..e3d2bf14a0 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -1225,7 +1225,7 @@ self => // Like Swiss cheese, with holes def stringCheese: Tree = atPos(in.offset) { val start = in.offset - val interpolator = in.name + val interpolator = in.name.encoded // ident() for INTERPOLATIONID val partsBuf = new ListBuffer[Tree] val exprBuf = new ListBuffer[Tree] diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index 4ab6663423..b650cdfa09 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -1009,8 +1009,15 @@ abstract class GenICode extends SubComponent { } // emit conversion - if (generatedType != expectedType) - adapt(generatedType, expectedType, resCtx, tree.pos) + if (generatedType != expectedType) { + tree match { + case Literal(Constant(null)) if generatedType == NullReference => + // literal null on the stack (as opposed to a boxed null, see SI-8233), + // we can bypass `adapt` which would otherwise emitt a redundant [DROP, CONSTANT(null)] + case _ => + adapt(generatedType, expectedType, resCtx, tree.pos) + } + } resCtx } @@ -1060,6 +1067,9 @@ abstract class GenICode extends SubComponent { case (NothingReference, _) => ctx.bb.emit(THROW(ThrowableClass)) ctx.bb.enterIgnoreMode() + case (NullReference, REFERENCE(_)) => + // SI-8223 we can't assume that the stack contains a `null`, it might contain a Null$ + ctx.bb.emit(Seq(DROP(from), CONSTANT(Constant(null)))) case _ if from isAssignabledTo to => () case (_, UNIT) => diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala index 90c15bca61..55f45257dc 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala @@ -55,23 +55,26 @@ abstract class Pickler extends SubComponent { case _ => } } - // If there are any erroneous types in the tree, then we will crash - // when we pickle it: so let's report an error instead. We know next - // to nothing about what happened, but our supposition is a lot better - // than "bad type: <error>" in terms of explanatory power. - for (t <- unit.body) { - if (t.isErroneous) { - unit.error(t.pos, "erroneous or inaccessible type") - return - } - if (!t.isDef && t.hasSymbolField && t.symbol.isTermMacro) { - unit.error(t.pos, "macro has not been expanded") - return - } + try { + pickle(unit.body) + } catch { + case e: FatalError => + for (t <- unit.body) { + // If there are any erroneous types in the tree, then we will crash + // when we pickle it: so let's report an error instead. We know next + // to nothing about what happened, but our supposition is a lot better + // than "bad type: <error>" in terms of explanatory power. + // + // OPT: do this only as a recovery after fatal error. Checking in advance was expensive. + if (t.isErroneous) { + if (settings.debug) e.printStackTrace() + unit.error(t.pos, "erroneous or inaccessible type") + return + } + } + throw e } - - pickle(unit.body) } } diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index aaaa3287c7..ccfddab94a 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -533,6 +533,8 @@ abstract class Erasure extends AddInterfaces class Eraser(_context: Context) extends Typer(_context) with TypeAdapter { val typer = this.asInstanceOf[analyzer.Typer] + override protected def stabilize(tree: Tree, pre: Type, mode: Mode, pt: Type): Tree = tree + /** Replace member references as follows: * * - `x == y` for == in class Any becomes `x equals y` with equals in class Object. diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 3791af1629..c59b726076 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -306,6 +306,9 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { /** Return the specialized name of 'sym' in the given environment. It * guarantees the same result regardless of the map order by sorting * type variables alphabetically. + * + * !!! Is this safe in the face of the following? + * scala> trait T { def foo[A] = 0}; object O extends T { override def foo[B] = 0 } */ private def specializedName(sym: Symbol, env: TypeEnv): TermName = { val tvars = ( @@ -391,13 +394,16 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { * enclosing member with the annotation. */ private def needsSpecialization(env: TypeEnv, sym: Symbol): Boolean = ( - !sym.ownerChain.exists(_ hasAnnotation UnspecializedClass) && ( + !hasUnspecializableAnnotation(sym) && ( specializedTypeVars(sym).intersect(env.keySet).diff(wasSpecializedForTypeVars(sym)).nonEmpty || sym.isClassConstructor && (sym.enclClass.typeParams exists (_.isSpecialized)) || isNormalizedMember(sym) && info(sym).typeBoundsIn(env) ) ) + private def hasUnspecializableAnnotation(sym: Symbol): Boolean = + sym.ownerChain.exists(_ hasAnnotation UnspecializedClass) + def isNormalizedMember(m: Symbol) = m.isSpecialized && (info get m exists { case NormalizedMember(_) => true case _ => false @@ -433,10 +439,10 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { else specializedTypeVars(sym.typeParams zip args collect { case (tp, arg) if tp.isSpecialized => arg }) - case PolyType(tparams, resTpe) => specializedTypeVars(resTpe :: tparams.map(_.info)) + case PolyType(tparams, resTpe) => specializedTypeVars(resTpe :: mapList(tparams)(symInfo)) // OPT // since this method may be run at phase typer (before uncurry, where NMTs are eliminated) case NullaryMethodType(resTpe) => specializedTypeVars(resTpe) - case MethodType(argSyms, resTpe) => specializedTypeVars(resTpe :: argSyms.map(_.tpe)) + case MethodType(argSyms, resTpe) => specializedTypeVars(resTpe :: mapList(argSyms)(symTpe)) // OPT case ExistentialType(_, res) => specializedTypeVars(res) case AnnotatedType(_, tp) => specializedTypeVars(tp) case TypeBounds(lo, hi) => specializedTypeVars(lo :: hi :: Nil) @@ -907,16 +913,20 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } if (sym.isMethod) { - val stvars = specializedTypeVars(sym) - if (stvars.nonEmpty) - debuglog("specialized %s on %s".format(sym.fullLocationString, stvars.map(_.name).mkString(", "))) + if (hasUnspecializableAnnotation(sym)) { + List() + } else { + val stvars = specializedTypeVars(sym) + if (stvars.nonEmpty) + debuglog("specialized %s on %s".format(sym.fullLocationString, stvars.map(_.name).mkString(", "))) - val tps1 = if (sym.isConstructor) tps filter (sym.info.paramTypes contains _) else tps - val tps2 = tps1 filter stvars - if (!sym.isDeferred) - addConcreteSpecMethod(sym) + val tps1 = if (sym.isConstructor) tps filter (sym.info.paramTypes contains _) else tps + val tps2 = tps1 filter stvars + if (!sym.isDeferred) + addConcreteSpecMethod(sym) - specializeOn(tps2) + specializeOn(tps2) + } } else Nil } diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala index b471d16ddd..5973c70583 100644 --- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala +++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala @@ -128,6 +128,7 @@ abstract class TailCalls extends Transform { logResult(msg)(method.newValue(nme.THIS, pos, SYNTHETIC) setInfo currentClass.typeOfThis) } override def toString = s"${method.name} tparams=$tparams tailPos=$tailPos label=$label label info=${label.info}" + } object EmptyTailContext extends TailContext { @@ -185,6 +186,18 @@ abstract class TailCalls extends Transform { private def noTailContext() = new ClonedTailContext(ctx, tailPos = false) private def yesTailContext() = new ClonedTailContext(ctx, tailPos = true) + + override def transformUnit(unit: CompilationUnit): Unit = { + try { + super.transformUnit(unit) + } finally { + // OPT clear these after each compilation unit + failPositions.clear() + failReasons.clear() + accessed.clear() + } + } + /** Rewrite this tree to contain no tail recursive calls */ def transform(tree: Tree, nctx: TailContext): Tree = { val saved = ctx @@ -218,12 +231,12 @@ abstract class TailCalls extends Transform { */ def fail(reason: String) = { debuglog("Cannot rewrite recursive call at: " + fun.pos + " because: " + reason) - failReasons(ctx) = reason + if (ctx.isMandatory) failReasons(ctx) = reason treeCopy.Apply(tree, noTailTransform(target), transformArgs) } /* Position of failure is that of the tree being considered. */ def failHere(reason: String) = { - failPositions(ctx) = fun.pos + if (ctx.isMandatory) failPositions(ctx) = fun.pos fail(reason) } def rewriteTailCall(recv: Tree): Tree = { @@ -237,7 +250,8 @@ abstract class TailCalls extends Transform { if (!ctx.isEligible) fail("it is neither private nor final so can be overridden") else if (!isRecursiveCall) { - if (receiverIsSuper) failHere("it contains a recursive call targeting a supertype") + if (ctx.isMandatory && receiverIsSuper) // OPT expensive check, avoid unless we will actually report the error + failHere("it contains a recursive call targeting a supertype") else failHere(defaultReason) } else if (!matchesTypeArgs) failHere("it is called recursively with different type arguments") @@ -245,6 +259,11 @@ abstract class TailCalls extends Transform { else if (!receiverIsSame) failHere("it changes type of 'this' on a polymorphic recursive call") else rewriteTailCall(receiver) } + + def isEligible(tree: DefDef) = { + val sym = tree.symbol + !(sym.hasAccessorFlag || sym.isConstructor) + } tree match { case ValDef(_, _, _, _) => @@ -253,7 +272,7 @@ abstract class TailCalls extends Transform { super.transform(tree) - case dd @ DefDef(_, name, _, vparamss0, _, rhs0) if !dd.symbol.hasAccessorFlag => + case dd @ DefDef(_, name, _, vparamss0, _, rhs0) if isEligible(dd) => val newCtx = new DefDefTailContext(dd) if (newCtx.isMandatory && !(newCtx containsRecursiveCall rhs0)) unit.error(tree.pos, "@tailrec annotated method contains no recursive calls") diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 4d0eda2377..2043eb5d5d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -418,7 +418,7 @@ trait ContextErrors { case TypeRef(_, _, arg :: _) if arg.typeSymbol == TupleClass(funArity) && funArity > 1 => sm"""| |Note: The expected type requires a one-argument function accepting a $funArity-Tuple. - | Consider a pattern matching anoynmous function, `{ case $example => ... }`""" + | Consider a pattern matching anonymous function, `{ case $example => ... }`""" case _ => "" } case _ => "" diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 598b12b00d..e5907e1a0f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -1303,7 +1303,7 @@ trait Contexts { self: Analyzer => var renamed = false var selectors = tree.selectors def current = selectors.head - while (selectors.nonEmpty && result == NoSymbol) { + while ((selectors ne Nil) && result == NoSymbol) { if (current.rename == name.toTermName) result = qual.tpe.nonLocalMember( // new to address #2733: consider only non-local members for imports if (name.isTypeName) current.name.toTypeName else current.name) diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 847211945c..776920ed42 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -788,7 +788,7 @@ trait Implicits { final class LocalShadower extends Shadower { val shadowed = util.HashSet[Name](512) def addInfos(infos: Infos) { - shadowed addEntries infos.map(_.name) + infos.foreach(i => shadowed.addEntry(i.name)) } def isShadowed(name: Name) = shadowed(name) } @@ -805,7 +805,6 @@ trait Implicits { private def isIneligible(info: ImplicitInfo) = ( info.isCyclicOrErroneous || isView && (info.sym eq Predef_conforms) - || shadower.isShadowed(info.name) || (!context.macrosEnabled && info.sym.isTermMacro) ) @@ -814,6 +813,7 @@ trait Implicits { def survives(info: ImplicitInfo) = ( !isIneligible(info) // cyclic, erroneous, shadowed, or specially excluded && isPlausiblyCompatible(info.tpe, wildPt) // optimization to avoid matchesPt + && !shadower.isShadowed(info.name) // OPT rare, only check for plausible candidates && matchesPt(info) // stable and matches expected type ) /** The implicits that are not valid because they come later in the source and diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index dd0923a696..997fd6fc65 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -595,6 +595,7 @@ trait Infer extends Checkable { } private[typechecker] def followApply(tp: Type): Type = tp match { + case _ if tp.isError => tp // SI-8228, `ErrorType nonPrivateMember nme.apply` returns an member with an erroneous type! case NullaryMethodType(restp) => val restp1 = followApply(restp) if (restp1 eq restp) tp else restp1 diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 27e8698676..645f267a21 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -1069,8 +1069,9 @@ trait Namers extends MethodSynthesis { } def overriddenSymbol(resTp: Type) = { + lazy val schema: Type = methodTypeSchema(resTp) // OPT create once. Must be lazy to avoid cycles in neg/t5093.scala intersectionType(methOwner.info.parents).nonPrivateMember(meth.name).filter { sym => - sym != NoSymbol && (site.memberType(sym) matches methodTypeSchema(resTp)) + sym != NoSymbol && (site.memberType(sym) matches schema) } } // TODO: see whether this or something similar would work instead: diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 68d724b6fc..f7684b93af 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1596,6 +1596,10 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } } + private def checkUnexpandedMacro(t: Tree) = + if (!t.isDef && t.hasSymbolField && t.symbol.isTermMacro) + unit.error(t.pos, "macro has not been expanded") + override def transform(tree: Tree): Tree = { val savedLocalTyper = localTyper val savedCurrentApplication = currentApplication @@ -1755,6 +1759,9 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans varianceValidator.traverse(tt.original) // See SI-7872 case _ => } + + checkUnexpandedMacro(result) + result } catch { case ex: TypeError => diff --git a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala index 995f98cc2c..57f27a05fd 100644 --- a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala +++ b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala @@ -46,7 +46,7 @@ trait StdAttachments { * The parameter is of type `Any`, because macros can expand both into trees and into annotations. */ def hasMacroExpansionAttachment(any: Any): Boolean = any match { - case tree: Tree => tree.attachments.get[MacroExpansionAttachment].isDefined + case tree: Tree => tree.hasAttachment[MacroExpansionAttachment] case _ => false } @@ -96,7 +96,7 @@ trait StdAttachments { */ def isMacroExpansionSuppressed(tree: Tree): Boolean = ( settings.Ymacroexpand.value == settings.MacroExpand.None // SI-6812 - || tree.attachments.get[SuppressMacroExpansionAttachment.type].isDefined + || tree.hasAttachment[SuppressMacroExpansionAttachment.type] || (tree match { // we have to account for the fact that during typechecking an expandee might become wrapped, // i.e. surrounded by an inferred implicit argument application or by an inferred type argument application. @@ -150,7 +150,7 @@ trait StdAttachments { /** Determines whether a tree should or should not be adapted, * because someone has put MacroImplRefAttachment on it. */ - def isMacroImplRef(tree: Tree): Boolean = tree.attachments.get[MacroImplRefAttachment.type].isDefined + def isMacroImplRef(tree: Tree): Boolean = tree.hasAttachment[MacroImplRefAttachment.type] /** Since mkInvoke, the applyDynamic/selectDynamic/etc desugarer, is disconnected * from typedNamedApply, the applyDynamicNamed argument rewriter, the latter diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 9776b1e80e..101e1526fe 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -579,7 +579,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper * 3. Turn tree type into stable type if possible and required by context. * 4. Give getClass calls a more precise type based on the type of the target of the call. */ - private def stabilize(tree: Tree, pre: Type, mode: Mode, pt: Type): Tree = { + protected def stabilize(tree: Tree, pre: Type, mode: Mode, pt: Type): Tree = { + // Side effect time! Don't be an idiot like me and think you // can move "val sym = tree.symbol" before this line, because // inferExprAlternative side-effects the tree's symbol. @@ -992,7 +993,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def adaptMismatchedSkolems() = { def canIgnoreMismatch = ( !context.reportErrors && isPastTyper - || tree.attachments.get[MacroExpansionAttachment].isDefined + || tree.hasAttachment[MacroExpansionAttachment] ) def bound = pt match { case ExistentialType(qs, _) => qs @@ -2232,14 +2233,14 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper DeprecatedParamNameError(p, n) } } - } - if (meth.isStructuralRefinementMember) - checkMethodStructuralCompatible(ddef) + if (meth.isStructuralRefinementMember) + checkMethodStructuralCompatible(ddef) - if (meth.isImplicit && !meth.isSynthetic) meth.info.paramss match { - case List(param) :: _ if !param.isImplicit => - checkFeature(ddef.pos, ImplicitConversionsFeature, meth.toString) - case _ => + if (meth.isImplicit && !meth.isSynthetic) meth.info.paramss match { + case List(param) :: _ if !param.isImplicit => + checkFeature(ddef.pos, ImplicitConversionsFeature, meth.toString) + case _ => + } } treeCopy.DefDef(ddef, typedMods, ddef.name, tparams1, vparamss1, tpt1, rhs1) setType NoType @@ -3411,7 +3412,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // instantiate dependent method types, must preserve singleton types where possible (stableTypeFor) -- example use case: // val foo = "foo"; def precise(x: String)(y: x.type): x.type = {...}; val bar : foo.type = precise(foo)(foo) // precise(foo) : foo.type => foo.type - val restpe = mt.resultType(args1 map (arg => gen stableTypeFor arg orElse arg.tpe)) + val restpe = mt.resultType(mapList(args1)(arg => gen stableTypeFor arg orElse arg.tpe)) def ifPatternSkipFormals(tp: Type) = tp match { case MethodType(_, rtp) if (mode.inPatternMode) => rtp case _ => tp @@ -3831,7 +3832,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // lifted out of typed1 because it's needed in typedImplicit0 protected def typedTypeApply(tree: Tree, mode: Mode, fun: Tree, args: List[Tree]): Tree = fun.tpe match { case OverloadedType(pre, alts) => - inferPolyAlternatives(fun, args map (_.tpe)) + inferPolyAlternatives(fun, mapList(args)(treeTpe)) val tparams = fun.symbol.typeParams //@M TODO: fun.symbol.info.typeParams ? (as in typedAppliedTypeTree) val args1 = if (sameLength(args, tparams)) { //@M: in case TypeApply we can't check the kind-arities of the type arguments, @@ -3851,7 +3852,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper typedTypeApply(tree, mode, fun setType fun.tpe.widen, args) case PolyType(tparams, restpe) if tparams.nonEmpty => if (sameLength(tparams, args)) { - val targs = args map (_.tpe) + val targs = mapList(args)(treeTpe) checkBounds(tree, NoPrefix, NoSymbol, tparams, targs, "") if (isPredefClassOf(fun.symbol)) typedClassOf(tree, args.head, noGen = true) @@ -4871,7 +4872,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper typedHigherKindedType(arg, mode, pt) } - val argtypes = args1 map (_.tpe) + val argtypes = mapList(args1)(treeTpe) foreach2(args, tparams) { (arg, tparam) => // note: can't use args1 in selector, because Binds got replaced diff --git a/src/library/scala/collection/immutable/List.scala b/src/library/scala/collection/immutable/List.scala index 90aabc5a9a..c3728fa02a 100644 --- a/src/library/scala/collection/immutable/List.scala +++ b/src/library/scala/collection/immutable/List.scala @@ -325,36 +325,7 @@ sealed abstract class List[+A] extends AbstractSeq[A] // Create a proxy for Java serialization that allows us to avoid mutation // during de-serialization. This is the Serialization Proxy Pattern. - protected final def writeReplace(): AnyRef = new SerializationProxy(this) -} - -@SerialVersionUID(1L) -private class SerializationProxy[B](@transient private var orig: List[B]) extends Serializable { - - private def writeObject(out: ObjectOutputStream) { - var xs: List[B] = orig - while (!xs.isEmpty) { - out.writeObject(xs.head) - xs = xs.tail - } - out.writeObject(ListSerializeEnd) - } - - // Java serialization calls this before readResolve during de-serialization. - // Read the whole list and store it in `orig`. - private def readObject(in: ObjectInputStream) { - val builder = List.newBuilder[B] - while (true) in.readObject match { - case ListSerializeEnd => - orig = builder.result() - return - case a => - builder += a.asInstanceOf[B] - } - } - - // Provide the result stored in `orig` for Java serialization - private def readResolve(): AnyRef = orig + protected final def writeReplace(): AnyRef = new List.SerializationProxy(this) } /** The empty list. @@ -385,8 +356,7 @@ case object Nil extends List[Nothing] { * @version 1.0, 15/07/2003 * @since 2.8 */ -final case class ::[B](private val hd: B, private[scala] var tl: List[B]) extends List[B] { - override def head : B = hd +final case class ::[B](override val head: B, private[scala] var tl: List[B]) extends List[B] { override def tail : List[B] = tl override def isEmpty: Boolean = false } @@ -405,6 +375,35 @@ object List extends SeqFactory[List] { override def empty[A]: List[A] = Nil override def apply[A](xs: A*): List[A] = xs.toList + + @SerialVersionUID(1L) + private class SerializationProxy[A](@transient private var orig: List[A]) extends Serializable { + + private def writeObject(out: ObjectOutputStream) { + var xs: List[A] = orig + while (!xs.isEmpty) { + out.writeObject(xs.head) + xs = xs.tail + } + out.writeObject(ListSerializeEnd) + } + + // Java serialization calls this before readResolve during de-serialization. + // Read the whole list and store it in `orig`. + private def readObject(in: ObjectInputStream) { + val builder = List.newBuilder[A] + while (true) in.readObject match { + case ListSerializeEnd => + orig = builder.result() + return + case a => + builder += a.asInstanceOf[A] + } + } + + // Provide the result stored in `orig` for Java serialization + private def readResolve(): AnyRef = orig + } } /** Only used for list serialization */ diff --git a/src/library/scala/collection/immutable/Set.scala b/src/library/scala/collection/immutable/Set.scala index e21a8dfa8a..0fbf7942d4 100644 --- a/src/library/scala/collection/immutable/Set.scala +++ b/src/library/scala/collection/immutable/Set.scala @@ -82,6 +82,16 @@ object Set extends ImmutableSetFactory[Set] { override def foreach[U](f: A => U): Unit = { f(elem1) } + override def exists(f: A => Boolean): Boolean = { + f(elem1) + } + override def forall(f: A => Boolean): Boolean = { + f(elem1) + } + override def find(f: A => Boolean): Option[A] = { + if (f(elem1)) Some(elem1) + else None + } } /** An optimized representation for immutable sets of size 2 */ @@ -102,6 +112,17 @@ object Set extends ImmutableSetFactory[Set] { override def foreach[U](f: A => U): Unit = { f(elem1); f(elem2) } + override def exists(f: A => Boolean): Boolean = { + f(elem1) || f(elem2) + } + override def forall(f: A => Boolean): Boolean = { + f(elem1) && f(elem2) + } + override def find(f: A => Boolean): Option[A] = { + if (f(elem1)) Some(elem1) + else if (f(elem2)) Some(elem2) + else None + } } /** An optimized representation for immutable sets of size 3 */ @@ -123,6 +144,18 @@ object Set extends ImmutableSetFactory[Set] { override def foreach[U](f: A => U): Unit = { f(elem1); f(elem2); f(elem3) } + override def exists(f: A => Boolean): Boolean = { + f(elem1) || f(elem2) || f(elem3) + } + override def forall(f: A => Boolean): Boolean = { + f(elem1) && f(elem2) && f(elem3) + } + override def find(f: A => Boolean): Option[A] = { + if (f(elem1)) Some(elem1) + else if (f(elem2)) Some(elem2) + else if (f(elem3)) Some(elem3) + else None + } } /** An optimized representation for immutable sets of size 4 */ @@ -145,6 +178,19 @@ object Set extends ImmutableSetFactory[Set] { override def foreach[U](f: A => U): Unit = { f(elem1); f(elem2); f(elem3); f(elem4) } + override def exists(f: A => Boolean): Boolean = { + f(elem1) || f(elem2) || f(elem3) || f(elem4) + } + override def forall(f: A => Boolean): Boolean = { + f(elem1) && f(elem2) && f(elem3) && f(elem4) + } + override def find(f: A => Boolean): Option[A] = { + if (f(elem1)) Some(elem1) + else if (f(elem2)) Some(elem2) + else if (f(elem3)) Some(elem3) + else if (f(elem4)) Some(elem4) + else None + } } } diff --git a/src/library/scala/collection/immutable/Stream.scala b/src/library/scala/collection/immutable/Stream.scala index 49c3b4c3cd..60de147477 100644 --- a/src/library/scala/collection/immutable/Stream.scala +++ b/src/library/scala/collection/immutable/Stream.scala @@ -1108,11 +1108,15 @@ object Stream extends SeqFactory[Stream] { override def isEmpty = false override def head = hd @volatile private[this] var tlVal: Stream[A] = _ - def tailDefined: Boolean = tlVal ne null + @volatile private[this] var tlGen = tl _ + def tailDefined: Boolean = tlGen eq null override def tail: Stream[A] = { if (!tailDefined) synchronized { - if (!tailDefined) tlVal = tl + if (!tailDefined) { + tlVal = tlGen() + tlGen = null + } } tlVal diff --git a/src/library/scala/collection/immutable/StringLike.scala b/src/library/scala/collection/immutable/StringLike.scala index 43d46cf4d0..8e1d950d00 100644 --- a/src/library/scala/collection/immutable/StringLike.scala +++ b/src/library/scala/collection/immutable/StringLike.scala @@ -131,6 +131,7 @@ self => * end characters, i.e. apply `.stripLineEnd` to all lines * returned by `linesWithSeparators`. */ + @deprecated("Use `lines` instead.","2.11.0") def linesIterator: Iterator[String] = linesWithSeparators map (line => new WrappedString(line).stripLineEnd) diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index d3c5a6b019..a55432fd71 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -14,9 +14,45 @@ import scala.annotation.implicitNotFound import scala.util.Try /** - * An `ExecutionContext` is an abstraction over an entity that can execute program logic. + * An `ExecutionContext` can execute program logic, typically but not + * necessarily on a thread pool. + * + * APIs such as `Future.onComplete` require you to provide a callback + * and an implicit `ExecutionContext`. The implicit `ExecutionContext` + * will be used to execute the callback. + * + * It is possible to simply import + * `scala.concurrent.ExecutionContext.Implicits.global` to obtain an + * implicit `ExecutionContext`. This global context is a reasonable + * default thread pool. + * + * However, application developers should carefully consider where they + * want to set policy; ideally, one place per application (or per + * logically-related section of code) will make a decision about + * which `ExecutionContext` to use. That is, you might want to avoid + * hardcoding `scala.concurrent.ExecutionContext.Implicits.global` all + * over the place in your code. + * One approach is to add `(implicit ec: ExecutionContext)` + * to methods which need an `ExecutionContext`. Then import a specific + * context in one place for the entire application or module, + * passing it implicitly to individual methods. + * + * A custom `ExecutionContext` may be appropriate to execute code + * which blocks on IO or performs long-running computations. + * `ExecutionContext.fromExecutorService` and `ExecutionContext.fromExecutor` + * are good ways to create a custom `ExecutionContext`. + * + * The intent of `ExecutionContext` is to lexically scope code execution. + * That is, each method, class, file, package, or application determines + * how to run its own code. This avoids issues such as running + * application callbacks on a thread pool belonging to a networking library. + * The size of a networking library's thread pool can be safely configured, + * knowing that only that library's network operations will be affected. + * Application callback execution can be configured separately. */ -@implicitNotFound("Cannot find an implicit ExecutionContext, either import scala.concurrent.ExecutionContext.Implicits.global or use a custom one") +@implicitNotFound("""Cannot find an implicit ExecutionContext. You might pass +an (implicit ec: ExecutionContext) parameter to your method +or import scala.concurrent.ExecutionContext.Implicits.global.""") trait ExecutionContext { /** Runs a block of code on this execution context. diff --git a/src/library/scala/util/matching/Regex.scala b/src/library/scala/util/matching/Regex.scala index 86132bb876..b70426a145 100644 --- a/src/library/scala/util/matching/Regex.scala +++ b/src/library/scala/util/matching/Regex.scala @@ -67,7 +67,21 @@ import java.util.regex.{ Pattern, Matcher } * Regex, such as `findFirstIn` or `findAllIn`, or using it as an extractor in a * pattern match. * - * Note, however, that when Regex is used as an extractor in a pattern match, it + * Note that, when calling `findAllIn`, the resulting [[scala.util.matching.Regex.MatchIterator]] + * needs to be initialized (by calling `hasNext` or `next()`, or causing these to be + * called) before information about a match can be retrieved: + * + * {{{ + * val msg = "I love Scala" + * + * // val start = " ".r.findAllIn(msg).start // throws an IllegalStateException + * + * val matches = " ".r.findAllIn(msg) + * matches.hasNext // initializes the matcher + * val start = matches.start + * }}} + * + * When Regex is used as an extractor in a pattern match, note that it * only succeeds if the whole text can be matched. For this reason, one usually * calls a method to find the matching substrings, and then use it as an extractor * to break match into subgroups. @@ -267,6 +281,10 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends * that can be queried for data such as the text that precedes the * match, subgroups, etc. * + * Attempting to retrieve information about a match before initializing + * the iterator can result in [[java.lang.IllegalStateException]]s. See + * [[scala.util.matching.Regex.MatchIterator]] for details. + * * @param source The text to match against. * @return A [[scala.util.matching.Regex.MatchIterator]] of all matches. * @example {{{for (words <- """\w+""".r findAllIn "A simple example.") yield words}}} @@ -476,15 +494,7 @@ trait UnanchoredRegex extends Regex { } /** This object defines inner classes that describe - * regex matches and helper objects. The class hierarchy - * is as follows: - * - * {{{ - * MatchData - * / \ - * MatchIterator Match - * }}} - * + * regex matches and helper objects. */ object Regex { @@ -634,7 +644,14 @@ object Regex { def unapplySeq(m: Match): Option[Seq[String]] = if (m.groupCount > 0) Some(1 to m.groupCount map m.group) else None } - /** A class to step through a sequence of regex matches + /** A class to step through a sequence of regex matches. + * + * All methods inherited from [[scala.util.matching.Regex.MatchData]] will throw + * an [[java.lang.IllegalStateException]] until the matcher is initialized by + * calling `hasNext` or `next()` or causing these methods to be called, such as + * by invoking `toString` or iterating through the iterator's elements. + * + * @see [[java.util.regex.Matcher]] */ class MatchIterator(val source: CharSequence, val regex: Regex, val groupNames: Seq[String]) extends AbstractIterator[String] with Iterator[String] with MatchData { self => diff --git a/src/reflect/scala/reflect/internal/AnnotationInfos.scala b/src/reflect/scala/reflect/internal/AnnotationInfos.scala index 4fde57ed02..d634034fe9 100644 --- a/src/reflect/scala/reflect/internal/AnnotationInfos.scala +++ b/src/reflect/scala/reflect/internal/AnnotationInfos.scala @@ -48,7 +48,7 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => /** Tests for, get, or remove an annotation */ def hasAnnotation(cls: Symbol): Boolean = //OPT inlined from exists to save on #closures; was: annotations exists (_ matches cls) - dropOtherAnnotations(annotations, cls).nonEmpty + dropOtherAnnotations(annotations, cls) ne Nil def getAnnotation(cls: Symbol): Option[AnnotationInfo] = //OPT inlined from exists to save on #closures; was: annotations find (_ matches cls) diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index f8a6cae5f6..b8cd1e86d3 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -3404,8 +3404,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => * @return the new list of info-adjusted symbols */ def deriveSymbols(syms: List[Symbol], symFn: Symbol => Symbol): List[Symbol] = { - val syms1 = syms map symFn - syms1 map (_ substInfo (syms, syms1)) + val syms1 = mapList(syms)(symFn) + mapList(syms1)(_ substInfo (syms, syms1)) } /** Derives a new Type by first deriving new symbols as in deriveSymbols, @@ -3445,9 +3445,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => * @return the newly created, info-adjusted symbols */ def cloneSymbolsAndModify(syms: List[Symbol], infoFn: Type => Type): List[Symbol] = - cloneSymbols(syms) map (_ modifyInfo infoFn) + mapList(cloneSymbols(syms))(_ modifyInfo infoFn) def cloneSymbolsAtOwnerAndModify(syms: List[Symbol], owner: Symbol, infoFn: Type => Type): List[Symbol] = - cloneSymbolsAtOwner(syms, owner) map (_ modifyInfo infoFn) + mapList(cloneSymbolsAtOwner(syms, owner))(_ modifyInfo infoFn) /** Functions which perform the standard clone/substituting on the given symbols and type, * then call the creator function with the new symbols and type as arguments. diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index e602a12175..29fdba2781 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -49,10 +49,10 @@ abstract class TreeGen extends macros.TreeBuilder { mkMethodCall(Select(receiver, method), targs, args) def mkMethodCall(target: Tree, targs: List[Type], args: List[Tree]): Tree = - Apply(mkTypeApply(target, targs map TypeTree), args) + Apply(mkTypeApply(target, mapList(targs)(TypeTree)), args) def mkNullaryCall(method: Symbol, targs: List[Type]): Tree = - mkTypeApply(mkAttributedRef(method), targs map TypeTree) + mkTypeApply(mkAttributedRef(method), mapList(targs)(TypeTree)) /** Builds a reference to value whose type is given stable prefix. * The type must be suitable for this. For example, it @@ -281,7 +281,7 @@ abstract class TreeGen extends macros.TreeBuilder { case tree :: Nil if flattenUnary => tree case _ => - Apply(scalaDot(TupleClass(elems.length).companionModule.name), elems) + Apply(scalaDot(TupleClass(elems.length).name.toTermName), elems) } def mkTupleType(elems: List[Tree], flattenUnary: Boolean = true): Tree = elems match { diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index 497a7c91b1..7bab15b0f4 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -284,17 +284,17 @@ abstract class TreeInfo { /** Is tree a self constructor call this(...)? I.e. a call to a constructor of the * same object? */ - def isSelfConstrCall(tree: Tree): Boolean = tree match { - case Applied(Ident(nme.CONSTRUCTOR), _, _) => true - case Applied(Select(This(_), nme.CONSTRUCTOR), _, _) => true - case _ => false + def isSelfConstrCall(tree: Tree): Boolean = dissectApplied(tree).core match { + case Ident(nme.CONSTRUCTOR) => true + case Select(This(_), nme.CONSTRUCTOR) => true + case _ => false } /** Is tree a super constructor call? */ - def isSuperConstrCall(tree: Tree): Boolean = tree match { - case Applied(Select(Super(_, _), nme.CONSTRUCTOR), _, _) => true - case _ => false + def isSuperConstrCall(tree: Tree): Boolean = dissectApplied(tree).core match { + case Select(Super(_, _), nme.CONSTRUCTOR) => true + case _ => false } /** @@ -848,8 +848,10 @@ abstract class TreeInfo { case _ => false }) - def isMacroApplication(tree: Tree): Boolean = - !tree.isDef && tree.symbol != null && tree.symbol.isTermMacro && !tree.symbol.isErroneous + def isMacroApplication(tree: Tree): Boolean = !tree.isDef && { + val sym = tree.symbol + sym != null && sym.isTermMacro && !sym.isErroneous + } def isMacroApplicationOrBlock(tree: Tree): Boolean = tree match { case Block(_, expr) => isMacroApplicationOrBlock(expr) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 9ddaea4c62..53c528a2bb 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -1977,7 +1977,7 @@ trait Types def pendingVolatiles = _pendingVolatiles class ArgsTypeRef(pre0: Type, sym0: Symbol, args0: List[Type]) extends TypeRef(pre0, sym0, args0) { - require(args0.nonEmpty, this) + require(args0 ne Nil, this) /** No unapplied type params size it has (should have) equally as many args. */ override def isHigherKinded = false @@ -1988,12 +1988,39 @@ trait Types // too little information is known to determine its kind, and // it later turns out not to have kind *. See SI-4070. Only // logging it for now. - if (sym.typeParams.size != args.size) + val tparams = sym.typeParams + if (tparams.size != args.size) devWarning(s"$this.transform($tp), but tparams.isEmpty and args=$args") - - val GenPolyType(tparams, result) = asSeenFromOwner(tp) - assert((tparams eq Nil) || tparams == sym.typeParams, (tparams, sym.typeParams)) - result.instantiateTypeParams(sym.typeParams, args) + def asSeenFromInstantiated(tp: Type) = + asSeenFromOwner(tp).instantiateTypeParams(tparams, args) + // If we're called with a poly type, and we were to run the `asSeenFrom`, over the entire + // type, we can end up with new symbols for the type parameters (clones from TypeMap). + // The subsequent substitution of type arguments would fail. This problem showed up during + // the fix for SI-8046, however the solution taken there wasn't quite right, and led to + // SI-8170. + // + // Now, we detect the PolyType before both the ASF *and* the substitution, and just operate + // on the result type. + // + // TODO: Revisit this and explore the questions raised: + // + // AM: I like this better than the old code, but is there any way the tparams would need the ASF treatment as well? + // JZ: I think its largely irrelevant, as they are no longer referred to in the result type. + // In fact, you can get away with returning a type of kind * here and the sky doesn't fall: + // `case PolyType(`tparams`, result) => asSeenFromInstantiated(result)` + // But I thought it was better to retain the kind. + // AM: I've been experimenting with apply-type-args-then-ASF, but running into cycles. + // In general, it seems iffy the tparams can never occur in the result + // then we might as well represent the type as a no-arg typeref. + // AM: I've also been trying to track down uses of transform (pretty generic name for something that + // does not seem that widely applicable). + // It's kind of a helper for computing baseType (since it tries to propagate our type args to some + // other type, which has to be related to this type for that to make sense). + // + tp match { + case PolyType(`tparams`, result) => PolyType(tparams, asSeenFromInstantiated(result)) + case _ => asSeenFromInstantiated(tp) + } } // note: does not go through typeRef. There's no need to because @@ -2035,7 +2062,7 @@ trait Types // to a java or scala symbol, but it does matter whether it occurs in java or scala code. // TypeRefs w/o type params that occur in java signatures/code are considered raw types, and are // represented as existential types. - override def isHigherKinded = typeParams.nonEmpty + override def isHigherKinded = (typeParams ne Nil) override def typeParams = if (isDefinitionsInitialized) sym.typeParams else sym.unsafeTypeParams private def isRaw = !phase.erasedTypes && isRawIfWithoutArgs(sym) @@ -2221,7 +2248,7 @@ trait Types //OPT specialize hashCode override final def computeHashCode = { import scala.util.hashing.MurmurHash3._ - val hasArgs = args.nonEmpty + val hasArgs = args ne Nil var h = productSeed h = mix(h, pre.hashCode) h = mix(h, sym.hashCode) @@ -2412,7 +2439,7 @@ trait Types object TypeRef extends TypeRefExtractor { def apply(pre: Type, sym: Symbol, args: List[Type]): Type = unique({ - if (args.nonEmpty) { + if (args ne Nil) { if (sym.isAliasType) new AliasArgsTypeRef(pre, sym, args) else if (sym.isAbstractType) new AbstractArgsTypeRef(pre, sym, args) else new ClassArgsTypeRef(pre, sym, args) @@ -2485,7 +2512,7 @@ trait Types true } - def isImplicit = params.nonEmpty && params.head.isImplicit + def isImplicit = (params ne Nil) && params.head.isImplicit def isJava = false // can we do something like for implicits? I.e. do Java methods without parameters need to be recognized? //assert(paramTypes forall (pt => !pt.typeSymbol.isImplClass))//DEBUG @@ -2493,7 +2520,7 @@ trait Types override def paramss: List[List[Symbol]] = params :: resultType.paramss - override def paramTypes = params map (_.tpe) + override def paramTypes = mapList(params)(symTpe) // OPT use mapList rather than .map override def boundSyms = resultType.boundSyms ++ params @@ -4120,7 +4147,7 @@ trait Types && (variance.isCovariant || isSubType(t2, t1, depth)) ) - corresponds3(tps1, tps2, tparams map (_.variance))(isSubArg) + corresponds3(tps1, tps2, mapList(tparams)(_.variance))(isSubArg) } def specializesSym(tp: Type, sym: Symbol, depth: Depth): Boolean = { @@ -4304,7 +4331,7 @@ trait Types } def instantiatedBounds(pre: Type, owner: Symbol, tparams: List[Symbol], targs: List[Type]): List[TypeBounds] = - tparams map (_.info.asSeenFrom(pre, owner).instantiateTypeParams(tparams, targs).bounds) + mapList(tparams)(_.info.asSeenFrom(pre, owner).instantiateTypeParams(tparams, targs).bounds) def elimAnonymousClass(t: Type) = t match { case TypeRef(pre, clazz, Nil) if clazz.isAnonymousClass => @@ -4553,7 +4580,10 @@ trait Types private[scala] val typeIsExistentiallyBound = (tp: Type) => tp.typeSymbol.isExistentiallyBound private[scala] val typeIsErroneous = (tp: Type) => tp.isErroneous private[scala] val symTypeIsError = (sym: Symbol) => sym.tpe.isError - private[scala] val typeHasAnnotations = (tp: Type) => tp.annotations.nonEmpty + private[scala] val treeTpe = (t: Tree) => t.tpe + private[scala] val symTpe = (sym: Symbol) => sym.tpe + private[scala] val symInfo = (sym: Symbol) => sym.info + private[scala] val typeHasAnnotations = (tp: Type) => tp.annotations ne Nil private[scala] val boundsContainType = (bounds: TypeBounds, tp: Type) => bounds containsType tp private[scala] val typeListIsEmpty = (ts: List[Type]) => ts.isEmpty private[scala] val typeIsSubTypeOfSerializable = (tp: Type) => tp <:< SerializableTpe diff --git a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala index 09f4389b82..f427813c01 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala @@ -861,7 +861,7 @@ private[internal] trait TypeMaps { class InstantiateDependentMap(params: List[Symbol], actuals0: List[Type]) extends TypeMap with KeepOnlyTypeConstraints { private val actuals = actuals0.toIndexedSeq private val existentials = new Array[Symbol](actuals.size) - def existentialsNeeded: List[Symbol] = existentials.filter(_ ne null).toList + def existentialsNeeded: List[Symbol] = existentials.iterator.filter(_ ne null).toList private object StableArg { def unapply(param: Symbol) = Arg unapply param map actuals filter (tp => diff --git a/src/reflect/scala/reflect/internal/util/Collections.scala b/src/reflect/scala/reflect/internal/util/Collections.scala index 7cc2952c96..d128521be8 100644 --- a/src/reflect/scala/reflect/internal/util/Collections.scala +++ b/src/reflect/scala/reflect/internal/util/Collections.scala @@ -47,6 +47,30 @@ trait Collections { final def mforeach[A](xss: List[List[A]])(f: A => Unit) = xss foreach (_ foreach f) final def mforeach[A](xss: Traversable[Traversable[A]])(f: A => Unit) = xss foreach (_ foreach f) + /** A version of List#map, specialized for List, and optimized to avoid allocation if `as` is empty */ + final def mapList[A, B](as: List[A])(f: A => B): List[B] = if (as eq Nil) Nil else { + val head = new ::[B](f(as.head), Nil) + var tail: ::[B] = head + var rest = as.tail + while (rest ne Nil) { + val next = new ::(f(rest.head), Nil) + tail.tl = next + tail = next + rest = rest.tail + } + head + } + + final def collectFirst[A, B](as: List[A])(pf: PartialFunction[A, B]): Option[B] = { + @tailrec + def loop(rest: List[A]): Option[B] = rest match { + case Nil => None + case a :: as if pf.isDefinedAt(a) => Some(pf(a)) + case a :: as => loop(as) + } + loop(as) + } + final def map2[A, B, C](xs1: List[A], xs2: List[B])(f: (A, B) => C): List[C] = { val lb = new ListBuffer[C] var ys1 = xs1 @@ -99,15 +123,19 @@ trait Collections { else f(xs1.head, xs2.head, xs3.head) :: map3(xs1.tail, xs2.tail, xs3.tail)(f) } final def flatMap2[A, B, C](xs1: List[A], xs2: List[B])(f: (A, B) => List[C]): List[C] = { - val lb = new ListBuffer[C] + var lb: ListBuffer[C] = null var ys1 = xs1 var ys2 = xs2 while (!ys1.isEmpty && !ys2.isEmpty) { - lb ++= f(ys1.head, ys2.head) + val cs = f(ys1.head, ys2.head) + if (cs ne Nil) { + if (lb eq null) lb = new ListBuffer[C] + lb ++= cs + } ys1 = ys1.tail ys2 = ys2.tail } - lb.toList + if (lb eq null) Nil else lb.result } final def flatCollect[A, B](elems: List[A])(pf: PartialFunction[A, Traversable[B]]): List[B] = { diff --git a/src/reflect/scala/reflect/macros/Attachments.scala b/src/reflect/scala/reflect/macros/Attachments.scala index 039e75fbee..5ccdc15a03 100644 --- a/src/reflect/scala/reflect/macros/Attachments.scala +++ b/src/reflect/scala/reflect/macros/Attachments.scala @@ -43,7 +43,7 @@ abstract class Attachments { self => /** Check underlying payload contains an instance of type `T`. */ def contains[T: ClassTag]: Boolean = - all exists matchesTag[T] + !isEmpty && (all exists matchesTag[T]) /** Creates a copy of this attachment with the payload slot of T added/updated with the provided value. * Replaces an existing payload of the same type, if exists. @@ -57,6 +57,8 @@ abstract class Attachments { self => if (newAll.isEmpty) pos.asInstanceOf[Attachments { type Pos = self.Pos }] else new NonemptyAttachments[Pos](this.pos, newAll) } + + def isEmpty: Boolean = true } // SI-7018: This used to be an inner class of `Attachments`, but that led to a memory leak in the @@ -64,4 +66,5 @@ abstract class Attachments { self => private final class NonemptyAttachments[P >: Null](override val pos: P, override val all: Set[Any]) extends Attachments { type Pos = P def withPos(newPos: Pos) = new NonemptyAttachments(newPos, all) + override def isEmpty: Boolean = false } diff --git a/src/scaladoc/scala/tools/nsc/doc/Settings.scala b/src/scaladoc/scala/tools/nsc/doc/Settings.scala index e5dbaa3fd5..5ea1443a19 100644 --- a/src/scaladoc/scala/tools/nsc/doc/Settings.scala +++ b/src/scaladoc/scala/tools/nsc/doc/Settings.scala @@ -122,6 +122,11 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_)) "Hide the members inherited by the given comma separated, fully qualified implicit conversions. Add dot (.) to include default conversions." ) + val docAuthor = BooleanSetting ( + "-author", + "Include authors." + ) + val docDiagrams = BooleanSetting ( "-diagrams", "Create inheritance diagrams for classes, traits and packages." @@ -207,7 +212,7 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_)) // For improved help output. def scaladocSpecific = Set[Settings#Setting]( docformat, doctitle, docfooter, docversion, docUncompilable, docsourceurl, docgenerator, docRootContent, useStupidTypes, - docDiagrams, docDiagramsDebug, docDiagramsDotPath, + docAuthor, docDiagrams, docDiagramsDebug, docDiagramsDotPath, docDiagramsDotTimeout, docDiagramsDotRestart, docImplicits, docImplicitsDebug, docImplicitsShowAll, docImplicitsHide, docDiagramsMaxNormalClasses, docDiagramsMaxImplicitClasses, diff --git a/src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala index 119d4e0143..26ee005d3e 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala @@ -351,6 +351,14 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp if (mbr.comment.isEmpty) NodeSeq.Empty else <div class="comment cmt">{ commentToHtml(mbr.comment) }</div> + val authorComment = + if (! s.docAuthor || mbr.comment.isEmpty || + mbr.comment.isDefined && mbr.comment.get.authors.isEmpty) NodeSeq.Empty + else <div class="comment cmt"> + {if (mbr.comment.get.authors.size > 1) <h6>Authors:</h6> else <h6>Author:</h6>} + { mbr.comment.get.authors map bodyToHtml} + </div> + val paramComments = { val prs: List[ParameterEntity] = mbr match { case cls: Class => cls.typeParams ::: cls.valueParams.flatten @@ -681,7 +689,7 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp val typeHierarchy = createDiagram(_.inheritanceDiagram, "Type Hierarchy", "inheritance-diagram") val contentHierarchy = createDiagram(_.contentDiagram, "Content Hierarchy", "content-diagram") - memberComment ++ paramComments ++ attributesBlock ++ linearization ++ subclasses ++ typeHierarchy ++ contentHierarchy + memberComment ++ authorComment ++ paramComments ++ attributesBlock ++ linearization ++ subclasses ++ typeHierarchy ++ contentHierarchy } def boundsToHtml(hi: Option[TypeEntity], lo: Option[TypeEntity], hasLinks: Boolean): NodeSeq = { diff --git a/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala b/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala index 8f217e087c..ef84ac42ba 100644 --- a/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala +++ b/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala @@ -10,6 +10,7 @@ import diagram._ import scala.collection._ import scala.util.matching.Regex +import scala.reflect.macros.internal.macroImpl import symtab.Flags import io._ @@ -80,7 +81,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { def inTemplate: TemplateImpl = inTpl def toRoot: List[EntityImpl] = this :: inTpl.toRoot def qualifiedName = name - def annotations = sym.annotations.map(makeAnnotation) + def annotations = sym.annotations.filterNot(_.tpe =:= typeOf[macroImpl]).map(makeAnnotation) def inPackageObject: Boolean = sym.owner.isModuleClass && sym.owner.sourceModule.isPackageObject def isType = sym.name.isTypeName } @@ -145,6 +146,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { * any abstract terms, otherwise it would fail compilation. So we reset the DEFERRED flag. */ if (!sym.isTrait && (sym hasFlag Flags.DEFERRED) && (!isImplicitlyInherited)) fgs += Paragraph(Text("abstract")) if (!sym.isModule && (sym hasFlag Flags.FINAL)) fgs += Paragraph(Text("final")) + if (sym.isMacro) fgs += Paragraph(Text("macro")) fgs.toList } def deprecation = diff --git a/test/files/jvm/t6941/Analyzed_1.scala b/test/files/jvm/t6941/Analyzed_1.scala index 549abd5e64..b6951f71ee 100644 --- a/test/files/jvm/t6941/Analyzed_1.scala +++ b/test/files/jvm/t6941/Analyzed_1.scala @@ -6,6 +6,6 @@ class SameBytecode { } def b(xs: List[Int]) = xs match { - case xs: ::[Int] => xs.hd$1 + case xs: ::[Int] => xs.head } }
\ No newline at end of file diff --git a/test/files/neg/missing-param-type-tuple.check b/test/files/neg/missing-param-type-tuple.check index bc46ba1023..3a4258ff8c 100644 --- a/test/files/neg/missing-param-type-tuple.check +++ b/test/files/neg/missing-param-type-tuple.check @@ -1,6 +1,6 @@ missing-param-type-tuple.scala:3: error: missing parameter type Note: The expected type requires a one-argument function accepting a 2-Tuple. - Consider a pattern matching anoynmous function, `{ case (a, b) => ... }` + Consider a pattern matching anonymous function, `{ case (a, b) => ... }` val x: ((Int, Int)) => Int = (a, b) => 0 ^ missing-param-type-tuple.scala:3: error: missing parameter type @@ -8,7 +8,7 @@ missing-param-type-tuple.scala:3: error: missing parameter type ^ missing-param-type-tuple.scala:5: error: missing parameter type Note: The expected type requires a one-argument function accepting a 3-Tuple. - Consider a pattern matching anoynmous function, `{ case (param1, ..., param3) => ... }` + Consider a pattern matching anonymous function, `{ case (param1, ..., param3) => ... }` val y: ((Int, Int, Int)) => Int = (a, b, !!) => 0 ^ missing-param-type-tuple.scala:5: error: missing parameter type @@ -19,7 +19,7 @@ missing-param-type-tuple.scala:5: error: missing parameter type ^ missing-param-type-tuple.scala:7: error: missing parameter type Note: The expected type requires a one-argument function accepting a 3-Tuple. - Consider a pattern matching anoynmous function, `{ case (param1, ..., param3) => ... }` + Consider a pattern matching anonymous function, `{ case (param1, ..., param3) => ... }` val z: ((Int, Int, Int)) => Int = (a, NotAVariablePatternName, c) => 0 ^ missing-param-type-tuple.scala:7: error: missing parameter type diff --git a/test/files/neg/t8228.check b/test/files/neg/t8228.check new file mode 100644 index 0000000000..02eff4b1b7 --- /dev/null +++ b/test/files/neg/t8228.check @@ -0,0 +1,4 @@ +t8228.scala:4: error: recursive value foo needs type + val foo = foo(null) + ^ +one error found diff --git a/test/files/neg/t8228.scala b/test/files/neg/t8228.scala new file mode 100644 index 0000000000..19d71aeab4 --- /dev/null +++ b/test/files/neg/t8228.scala @@ -0,0 +1,7 @@ +object X { + def bar = { + def foo(x: Any) = "" + val foo = foo(null) + foo(null) // cycle in isApplicableBasedOnArity + } +} diff --git a/test/files/pos/t7322.scala b/test/files/pos/t7322.scala new file mode 100644 index 0000000000..006bf89e9f --- /dev/null +++ b/test/files/pos/t7322.scala @@ -0,0 +1,11 @@ + +package object t7322 { + implicit class X(sc: StringContext) { + def x_?(args: Any*) = "hi there" + } +} +package t7322 { + trait Y { + x_?"junk" // assume that if it compiles, it works + } +} diff --git a/test/files/pos/t8170.scala b/test/files/pos/t8170.scala new file mode 100644 index 0000000000..b65f4b8572 --- /dev/null +++ b/test/files/pos/t8170.scala @@ -0,0 +1,27 @@ +object O { + trait X + trait B extends A { + override type T[F1 <: X] = F1 + } + trait A { + type T[F <: X] + } +} + +object Test { + import O._ + val a: B = ??? + val b: a.T[X] = ??? + b.ensuring(x => true) // trigger an implicit search +} + + +/* +this = {AliasArgsTypeRef@3004}"Test#7680.a#14899.T#14823[O#7702.X#7793]" + sym = type T#14823 + info = namer: [F#14824 <: O#7703.X#7793]F#14824 +result = {AbstractNoArgsTypeRef@3237}"F#24451" +tp = {PolyType@3235}"[F#14824 <: O#7703.X#7793]F#14824" +tparams = + (0) = {AbstractTypeSymbol@3247}"type F#24451" +*/
\ No newline at end of file diff --git a/test/files/pos/t8170b.scala b/test/files/pos/t8170b.scala new file mode 100644 index 0000000000..53036f6c8a --- /dev/null +++ b/test/files/pos/t8170b.scala @@ -0,0 +1,25 @@ +import language._ + +object ScalaZeee { + trait HFold[M[_], U] { + type Apply[E, A <: U] <: U + } + trait GenericCons[M[_], H, +T <: GenericList[M]] extends GenericList[M] { + val tail: T + override type Folded[N[X] >: M[X], U, F <: HFold[N, U]] = F#Apply[H, tail.Folded[N, U, F]] + } + val KNil: GenericList[Nothing] = ??? + sealed trait GenericList[+M[_]] { + type Folded[N[X] >: M[X], U, F <: HFold[N, U]] <: U + } +} + +object TypelevelUsage { + import ScalaZeee._ + type T = GenericCons[Some, String, KNil.type] + val klist1: T = ??? + type T2 = klist1.Folded[Option, Int, HFold[Option, Int]] + val count2: T2 = ??? + + count2.ensuring(x => true).toChar // trigger an implicit search +} diff --git a/test/files/run/global-showdef.scala b/test/files/run/global-showdef.scala index c3ace590ed..1d4891fd1f 100644 --- a/test/files/run/global-showdef.scala +++ b/test/files/run/global-showdef.scala @@ -54,7 +54,7 @@ object Bippy { val run = new compiler.Run() run.compileSources(List(src)) } - output.linesIterator.toList + output.lines.toList } def showClass(name: String) = lines("-Yshow:typer", "-Xshow-class", name) def showObject(name: String) = lines("-Yshow:typer", "-Xshow-object", name) diff --git a/test/files/run/t7700.check b/test/files/run/t7700.check new file mode 100644 index 0000000000..ca8e686984 --- /dev/null +++ b/test/files/run/t7700.check @@ -0,0 +1,2 @@ +public abstract java.lang.Object C.bar(java.lang.Object) +public abstract java.lang.Object C.foo(java.lang.Object) diff --git a/test/files/run/t7700.scala b/test/files/run/t7700.scala new file mode 100644 index 0000000000..76d16b808c --- /dev/null +++ b/test/files/run/t7700.scala @@ -0,0 +1,17 @@ +import scala.annotation._ + +trait C[@specialized U] { + @unspecialized + def foo(u: U): U + @unspecialized + def bar[A](u: U) = u +} + +object Test extends App { + val declared = classOf[C[_]].getDeclaredMethods.sortBy(_.getName) + println(declared.mkString("\n")) + object CInt extends C[Int] { def foo(i: Int) = i } + object CAny extends C[Any] { def foo(a: Any) = a } + assert(CInt.foo(1) == 1) + assert(CAny.foo("") == "") +} diff --git a/test/files/run/t8233-bcode.flags b/test/files/run/t8233-bcode.flags new file mode 100644 index 0000000000..c30091d3de --- /dev/null +++ b/test/files/run/t8233-bcode.flags @@ -0,0 +1 @@ +-Ybackend:GenBCode diff --git a/test/files/run/t8233-bcode.scala b/test/files/run/t8233-bcode.scala new file mode 100644 index 0000000000..fae1c2b702 --- /dev/null +++ b/test/files/run/t8233-bcode.scala @@ -0,0 +1,18 @@ +object Test { + def bar(s: String) = s; + val o: Option[Null] = None + def nullReference { + val a: Null = o.get + bar(a) // Was: VerifyError under GenICode + } + + def literal { + val a: Null = null + bar(a) + } + + def main(args: Array[String]) = { + try { nullReference } catch { case _: NoSuchElementException => } + literal + } +} diff --git a/test/files/run/t8233.scala b/test/files/run/t8233.scala new file mode 100644 index 0000000000..fae1c2b702 --- /dev/null +++ b/test/files/run/t8233.scala @@ -0,0 +1,18 @@ +object Test { + def bar(s: String) = s; + val o: Option[Null] = None + def nullReference { + val a: Null = o.get + bar(a) // Was: VerifyError under GenICode + } + + def literal { + val a: Null = null + bar(a) + } + + def main(args: Array[String]) = { + try { nullReference } catch { case _: NoSuchElementException => } + literal + } +} diff --git a/test/scaladoc/resources/SI-4014_0.scala b/test/scaladoc/resources/SI-4014_0.scala new file mode 100644 index 0000000000..c398fcc1e0 --- /dev/null +++ b/test/scaladoc/resources/SI-4014_0.scala @@ -0,0 +1,4 @@ +/** A template without authors. + * + */ +trait Foo
\ No newline at end of file diff --git a/test/scaladoc/resources/SI-4014_1.scala b/test/scaladoc/resources/SI-4014_1.scala new file mode 100644 index 0000000000..34386b515e --- /dev/null +++ b/test/scaladoc/resources/SI-4014_1.scala @@ -0,0 +1,5 @@ +/** A template with one author. + * + * @author The Only Author + */ +trait Foo
\ No newline at end of file diff --git a/test/scaladoc/resources/SI-4014_2.scala b/test/scaladoc/resources/SI-4014_2.scala new file mode 100644 index 0000000000..514f7a1e4c --- /dev/null +++ b/test/scaladoc/resources/SI-4014_2.scala @@ -0,0 +1,6 @@ +/** A template with more than one author. + * + * @author The First Author + * @author The Second Author + */ +trait Foo
\ No newline at end of file diff --git a/test/scaladoc/run/t7124.check b/test/scaladoc/run/t7124.check new file mode 100644 index 0000000000..96b627a322 --- /dev/null +++ b/test/scaladoc/run/t7124.check @@ -0,0 +1,3 @@ +List() +List(Paragraph(Text(macro))) +Done. diff --git a/test/scaladoc/run/t7124.scala b/test/scaladoc/run/t7124.scala new file mode 100644 index 0000000000..e8272000d2 --- /dev/null +++ b/test/scaladoc/run/t7124.scala @@ -0,0 +1,22 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + + override def code = """ + import scala.language.experimental.macros + class Test { + def print(): Unit = macro ??? + } + """ + + def scaladocSettings = "" + + def testModel(root: Package) = { + import access._ + val p = root._class("Test")._method("print") + + println(p.annotations) // no annotations + println(p.flags) // a 'macro' flag + } +} diff --git a/test/scaladoc/scalacheck/HtmlFactoryTest.scala b/test/scaladoc/scalacheck/HtmlFactoryTest.scala index 03348b81d2..56328ea875 100644 --- a/test/scaladoc/scalacheck/HtmlFactoryTest.scala +++ b/test/scaladoc/scalacheck/HtmlFactoryTest.scala @@ -47,6 +47,7 @@ object Test extends Properties("HtmlFactory") { settings.scaladocQuietRun = true settings.nowarn.value = true settings.classpath.value = getClasspath + settings.docAuthor.value = true val reporter = new scala.tools.nsc.reporters.ConsoleReporter(settings) new DocFactory(reporter, settings) @@ -563,12 +564,13 @@ object Test extends Properties("HtmlFactory") { property("Comment inheritance: Correct explicit inheritance for override") = checkText("explicit-inheritance-override.scala")( (Some("InheritDocDerived"), - """def function[T](arg1: T, arg2: String): Double + """def function[T](arg1: T, arg2: String): Double Starting line Starting line The base comment. And another sentence... The base comment. And another sentence... Ending line + Author: StartAuthor a Scala developer EndAuthor T StartT the type of the first argument EndT arg1 Start1 The T term comment End1 arg2 Start2 The string comment End2 @@ -589,12 +591,13 @@ object Test extends Properties("HtmlFactory") { property("Comment inheritance: Correct explicit inheritance for usecase") = checkText("explicit-inheritance-usecase.scala")( (Some("UseCaseInheritDoc"), - """def function[T](arg1: T, arg2: String): Double + """def function[T](arg1: T, arg2: String): Double [use case] Starting line [use case] Starting line The base comment. And another sentence... The base comment. And another sentence... Ending line + Author: StartAuthor a Scala developer EndAuthor T StartT the type of the first argument EndT arg1 Start1 The T term comment End1 arg2 Start2 The string comment End2 @@ -663,6 +666,45 @@ object Test extends Properties("HtmlFactory") { } } + property("SI-4014: Scaladoc omits @author: no authors") = { + val noAuthors = createTemplates("SI-4014_0.scala")("Foo.html") + + noAuthors match { + case node: scala.xml.Node => { + val s = node.toString + ! s.contains("Author") + } + case _ => false + } + } + + property("SI-4014: Scaladoc omits @author: one author") = { + val oneAuthor = createTemplates("SI-4014_1.scala")("Foo.html") + + oneAuthor match { + case node: scala.xml.Node => { + val s = node.toString + s.contains("<h6>Author:</h6>") + s.contains("<p>The Only Author\n</p>") + } + case _ => false + } + } + + property("SI-4014: Scaladoc omits @author: two authors") = { + val twoAuthors = createTemplates("SI-4014_2.scala")("Foo.html") + + twoAuthors match { + case node: scala.xml.Node => { + val s = node.toString + s.contains("<h6>Authors:</h6>") + s.contains("<p>The First Author\n</p>") + s.contains("<p>The Second Author\n</p>") + } + case _ => false + } + } + { val files = createTemplates("basic.scala") //println(files) |