diff options
Diffstat (limited to 'src')
17 files changed, 519 insertions, 336 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index af874ed28c..b9eb511a9a 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -162,6 +162,11 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { } } + // drop annotations generated by CPS plugin etc, since its annotationchecker rejects T @cps[U] <: Any + // let's assume for now annotations don't affect casts, drop them there, and bring them back using the outer Typed tree + def mkCastPreservingAnnotations(tree: Tree, pt: Type) = + Typed(mkCast(tree, pt.withoutAnnotations.dealias), TypeTree(pt)) + /** Generate a cast for tree Tree representing Array with * elem type elemtp to expected type pt. */ diff --git a/src/compiler/scala/tools/nsc/doc/Settings.scala b/src/compiler/scala/tools/nsc/doc/Settings.scala index 16f3c3776b..02630a99b2 100644 --- a/src/compiler/scala/tools/nsc/doc/Settings.scala +++ b/src/compiler/scala/tools/nsc/doc/Settings.scala @@ -6,7 +6,7 @@ package scala.tools.nsc package doc -import java.net.URI +import java.io.File import scala.language.postfixOps /** An extended version of compiler settings, with additional Scaladoc-specific options. @@ -70,10 +70,10 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_)) "" ) - val docExternalUris = MultiStringSetting ( - "-doc-external-uris", + val docExternalDoc = MultiStringSetting ( + "-doc-external-doc", "external-doc", - "comma-separated list of file://classpath_entry_path#doc_URL URIs for external dependencies" + "comma-separated list of classpath_entry_path#doc_URL pairs describing external dependencies." ) val useStupidTypes = BooleanSetting ( @@ -263,9 +263,15 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_)) map ++ (pkgs map (_ -> url)) } - lazy val extUrlMapping: Map[String, String] = docExternalUris.value map { s => - val uri = new URI(s) - uri.getSchemeSpecificPart -> appendIndex(uri.getFragment) + lazy val extUrlMapping: Map[String, String] = docExternalDoc.value flatMap { s => + val idx = s.indexOf("#") + if (idx > 0) { + val (first, last) = s.splitAt(idx) + Some(new File(first).getAbsolutePath -> appendIndex(last.substring(1))) + } else { + error(s"Illegal -doc-external-doc option; expected a pair with '#' separator, found: '$s'") + None + } } toMap /** diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js b/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js index ff73745972..1323a06c01 100644 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js @@ -339,7 +339,7 @@ function configureTextFilter() { printAlphabet(); var input = $("#textfilter input"); resizeFilterBlock(); - input.bind("keydown", function(event) { + input.bind('keyup', function(event) { if (event.keyCode == 27) { // escape input.attr("value", ""); } diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 36e6a74820..0875c1879a 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -82,7 +82,8 @@ trait ScalaSettings extends AbsScalaSettings val logFreeTerms = BooleanSetting ("-Xlog-free-terms", "Print a message when reification creates a free term.") val logFreeTypes = BooleanSetting ("-Xlog-free-types", "Print a message when reification resorts to generating a free type.") val maxClassfileName = IntSetting ("-Xmax-classfile-name", "Maximum filename length for generated classes", 255, Some((72, 255)), _ => None) - val Xmigration28 = BooleanSetting ("-Xmigration", "Warn about constructs whose behavior may have changed between 2.7 and 2.8.") + val Xmigration28 = BooleanSetting ("-Xmigration", "Warn about constructs whose behavior may have changed between 2.7 and 2.8."). + withDeprecationMessage("This setting is no longer useful and will be removed. Please remove it from your build.") val nouescape = BooleanSetting ("-Xno-uescape", "Disable handling of \\u unicode escapes.") val Xnojline = BooleanSetting ("-Xnojline", "Do not use JLine for editing.") val Xverify = BooleanSetting ("-Xverify", "Verify generic signatures in generated bytecode (asm backend only.)") @@ -170,6 +171,7 @@ trait ScalaSettings extends AbsScalaSettings val etaExpandKeepsStar = BooleanSetting ("-Yeta-expand-keeps-star", "Eta-expand varargs methods to T* rather than Seq[T]. This is a temporary option to ease transition.") val Yinvalidate = StringSetting ("-Yinvalidate", "classpath-entry", "Invalidate classpath entry before run", "") val noSelfCheck = BooleanSetting ("-Yno-self-type-checks", "Suppress check for self-type conformance among inherited members.") + val companionsInPkgObjs = BooleanSetting("-Ycompanions-in-pkg-objs", "Allow companion objects and case classes in package objects. See issue SI-5954.") val YvirtClasses = false // too embryonic to even expose as a -Y //BooleanSetting ("-Yvirtual-classes", "Support virtual classes") val exposeEmptyPackage = BooleanSetting("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly() diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index f4e40a216e..afe853e06e 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -461,13 +461,14 @@ abstract class UnCurry extends InfoTransform val fn1 = withInPattern(false)(transform(fn)) val args1 = transformTrees(fn.symbol.name match { case nme.unapply => args - case nme.unapplySeq => transformArgs(tree.pos, fn.symbol, args, analyzer.unapplyTypeList(fn.symbol, fn.tpe, args.length)) + case nme.unapplySeq => transformArgs(tree.pos, fn.symbol, args, analyzer.unapplyTypeList(fn.pos, fn.symbol, fn.tpe, args.length)) case _ => sys.error("internal error: UnApply node has wrong symbol") }) treeCopy.UnApply(tree, fn1, args1) case Apply(fn, args) => - withNeedLift(true) { + val needLift = needTryLift || !fn.symbol.isLabel // SI-6749, no need to lift in args to label jumps. + withNeedLift(needLift) { val formals = fn.tpe.paramTypes treeCopy.Apply(tree, transform(fn), transformTrees(transformArgs(tree.pos, fn.symbol, args, formals))) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index e435717839..bcf9910c5a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -952,7 +952,7 @@ trait Implicits { infoMap get sym match { case Some(infos1) => if (infos1.nonEmpty && !(pre =:= infos1.head.pre.prefix)) { - println("amb prefix: "+pre+"#"+sym+" "+infos1.head.pre.prefix+"#"+sym) + log(s"Ignoring implicit members of $pre#$sym as it is also visible via another prefix: ${infos1.head.pre.prefix}") infoMap(sym) = List() // ambiguous prefix - ignore implicit members } case None => diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 88f0ccca98..4177b7cdfa 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -90,7 +90,7 @@ trait Infer extends Checkable { * n > 1 and unapply’s result type is Option[(T1, ..., Tn)], for some types T1, ..., Tn. * the argument patterns p1, ..., pn are typed in turn with expected types T1, ..., Tn */ - def extractorFormalTypes(resTp: Type, nbSubPats: Int, unappSym: Symbol): (List[Type], List[Type]) = { + def extractorFormalTypes(pos: Position, resTp: Type, nbSubPats: Int, unappSym: Symbol): (List[Type], List[Type]) = { val isUnapplySeq = unappSym.name == nme.unapplySeq val booleanExtractor = resTp.typeSymbolDirect == BooleanClass @@ -104,11 +104,18 @@ trait Infer extends Checkable { if (nbSubPats == 0 && booleanExtractor && !isUnapplySeq) Nil else resTp.baseType(OptionClass).typeArgs match { case optionTArg :: Nil => - if (nbSubPats == 1) + def productArgs = getProductArgs(optionTArg) + if (nbSubPats == 1) { if (isUnapplySeq) List(seqToRepeatedChecked(optionTArg)) - else List(optionTArg) + else { + val productArity = productArgs.size + if (productArity > 1 && settings.lint.value) + global.currentUnit.warning(pos, s"extractor pattern binds a single value to a Product${productArity} of type ${optionTArg}") + List(optionTArg) + } + } // TODO: update spec to reflect we allow any ProductN, not just TupleN - else getProductArgs(optionTArg) match { + else productArgs match { case Nil if isUnapplySeq => List(seqToRepeatedChecked(optionTArg)) case tps if isUnapplySeq => tps.init :+ seqToRepeatedChecked(tps.last) case tps => tps @@ -569,7 +576,7 @@ trait Infer extends Checkable { foreach3(tparams, tvars, targs) { (tparam, tvar, targ) => val retract = ( - targ.typeSymbol == NothingClass // only retract Nothings + targ.typeSymbol == NothingClass // only retract Nothings && (restpe.isWildcard || !varianceInType(restpe)(tparam).isPositive) // don't retract covariant occurrences ) @@ -851,7 +858,7 @@ trait Infer extends Checkable { val lencmp = compareLengths(argtpes0, formals) if (lencmp > 0) tryTupleApply else if (lencmp == 0) { - // fast track if no named arguments are used + // fast track if no named arguments are used if (!containsNamedType(argtpes0)) typesCompatible(argtpes0) else { @@ -1177,10 +1184,10 @@ trait Infer extends Checkable { args: List[Tree], pt0: Type): List[Symbol] = fn.tpe match { case mt @ MethodType(params0, _) => try { - val pt = if (pt0.typeSymbol == UnitClass) WildcardType else pt0 - val formals = formalTypes(mt.paramTypes, args.length) + val pt = if (pt0.typeSymbol == UnitClass) WildcardType else pt0 + val formals = formalTypes(mt.paramTypes, args.length) val argtpes = tupleIfNecessary(formals, args map (x => elimAnonymousClass(x.tpe.deconst))) - val restpe = fn.tpe.resultType(argtpes) + val restpe = fn.tpe.resultType(argtpes) val AdjustedTypeArgs.AllArgsAndUndets(okparams, okargs, allargs, leftUndet) = methTypeArgs(undetparams, formals, restpe, argtpes, pt) @@ -1289,13 +1296,13 @@ trait Infer extends Checkable { inferred match { case Some(targs) => - new TreeTypeSubstituter(undetparams, targs).traverse(tree) - notifyUndetparamsInferred(undetparams, targs) + new TreeTypeSubstituter(undetparams, targs).traverse(tree) + notifyUndetparamsInferred(undetparams, targs) case _ => def full = if (isFullyDefined(pt)) "(fully defined)" else "(not fully defined)" devWarning(s"failed inferConstructorInstance for $tree: ${tree.tpe} undet=$undetparams, pt=$pt $full") - // if (settings.explaintypes.value) explainTypes(resTp.instantiateTypeParams(undetparams, tvars), pt) - ConstrInstantiationError(tree, resTp, pt) + // if (settings.explaintypes.value) explainTypes(resTp.instantiateTypeParams(undetparams, tvars), pt) + ConstrInstantiationError(tree, resTp, pt) } } @@ -1491,26 +1498,32 @@ trait Infer extends Checkable { */ def inferExprAlternative(tree: Tree, pt: Type) = tree.tpe match { case OverloadedType(pre, alts) => tryTwice { isSecondTry => - val alts0 = alts filter (alt => isWeaklyCompatible(pre.memberType(alt), pt)) + val alts0 = alts filter (alt => isWeaklyCompatible(pre.memberType(alt), pt)) val alts1 = if (alts0.isEmpty) alts else alts0 val bests = bestAlternatives(alts1) { (sym1, sym2) => val tp1 = pre.memberType(sym1) - val tp2 = pre.memberType(sym2) + val tp2 = pre.memberType(sym2) ( tp2 == ErrorType || (!isWeaklyCompatible(tp2, pt) && isWeaklyCompatible(tp1, pt)) || isStrictlyMoreSpecific(tp1, tp2, sym1, sym2) ) - } + } // todo: missing test case for bests.isEmpty bests match { case best :: Nil => tree setSymbol best setType (pre memberType best) - case best :: competing :: _ if alts0.nonEmpty => if (!pt.isErroneous) AmbiguousExprAlternativeError(tree, pre, best, competing, pt, isSecondTry) + case best :: competing :: _ if alts0.nonEmpty => + // SI-6912 Don't give up and leave an OverloadedType on the tree. + // Originally I wrote this as `if (secondTry) ... `, but `tryTwice` won't attempt the second try + // unless an error is issued. We're not issuing an error, in the assumption that it would be + // spurious in light of the erroneous expected type + if (pt.isErroneous) setError(tree) + else AmbiguousExprAlternativeError(tree, pre, best, competing, pt, isSecondTry) case _ => if (bests.isEmpty || alts0.isEmpty) NoBestExprAlternativeError(tree, pt, isSecondTry) + } } } - } @inline private def inSilentMode(context: Context)(expr: => Boolean): Boolean = { val oldState = context.state @@ -1554,7 +1567,7 @@ trait Infer extends Checkable { val namesMatch = namesOfNamedArguments(argtpes) match { case Nil => Nil case names => eligible filter (m => names forall (name => m.info.params exists (p => paramMatchesName(p, name)))) - } + } if (namesMatch.nonEmpty) namesMatch else if (eligible.isEmpty || eligible.tail.isEmpty) @@ -1562,8 +1575,8 @@ trait Infer extends Checkable { else eligible filter (alt => !alt.hasDefault && isApplicableBasedOnArity(alt.tpe, argtpes.length, varargsStar, tuplingAllowed = true) - ) - } + ) + } /** Assign `tree` the type of an alternative which is applicable * to `argtpes`, and whose result type is compatible with `pt`. @@ -1587,7 +1600,7 @@ trait Infer extends Checkable { val argtpes = argtpes0 mapConserve { case RepeatedType(tp) => varargsStar = true ; tp case tp => tp - } + } def followType(sym: Symbol) = followApply(pre memberType sym) def bestForExpectedType(pt: Type, isLastTry: Boolean): Unit = { val applicable0 = alts filter (alt => inSilentMode(context)(isApplicable(undetparams, followType(alt), argtpes, pt))) @@ -1600,8 +1613,8 @@ trait Infer extends Checkable { case best :: Nil => tree setSymbol best setType (pre memberType best) // success case Nil if pt eq WildcardType => NoBestMethodAlternativeError(tree, argtpes, pt, isLastTry) // failed case Nil => bestForExpectedType(WildcardType, isLastTry) // failed, but retry with WildcardType - } - } + } + } // This potentially makes up to four attempts: tryTwice may execute // with and without views enabled, and bestForExpectedType will try again // with pt = WildcardType if it fails with pt != WildcardType. @@ -1609,7 +1622,7 @@ trait Infer extends Checkable { val pt = if (pt0.typeSymbol == UnitClass) WildcardType else pt0 debuglog(s"infer method alt ${tree.symbol} with alternatives ${alts map pre.memberType} argtpes=$argtpes pt=$pt") bestForExpectedType(pt, isLastTry) - } + } } /** Try inference twice, once without views and once with views, diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index 5f70da6a63..48bdd07e66 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -604,7 +604,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // Some(this(extractorCall, args)) // TODO: simplify spliceApply? // } // } - } + } abstract class ExtractorCall(val args: List[Tree]) { val nbSubPats = args.length @@ -1470,13 +1470,8 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def drop(tgt: Tree)(n: Int): Tree = (tgt DOT vpmName.drop) (LIT(n)) def _equals(checker: Tree, binder: Symbol): Tree = checker MEMBER_== REF(binder) // NOTE: checker must be the target of the ==, that's the patmat semantics for ya - // drop annotations generated by CPS plugin etc, since its annotationchecker rejects T @cps[U] <: Any - // let's assume for now annotations don't affect casts, drop them there, and bring them back using the outer Typed tree - private def mkCast(t: Tree, tp: Type) = - Typed(gen.mkAsInstanceOf(t, tp.withoutAnnotations, true, false), TypeTree() setType tp) - // the force is needed mainly to deal with the GADT typing hack (we can't detect it otherwise as tp nor pt need contain an abstract type, we're just casting wildly) - def _asInstanceOf(b: Symbol, tp: Type): Tree = if (typesConform(b.info, tp)) REF(b) else mkCast(REF(b), tp) + def _asInstanceOf(b: Symbol, tp: Type): Tree = if (typesConform(b.info, tp)) REF(b) else gen.mkCastPreservingAnnotations(REF(b), tp) def _isInstanceOf(b: Symbol, tp: Type): Tree = gen.mkIsInstanceOf(REF(b), tp.withoutAnnotations, true, false) // duplicated out of frustration with cast generation @@ -1820,8 +1815,8 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL if (cols.isEmpty || cols.tails.isEmpty) cols map toString else { val colLens = cols map (c => toString(c).length) - val maxLen = max(colLens) - val avgLen = colLens.sum/colLens.length + val maxLen = max(colLens) + val avgLen = colLens.sum/colLens.length val goalLen = maxLen min avgLen*2 def pad(s: String) = { val toAdd = ((goalLen - s.length) max 0) + 2 @@ -3239,7 +3234,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL } } - private val defaultLabel: Symbol = newSynthCaseLabel("default") + private val defaultLabel: Symbol = newSynthCaseLabel("default") /** Collapse guarded cases that switch on the same constant (the last case may be unguarded). * diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 20907979e9..e7c89f6067 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -70,7 +70,7 @@ trait Typers extends Adaptations with Tags { @inline final def filter(p: T => Boolean): SilentResult[T] = this match { case SilentResultValue(value) if !p(value) => SilentTypeError(TypeErrorWrapper(new TypeError(NoPosition, "!p"))) case _ => this - } + } @inline final def orElse[T1 >: T](f: AbsTypeError => T1): T1 = this match { case SilentResultValue(value) => value case SilentTypeError(err) => f(err) @@ -806,9 +806,9 @@ trait Typers extends Adaptations with Tags { withCondConstrTyper(treeInfo.isSelfOrSuperConstrCall(tree))(typer1 => if (original != EmptyTree && pt != WildcardType) ( typer1 silent { tpr => - val withImplicitArgs = tpr.applyImplicitArgs(tree) - if (tpr.context.hasErrors) tree // silent will wrap it in SilentTypeError anyway - else tpr.typed(withImplicitArgs, mode, pt) + val withImplicitArgs = tpr.applyImplicitArgs(tree) + if (tpr.context.hasErrors) tree // silent will wrap it in SilentTypeError anyway + else tpr.typed(withImplicitArgs, mode, pt) } orElse { _ => debuglog("fallback on implicits: " + tree + "/" + resetAllAttrs(original)) @@ -817,12 +817,12 @@ trait Typers extends Adaptations with Tags { // we pass `EmptyTree` as the `original`. intended? added in 2009 (53d98e7d42) by martin. tree1 setType addAnnotations(tree1, tree1.tpe) if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, EmptyTree) - } + } ) - else - typer1.typed(typer1.applyImplicitArgs(tree), mode, pt) + else + typer1.typed(typer1.applyImplicitArgs(tree), mode, pt) ) - } + } def instantiateToMethodType(mt: MethodType): Tree = { val meth = tree match { @@ -950,6 +950,10 @@ trait Typers extends Adaptations with Tags { def apply(tp: Type) = mapOver(tp) match { // !!! FIXME - skipping this when variance.isInvariant allows unsoundness, see SI-5189 case TypeRef(NoPrefix, tpSym, Nil) if !variance.isInvariant && tpSym.isTypeParameterOrSkolem && tpSym.owner.isTerm => + // must initialize or tpSym.tpe might see random type params!! + // without this, we'll get very weird types inferred in test/scaladoc/run/SI-5933.scala + // TODO: why is that?? + tpSym.initialize val bounds = if (variance.isPositive) TypeBounds.upper(tpSym.tpe) else TypeBounds.lower(tpSym.tpe) // origin must be the type param so we can deskolemize val skolem = context.owner.newGADTSkolem(unit.freshTypeName("?"+tpSym.name), tpSym, bounds) @@ -972,7 +976,7 @@ trait Typers extends Adaptations with Tags { newTyper(ctorContext).infer.inferConstructorInstance(tree1, clazz.typeParams, ptSafe) // simplify types without losing safety, - // so that error messages don't unnecessarily refer to skolems + // so that we get rid of unnecessary type slack, and so that error messages don't unnecessarily refer to skolems val extrapolate = new ExistentialExtrapolation(freeVars) extrapolate (_: Type) val extrapolated = tree1.tpe match { case MethodType(ctorArgs, res) => // ctorArgs are actually in a covariant position, since this is the type of the subpatterns of the pattern represented by this Apply node @@ -1005,7 +1009,7 @@ trait Typers extends Adaptations with Tags { val pre = if (owner.isPackageClass) owner.thisType else if (owner.isClass) context.enclosingSubClassContext(owner).prefix - else NoPrefix + else NoPrefix stabilize0(pre) case Select(qualqual, _) => stabilize0(qualqual.tpe) @@ -1207,10 +1211,10 @@ trait Typers extends Adaptations with Tags { } fallBack } - } + } val tree1 = if (mode.inExprModeButNot(FUNmode) && treeInfo.isMacroApplication(tree)) macroExpandApply(this, tree, mode, pt) else tree if (tree == tree1) vanillaAdapt(tree1) else tree1 - } + } } def instantiate(tree: Tree, mode: Mode, pt: Type): Tree = { @@ -1224,9 +1228,9 @@ trait Typers extends Adaptations with Tags { def instantiateExpectingUnit(tree: Tree, mode: Mode): Tree = { val savedUndetparams = context.undetparams silent(_.instantiate(tree, mode, UnitClass.tpe)) orElse { _ => - context.undetparams = savedUndetparams - val valueDiscard = atPos(tree.pos)(Block(List(instantiate(tree, mode, WildcardType)), Literal(Constant()))) - typed(valueDiscard, mode, UnitClass.tpe) + context.undetparams = savedUndetparams + val valueDiscard = atPos(tree.pos)(Block(List(instantiate(tree, mode, WildcardType)), Literal(Constant()))) + typed(valueDiscard, mode, UnitClass.tpe) } } @@ -1297,16 +1301,16 @@ trait Typers extends Adaptations with Tags { */ def adaptToMemberWithArgs(tree: Tree, qual: Tree, name: Name, mode: Mode, reportAmbiguous: Boolean, saveErrors: Boolean): Tree = { def onError(reportError: => Tree): Tree = context.tree match { - case Apply(tree1, args) if (tree1 eq tree) && args.nonEmpty => + case Apply(tree1, args) if (tree1 eq tree) && args.nonEmpty => ( silent (_.typedArgs(args, mode)) map (_.asInstanceOf[List[Tree]]) filter (xs => !(xs exists (_.isErrorTyped))) map (xs => adaptToArguments(qual, name, xs, WildcardType, reportAmbiguous, saveErrors)) orElse ( _ => reportError) ) - case _ => - reportError - } + case _ => + reportError + } silent(_.adaptToMember(qual, HasMember(name), false)) orElse (err => onError { @@ -1314,7 +1318,7 @@ trait Typers extends Adaptations with Tags { setError(tree) } ) - } + } /** Try to apply an implicit conversion to `qual` to that it contains a * member `name` of arbitrary type. @@ -1818,7 +1822,33 @@ trait Typers extends Adaptations with Tags { }) } val impl2 = finishMethodSynthesis(impl1, clazz, context) + + // SI-5954. On second compile of a companion class contained in a package object we end up + // with some confusion of names which leads to having two symbols with the same name in the + // same owner. Until that can be straightened out we can't allow companion objects in package + // objects. But this code also tries to be friendly by distinguishing between case classes and + // user written companion pairs + def restrictPackageObjectMembers(mdef : ModuleDef) = for (m <- mdef.symbol.info.members) { + // ignore synthetic objects, because the "companion" object to a case class is synthetic and + // we only want one error per case class + if (!m.isSynthetic) { + // can't handle case classes in package objects + if (m.isCaseClass) pkgObjectRestriction(m, mdef, "case") + // can't handle companion class/object pairs in package objects + else if ((m.isClass && m.companionModule != NoSymbol && !m.companionModule.isSynthetic) || + (m.isModule && m.companionClass != NoSymbol && !m.companionClass.isSynthetic)) + pkgObjectRestriction(m, mdef, "companion") + } + + def pkgObjectRestriction(m : Symbol, mdef : ModuleDef, restricted : String) = { + val pkgName = mdef.symbol.ownerChain find (_.isPackage) map (_.decodedName) getOrElse mdef.symbol.toString + context.error(if (m.pos.isDefined) m.pos else mdef.pos, s"implementation restriction: package object ${pkgName} cannot contain ${restricted} ${m}. Instead, ${m} should be placed directly in package ${pkgName}.") + } + } + if (!settings.companionsInPkgObjs.value && mdef.symbol.isPackageObject) + restrictPackageObjectMembers(mdef) + treeCopy.ModuleDef(mdef, typedMods, mdef.name, impl2) setType NoType } /** In order to override this in the TreeCheckers Typer so synthetics aren't re-added @@ -1979,57 +2009,60 @@ trait Typers extends Adaptations with Tags { */ def computeParamAliases(clazz: Symbol, vparamss: List[List[ValDef]], rhs: Tree) { debuglog(s"computing param aliases for $clazz:${clazz.primaryConstructor.tpe}:$rhs") + val pending = ListBuffer[AbsTypeError]() + + // !!! This method is redundant with other, less buggy ones. def decompose(call: Tree): (Tree, List[Tree]) = call match { case Apply(fn, args) => - val (superConstr, args1) = decompose(fn) + // an object cannot be allowed to pass a reference to itself to a superconstructor + // because of initialization issues; SI-473, SI-3913, SI-6928. + foreachSubTreeBoundTo(args, clazz) { tree => + if (tree.symbol.isModule) + pending += SuperConstrReferenceError(tree) + tree match { + case This(qual) => + pending += SuperConstrArgsThisReferenceError(tree) + case _ => () + } + } + val (superConstr, preArgs) = decompose(fn) val params = fn.tpe.params - val args2 = if (params.isEmpty || !isRepeatedParamType(params.last.tpe)) args - else args.take(params.length - 1) :+ EmptyTree - assert(sameLength(args2, params) || call.isErrorTyped, "mismatch " + clazz + " " + (params map (_.tpe)) + " " + args2)//debug - (superConstr, args1 ::: args2) - case Block(stats, expr) if !stats.isEmpty => - decompose(stats.last) + // appending a dummy tree to represent Nil for an empty varargs (is this really necessary?) + val applyArgs = if (args.length < params.length) args :+ EmptyTree else args take params.length + + assert(sameLength(applyArgs, params) || call.isErrorTyped, + s"arity mismatch but call is not error typed: $clazz (params=$params, args=$applyArgs)") + + (superConstr, preArgs ::: applyArgs) + case Block(_ :+ superCall, _) => + decompose(superCall) case _ => - (call, List()) + (call, Nil) } val (superConstr, superArgs) = decompose(rhs) assert(superConstr.symbol ne null, superConstr)//debug - - val pending = ListBuffer[AbsTypeError]() - // an object cannot be allowed to pass a reference to itself to a superconstructor - // because of initialization issues; bug #473 - foreachSubTreeBoundTo(superArgs, clazz) { tree => - if (tree.symbol.isModule) - pending += SuperConstrReferenceError(tree) - tree match { - case This(qual) => - pending += SuperConstrArgsThisReferenceError(tree) - case _ => () - } - } - - if (superConstr.symbol.isPrimaryConstructor) { - val superClazz = superConstr.symbol.owner - if (!superClazz.isJavaDefined) { - val superParamAccessors = superClazz.constrParamAccessors - if (sameLength(superParamAccessors, superArgs)) { - for ((superAcc, superArg @ Ident(name)) <- superParamAccessors zip superArgs) { - if (vparamss.exists(_.exists(_.symbol == superArg.symbol))) { - var alias = superAcc.initialize.alias - if (alias == NoSymbol) - alias = superAcc.getter(superAcc.owner) - if (alias != NoSymbol && - superClazz.info.nonPrivateMember(alias.name) != alias) - alias = NoSymbol - if (alias != NoSymbol) { - var ownAcc = clazz.info.decl(name).suchThat(_.isParamAccessor) - if ((ownAcc hasFlag ACCESSOR) && !ownAcc.isDeferred) - ownAcc = ownAcc.accessed - if (!ownAcc.isVariable && !alias.accessed.isVariable) { - debuglog("" + ownAcc + " has alias "+alias.fullLocationString) //debug - ownAcc.asInstanceOf[TermSymbol].setAlias(alias) - } - } + def superClazz = superConstr.symbol.owner + def superParamAccessors = superClazz.constrParamAccessors + + // associate superclass paramaccessors with their aliases + if (superConstr.symbol.isPrimaryConstructor && !superClazz.isJavaDefined && sameLength(superParamAccessors, superArgs)) { + for ((superAcc, superArg @ Ident(name)) <- superParamAccessors zip superArgs) { + if (mexists(vparamss)(_.symbol == superArg.symbol)) { + val alias = ( + superAcc.initialize.alias + orElse (superAcc getter superAcc.owner) + filter (alias => superClazz.info.nonPrivateMember(alias.name) != alias) + ) + if (alias.exists && !alias.accessed.isVariable) { + val ownAcc = clazz.info decl name suchThat (_.isParamAccessor) match { + case acc if !acc.isDeferred && acc.hasAccessorFlag => acc.accessed + case acc => acc + } + ownAcc match { + case acc: TermSymbol if !acc.isVariable => + debuglog(s"$acc has alias ${alias.fullLocationString}") + acc setAlias alias + case _ => } } } @@ -2070,7 +2103,7 @@ trait Typers extends Adaptations with Tags { f(subTree) } - /** Check if a structurally defined method violates implementation restrictions. + /** Check if a structurally defined method violates implementation restrictions. * A method cannot be called if it is a non-private member of a refinement type * and if its parameter's types are any of: * - the self-type of the refinement @@ -2105,7 +2138,7 @@ trait Typers extends Adaptations with Tags { foreachWithIndex(paramssTypes(meth.tpe)) { (paramList, listIdx) => foreachWithIndex(paramList) { (paramType, paramIdx) => - val sym = paramType.typeSymbol + val sym = paramType.typeSymbol def paramPos = nthParamPos(listIdx, paramIdx) /** Not enough to look for abstract types; have to recursively check the bounds @@ -2120,17 +2153,17 @@ trait Typers extends Adaptations with Tags { || (!sym.hasTransOwner(meth) && failStruct(paramPos, "a type member of that refinement", what)) || checkAbstract(sym.info.bounds.hi, "Type bound") ) - } + } tp0.dealiasWidenChain forall (t => check(t.typeSymbol)) } checkAbstract(paramType, "Parameter type") - if (sym.isDerivedValueClass) - failStruct(paramPos, "a user-defined value class") - if (paramType.isInstanceOf[ThisType] && sym == meth.owner) - failStruct(paramPos, "the type of that refinement (self type)") - } + if (sym.isDerivedValueClass) + failStruct(paramPos, "a user-defined value class") + if (paramType.isInstanceOf[ThisType] && sym == meth.owner) + failStruct(paramPos, "the type of that refinement (self type)") } + } if (resultType.typeSymbol.isDerivedValueClass) failStruct(ddef.tpt.pos, "a user-defined value class", where = "Result type") } @@ -2151,12 +2184,12 @@ trait Typers extends Adaptations with Tags { if (context.scope.lookup(name) == NoSymbol) { lookupVariable(name.toString.substring(1), enclClass) foreach { repl => silent(_.typedTypeConstructor(stringParser(repl).typ())) map { tpt => - val alias = enclClass.newAliasType(name.toTypeName, useCase.pos) - val tparams = cloneSymbolsAtOwner(tpt.tpe.typeSymbol.typeParams, alias) - val newInfo = genPolyType(tparams, appliedType(tpt.tpe, tparams map (_.tpe))) - alias setInfo newInfo - context.scope.enter(alias) - } + val alias = enclClass.newAliasType(name.toTypeName, useCase.pos) + val tparams = cloneSymbolsAtOwner(tpt.tpe.typeSymbol.typeParams, alias) + val newInfo = genPolyType(tparams, appliedType(tpt.tpe, tparams map (_.tpe))) + alias setInfo newInfo + context.scope.enter(alias) + } } } for (tree <- trees; t <- tree) @@ -2474,172 +2507,181 @@ trait Typers extends Adaptations with Tags { match_ // will be translated in phase `patmat` } - // synthesize and type check a PartialFunction implementation based on a match specified by `cases` - // Match(EmptyTree, cases) ==> new PartialFunction { def apply<OrElse>(params) = `translateMatch('`(param1,...,paramN)` match { cases }')` } - // for fresh params, the selector of the match we'll translated simply gathers those in a tuple - // NOTE: restricted to PartialFunction -- leave Function trees if the expected type does not demand a partial function - class MatchFunTyper(tree: Tree, cases: List[CaseDef], mode: Mode, pt: Type) { - // TODO: remove FunctionN support -- this is currently designed so that it can emit FunctionN and PartialFunction subclasses - // however, we should leave Function nodes until Uncurry so phases after typer can still detect normal Function trees - // we need to synthesize PartialFunction impls, though, to avoid nastiness in Uncurry in transforming&duplicating generated pattern matcher trees - // TODO: remove PartialFunction support from UnCurry - private val targs = pt.normalize.typeArgs - private val arity = if (isFunctionType(pt)) targs.length - 1 else 1 // TODO pt should always be a (Partial)Function, right? - private val ptRes = if (targs.isEmpty) WildcardType else targs.last // may not be fully defined + /** synthesize and type check a PartialFunction implementation based on the match in `tree` + * + * `param => sel match { cases }` becomes: + * + * new AbstractPartialFunction[$argTp, $matchResTp] { + * def applyOrElse[A1 <: $argTp, B1 >: $matchResTp]($param: A1, default: A1 => B1): B1 = + * $selector match { $cases } + * def isDefinedAt(x: $argTp): Boolean = + * $selector match { $casesTrue } + * } + * + * TODO: it would be nicer to generate the tree specified above at once and type it as a whole, + * there are two gotchas: + * - matchResTp may not be known until we've typed the match (can only use resTp when it's fully defined), + * - if we typed the match in isolation first, you'd know its result type, but would have to re-jig the owner structure + * - could we use a type variable for matchResTp and backpatch it? + * - occurrences of `this` in `cases` or `sel` must resolve to the this of the class originally enclosing the match, + * not of the anonymous partial function subclass + * + * an alternative TODO: add partial function AST node or equivalent and get rid of this synthesis --> do everything in uncurry (or later) + * however, note that pattern matching codegen is designed to run *before* uncurry + */ + def synthesizePartialFunction(paramName: TermName, paramPos: Position, tree: Tree, mode: Mode, pt: Type): Tree = { + assert(pt.typeSymbol == PartialFunctionClass, s"PartialFunction synthesis for match in $tree requires PartialFunction expected type, but got $pt.") + val targs = pt.normalize.typeArgs + + // if targs.head isn't fully defined, we can translate --> error + targs match { + case argTp :: _ if isFullyDefined(argTp) => // ok + case _ => // uh-oh + MissingParameterTypeAnonMatchError(tree, pt) + return setError(tree) + } - private val isPartial = pt.typeSymbol == PartialFunctionClass - assert(isPartial) + // NOTE: resTp still might not be fully defined + val argTp :: resTp :: Nil = targs - private val anonClass = context.owner.newAnonymousFunctionClass(tree.pos) + // targs must conform to Any for us to synthesize an applyOrElse (fallback to apply otherwise -- typically for @cps annotated targs) + val targsValidParams = targs forall (_ <:< AnyClass.tpe) - anonClass addAnnotation AnnotationInfo(SerialVersionUIDAttr.tpe, List(Literal(Constant(0))), List()) + val anonClass = (context.owner + newAnonymousFunctionClass tree.pos + addAnnotation AnnotationInfo(SerialVersionUIDAttr.tpe, List(Literal(Constant(0))), List())) - def deriveFormals = - if (targs.isEmpty) Nil - else targs.init + import CODE._ - def mkParams(methodSym: Symbol, formals: List[Type] = deriveFormals) = - if (formals.isEmpty || !formals.forall(isFullyDefined)) { MissingParameterTypeAnonMatchError(tree, pt); Nil } - else methodSym newSyntheticValueParams formals + val Match(sel, cases) = tree - def mkSel(params: List[Symbol]) = - if (params.isEmpty) EmptyTree - else { - val ids = params map (p => Ident(p.name)) - atPos(tree.pos.focusStart) { if (arity == 1) ids.head else gen.mkTuple(ids) } - } + // need to duplicate the cases before typing them to generate the apply method, or the symbols will be all messed up + val casesTrue = cases map (c => deriveCaseDef(c)(x => atPos(x.pos.focus)(TRUE)).duplicate.asInstanceOf[CaseDef]) + + // must generate a new tree every time + def selector: Tree = gen.mkUnchecked( + if (sel != EmptyTree) sel.duplicate + else atPos(tree.pos.focusStart)( + // SI-6925: subsume type of the selector to `argTp` + // we don't want/need the match to see the `A1` type that we must use for variance reasons in the method signature + // + // this failed: replace `selector` by `Typed(selector, TypeTree(argTp))` -- as it's an upcast, this should never fail, + // `(x: A1): A` doesn't always type check, even though `A1 <: A`, due to singleton types (test/files/pos/t4269.scala) + // hence the cast, which will be erased in posterasure + // (the cast originally caused extremely weird types to show up + // in test/scaladoc/run/SI-5933.scala because `variantToSkolem` was missing `tpSym.initialize`) + gen.mkCastPreservingAnnotations(Ident(paramName), argTp) + )) + + def mkParam(methodSym: Symbol, tp: Type = argTp) = + methodSym.newValueParameter(paramName, paramPos.focus, SYNTHETIC) setInfo tp + + // `def applyOrElse[A1 <: $argTp, B1 >: $matchResTp](x: A1, default: A1 => B1): B1 = + // ${`$selector match { $cases }` updateAttachment DefaultOverrideMatchAttachment(REF(default) APPLY (REF(x)))}` + def applyOrElseMethodDef = { + val methodSym = anonClass.newMethod(nme.applyOrElse, tree.pos, FINAL | OVERRIDE) - import CODE._ + // create the parameter that corresponds to the function's parameter + val A1 = methodSym newTypeParameter (newTypeName("A1")) setInfo TypeBounds.upper(argTp) + val x = mkParam(methodSym, A1.tpe) - // need to duplicate the cases before typing them to generate the apply method, or the symbols will be all messed up - val casesTrue = if (isPartial) cases map (c => deriveCaseDef(c)(x => atPos(x.pos.focus)(TRUE)).duplicate.asInstanceOf[CaseDef]) else Nil - // println("casesTrue "+ casesTrue) - def parentsPartial(targs: List[Type]) = addSerializable(appliedType(AbstractPartialFunctionClass.typeConstructor, targs)) + // applyOrElse's default parameter: + val B1 = methodSym newTypeParameter (newTypeName("B1")) setInfo TypeBounds.empty //lower(resTp) + val default = methodSym newValueParameter (newTermName("default"), tree.pos.focus, SYNTHETIC) setInfo functionType(List(A1.tpe), B1.tpe) - def applyMethod = { - // rig the show so we can get started typing the method body -- later we'll correct the infos... - anonClass setInfo ClassInfoType(addSerializable(ObjectClass.tpe, pt), newScope, anonClass) - val methodSym = anonClass.newMethod(nme.apply, tree.pos, if(isPartial) (FINAL | OVERRIDE) else FINAL) - val paramSyms = mkParams(methodSym) - val selector = mkSel(paramSyms) + val paramSyms = List(x, default) + methodSym setInfo polyType(List(A1, B1), MethodType(paramSyms, B1.tpe)) - if (selector eq EmptyTree) EmptyTree - else { - methodSym setInfoAndEnter MethodType(paramSyms, AnyClass.tpe) + val methodBodyTyper = newTyper(context.makeNewScope(context.tree, methodSym)) + // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it) + paramSyms foreach (methodBodyTyper.context.scope enter _) - val methodBodyTyper = newTyper(context.makeNewScope(context.tree, methodSym)) // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it) - paramSyms foreach (methodBodyTyper.context.scope enter _) + val match_ = methodBodyTyper.typedMatch(selector, cases, mode, resTp) - val match_ = methodBodyTyper.typedMatch(gen.mkUnchecked(selector), cases, mode, ptRes) - val resTp = match_.tpe + val matchResTp = match_.tpe + B1 setInfo TypeBounds.lower(matchResTp) // patch info - val methFormals = paramSyms map (_.tpe) - val parents = ( - if (isPartial) parentsPartial(List(methFormals.head, resTp)) - else addSerializable(abstractFunctionType(methFormals, resTp)) - ) - anonClass setInfo ClassInfoType(parents, newScope, anonClass) - methodSym setInfoAndEnter MethodType(paramSyms, resTp) + match_ setType B1.tpe - DefDef(methodSym, methodBodyTyper.virtualizedMatch(match_, mode, resTp)) - } + // the default uses applyOrElse's first parameter since the scrut's type has been widened + val matchWithDefault = match_ updateAttachment DefaultOverrideMatchAttachment(REF(default) APPLY (REF(x))) + (DefDef(methodSym, methodBodyTyper.virtualizedMatch(matchWithDefault, mode, B1.tpe)), matchResTp) } - // def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = - def applyOrElseMethodDef = { - // rig the show so we can get started typing the method body -- later we'll correct the infos... - // targs were type arguments for PartialFunction, so we know they will work for AbstractPartialFunction as well - anonClass setInfo ClassInfoType(parentsPartial(targs), newScope, anonClass) - val methodSym = anonClass.newMethod(nme.applyOrElse, tree.pos, FINAL | OVERRIDE) + // `def isDefinedAt(x: $argTp): Boolean = ${`$selector match { $casesTrue ` updateAttachment DefaultOverrideMatchAttachment(FALSE)}` + def isDefinedAtMethod = { + val methodSym = anonClass.newMethod(nme.isDefinedAt, tree.pos.makeTransparent, FINAL) + val paramSym = mkParam(methodSym) - // create the parameter that corresponds to the function's parameter - val List(argTp) = deriveFormals - val A1 = methodSym newTypeParameter(newTypeName("A1")) setInfo TypeBounds.upper(argTp) - val paramSyms@List(x) = mkParams(methodSym, List(A1.tpe)) - val selector = mkSel(paramSyms) + val methodBodyTyper = newTyper(context.makeNewScope(context.tree, methodSym)) // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it) + methodBodyTyper.context.scope enter paramSym + methodSym setInfo MethodType(List(paramSym), BooleanClass.tpe) - if (selector eq EmptyTree) EmptyTree - else { - // applyOrElse's default parameter: - val B1 = methodSym newTypeParameter(newTypeName("B1")) setInfo TypeBounds.empty //lower(resTp) - val default = methodSym newValueParameter(newTermName("default"), tree.pos.focus, SYNTHETIC) setInfo functionType(List(A1.tpe), B1.tpe) + val match_ = methodBodyTyper.typedMatch(selector, casesTrue, mode, BooleanClass.tpe) - val paramSyms = List(x, default) - methodSym setInfoAndEnter polyType(List(A1, B1), MethodType(paramSyms, B1.tpe)) + val matchWithDefault = match_ updateAttachment DefaultOverrideMatchAttachment(FALSE) + DefDef(methodSym, methodBodyTyper.virtualizedMatch(matchWithDefault, mode, BooleanClass.tpe)) + } - val methodBodyTyper = newTyper(context.makeNewScope(context.tree, methodSym)) // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it) - paramSyms foreach (methodBodyTyper.context.scope enter _) + // only used for @cps annotated partial functions + // `def apply(x: $argTp): $matchResTp = $selector match { $cases }` + def applyMethod = { + val methodSym = anonClass.newMethod(nme.apply, tree.pos, FINAL | OVERRIDE) + val paramSym = mkParam(methodSym) - val match_ = methodBodyTyper.typedMatch(gen.mkUnchecked(selector), cases, mode, ptRes) - val resTp = match_.tpe + methodSym setInfo MethodType(List(paramSym), AnyClass.tpe) - anonClass setInfo ClassInfoType(parentsPartial(List(argTp, resTp)), newScope, anonClass) - B1 setInfo TypeBounds.lower(resTp) - anonClass.info.decls enter methodSym // methodSym's info need not change (B1's bound has been updated instead) + val methodBodyTyper = newTyper(context.makeNewScope(context.tree, methodSym)) + // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it) + methodBodyTyper.context.scope enter paramSym - match_ setType B1.tpe + val match_ = methodBodyTyper.typedMatch(selector, cases, mode, resTp) - // the default uses applyOrElse's first parameter since the scrut's type has been widened - val body = methodBodyTyper.virtualizedMatch(match_ updateAttachment DefaultOverrideMatchAttachment(REF(default) APPLY (REF(x))), mode, B1.tpe) + val matchResTp = match_.tpe + methodSym setInfo MethodType(List(paramSym), matchResTp) // patch info - DefDef(methodSym, body) - } + (DefDef(methodSym, methodBodyTyper.virtualizedMatch(match_, mode, matchResTp)), matchResTp) } - def isDefinedAtMethod = { - val methodSym = anonClass.newMethod(nme.isDefinedAt, tree.pos.makeTransparent, FINAL) - val paramSyms = mkParams(methodSym) - val selector = mkSel(paramSyms) - - if (selector eq EmptyTree) EmptyTree - else { - val methodBodyTyper = newTyper(context.makeNewScope(context.tree, methodSym)) // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it) - paramSyms foreach (methodBodyTyper.context.scope enter _) - methodSym setInfoAndEnter MethodType(paramSyms, BooleanClass.tpe) + def parents(resTp: Type) = addSerializable(appliedType(AbstractPartialFunctionClass.typeConstructor, List(argTp, resTp))) - val match_ = methodBodyTyper.typedMatch(gen.mkUnchecked(selector), casesTrue, mode, BooleanClass.tpe) - val body = methodBodyTyper.virtualizedMatch(match_ updateAttachment DefaultOverrideMatchAttachment(FALSE), mode, BooleanClass.tpe) + val members = { + val (applyMeth, matchResTp) = { + // rig the show so we can get started typing the method body -- later we'll correct the infos... + // targs were type arguments for PartialFunction, so we know they will work for AbstractPartialFunction as well + anonClass setInfo ClassInfoType(parents(resTp), newScope, anonClass) - DefDef(methodSym, body) + // somehow @cps annotations upset the typer when looking at applyOrElse's signature, but not apply's + // TODO: figure out the details (T @cps[U] is not a subtype of Any, but then why does it work for the apply method?) + if (targsValidParams) applyOrElseMethodDef + else applyMethod } - } - lazy val members = if (isPartial) { - // somehow @cps annotations upset the typer when looking at applyOrElse's signature, but not apply's - // TODO: figure out the details (T @cps[U] is not a subtype of Any, but then why does it work for the apply method?) - if (targs forall (_ <:< AnyClass.tpe)) List(applyOrElseMethodDef, isDefinedAtMethod) - else List(applyMethod, isDefinedAtMethod) - } else List(applyMethod) + // patch info to the class's definitive info + anonClass setInfo ClassInfoType(parents(matchResTp), newScope, anonClass) + List(applyMeth, isDefinedAtMethod) + } - def translated = - if (members.head eq EmptyTree) setError(tree) - else { - val typedBlock = typedPos(tree.pos, mode, pt) { - Block(ClassDef(anonClass, NoMods, ListOfNil, members, tree.pos.focus), atPos(tree.pos.focus)(New(anonClass.tpe))) - } - // Don't leak implementation details into the type, see SI-6575 - if (isPartial && !typedBlock.isErrorTyped) - typedPos(tree.pos, mode, pt) { - Typed(typedBlock, TypeTree(typedBlock.tpe baseType PartialFunctionClass)) - } - else typedBlock - } - } + members foreach (m => anonClass.info.decls enter m.symbol) - // Function(params, Match(sel, cases)) ==> new <Partial>Function { def apply<OrElse>(params) = `translateMatch('sel match { cases }')` } - class MatchFunTyperBetaReduced(fun: Function, sel: Tree, cases: List[CaseDef], mode: Mode, pt: Type) extends MatchFunTyper(fun, cases, mode, pt) { - override def deriveFormals = - fun.vparams map { p => if(p.tpt.tpe == null) typedType(p.tpt).tpe else p.tpt.tpe } + val typedBlock = typedPos(tree.pos, mode, pt) { + Block(ClassDef(anonClass, NoMods, ListOfNil, members, tree.pos.focus), atPos(tree.pos.focus)(New(anonClass.tpe))) + } - // the only difference from the super class is that we must preserve the names of the parameters - override def mkParams(methodSym: Symbol, formals: List[Type] = deriveFormals) = - (fun.vparams, formals).zipped map { (p, tp) => - methodSym.newValueParameter(p.name, p.pos.focus, SYNTHETIC) setInfo tp + if (typedBlock.isErrorTyped) typedBlock + else // Don't leak implementation details into the type, see SI-6575 + typedPos(tree.pos, mode, pt) { + Typed(typedBlock, TypeTree(typedBlock.tpe baseType PartialFunctionClass)) } - - override def mkSel(params: List[Symbol]) = sel.duplicate } + + /** + * @param fun ... + * @param mode ... + * @param pt ... + * @return ... + */ private def typedFunction(fun: Function, mode: Mode, pt: Type): Tree = { val numVparams = fun.vparams.length if (numVparams > definitions.MaxFunctionArity) @@ -2666,12 +2708,12 @@ trait Typers extends Adaptations with Tags { fun match { case etaExpansion(vparams, fn, args) => silent(_.typed(fn, mode.forFunMode, pt)) filter (_ => context.undetparams.isEmpty) map { fn1 => - // if context,undetparams is not empty, the function was polymorphic, - // so we need the missing arguments to infer its type. See #871 - //println("typing eta "+fun+":"+fn1.tpe+"/"+context.undetparams) - val ftpe = normalize(fn1.tpe) baseType FunctionClass(numVparams) - if (isFunctionType(ftpe) && isFullyDefined(ftpe)) - return typedFunction(fun, mode, ftpe) + // if context,undetparams is not empty, the function was polymorphic, + // so we need the missing arguments to infer its type. See #871 + //println("typing eta "+fun+":"+fn1.tpe+"/"+context.undetparams) + val ftpe = normalize(fn1.tpe) baseType FunctionClass(numVparams) + if (isFunctionType(ftpe) && isFullyDefined(ftpe)) + return typedFunction(fun, mode, ftpe) } case _ => } @@ -2683,14 +2725,17 @@ trait Typers extends Adaptations with Tags { } fun.body match { - // later phase indicates scaladoc is calling (where shit is messed up, I tell you) - // -- so fall back to old patmat, which is more forgiving + // translate `x => x match { <cases> }` : PartialFunction to + // `new PartialFunction { def applyOrElse(x, default) = x match { <cases> } def isDefinedAt(x) = ... }` case Match(sel, cases) if (sel ne EmptyTree) && newPatternMatching && (pt.typeSymbol == PartialFunctionClass) => // go to outer context -- must discard the context that was created for the Function since we're discarding the function // thus, its symbol, which serves as the current context.owner, is not the right owner // you won't know you're using the wrong owner until lambda lift crashes (unless you know better than to use the wrong owner) val outerTyper = newTyper(context.outer) - (new outerTyper.MatchFunTyperBetaReduced(fun, sel, cases, mode, pt)).translated + val p = fun.vparams.head + if (p.tpt.tpe == null) p.tpt setType outerTyper.typedType(p.tpt).tpe + + outerTyper.synthesizePartialFunction(p.name, p.pos, fun.body, mode, pt) case _ => val vparamSyms = fun.vparams map { vparam => enterSym(context, vparam) @@ -2699,8 +2744,8 @@ trait Typers extends Adaptations with Tags { } val vparams = fun.vparams mapConserve typedValDef val formals = vparamSyms map (_.tpe) - val body1 = typed(fun.body, respt) - val restpe = packedType(body1, fun.symbol).deconst.resultType + val body1 = typed(fun.body, respt) + val restpe = packedType(body1, fun.symbol).deconst.resultType val funtpe = appliedType(clazz, formals :+ restpe: _*) treeCopy.Function(fun, vparams, body1) setType funtpe @@ -2723,8 +2768,8 @@ trait Typers extends Adaptations with Tags { templ updateAttachment att.copy(stats = stats1) for (stat <- stats1 if stat.isDef && stat.symbol.isOverridingSymbol) stat.symbol setFlag OVERRIDE - } - } + } + } def typedImport(imp : Import) : Import = (transformed remove imp) match { case Some(imp1: Import) => imp1 @@ -2965,8 +3010,8 @@ trait Typers extends Adaptations with Tags { NothingClass.tpe } val argtypes = args map shapeType - val pre = fun.symbol.tpe.prefix - var sym = fun.symbol filter { alt => + val pre = fun.symbol.tpe.prefix + var sym = fun.symbol filter { alt => // must use pt as expected type, not WildcardType (a tempting quick fix to #2665) // now fixed by using isWeaklyCompatible in exprTypeArgs // TODO: understand why exactly -- some types were not inferred anymore (`ant clean quick.bin` failed) @@ -2980,8 +3025,8 @@ trait Typers extends Adaptations with Tags { isApplicableSafe(context.undetparams, followApply(pre memberType alt), argtypes, pt) } if (sym.isOverloaded) { - // eliminate functions that would result from tupling transforms - // keeps alternatives with repeated params + // eliminate functions that would result from tupling transforms + // keeps alternatives with repeated params val sym1 = sym filter (alt => isApplicableBasedOnArity(pre memberType alt, argtypes.length, varargsStar = false, tuplingAllowed = false) || alt.tpe.params.exists(_.hasDefault) @@ -2999,8 +3044,8 @@ trait Typers extends Adaptations with Tags { case OverloadedType(pre, alts) => def handleOverloaded = { val undetparams = context.extractUndetparams() - val argtpes = new ListBuffer[Type] - val amode = forArgMode(fun, mode) + val argtpes = new ListBuffer[Type] + val amode = forArgMode(fun, mode) val args1 = args map { case arg @ AssignOrNamedArg(Ident(name), rhs) => // named args: only type the righthand sides ("unknown identifier" errors otherwise) @@ -3052,7 +3097,7 @@ trait Typers extends Adaptations with Tags { || checkValidAdaptation(tupledTree, args) ) } orElse { _ => context.undetparams = savedUndetparams ; None } - } + } else None ) @@ -3211,9 +3256,9 @@ trait Typers extends Adaptations with Tags { } else arg1 } val args1 = map2(args, formals)(typedArgToPoly) - if (args1 exists {_.isErrorTyped}) duplErrTree + if (args1 exists { _.isErrorTyped }) duplErrTree else { - debuglog("infer method inst "+fun+", tparams = "+tparams+", args = "+args1.map(_.tpe)+", pt = "+pt+", lobounds = "+tparams.map(_.tpe.bounds.lo)+", parambounds = "+tparams.map(_.info)) //debug + debuglog("infer method inst " + fun + ", tparams = " + tparams + ", args = " + args1.map(_.tpe) + ", pt = " + pt + ", lobounds = " + tparams.map(_.tpe.bounds.lo) + ", parambounds = " + tparams.map(_.info)) //debug // define the undetparams which have been fixed by this param list, replace the corresponding symbols in "fun" // returns those undetparams which have not been instantiated. val undetparams = inferMethodInstance(fun, tparams, args1, pt) @@ -3298,7 +3343,7 @@ trait Typers extends Adaptations with Tags { val resTp = fun1.tpe.finalResultType.normalize val nbSubPats = args.length - val (formals, formalsExpanded) = extractorFormalTypes(resTp, nbSubPats, fun1.symbol) + val (formals, formalsExpanded) = extractorFormalTypes(fun0.pos, resTp, nbSubPats, fun1.symbol) if (formals == null) duplErrorTree(WrongNumberOfArgsError(tree, fun)) else { val args1 = typedArgs(args, mode, formals, formalsExpanded) @@ -3466,15 +3511,18 @@ trait Typers extends Adaptations with Tags { } else if (argss.length > 1) { reportAnnotationError(MultipleArgumentListForAnnotationError(ann)) } else { - val args = - if (argss.head.length == 1 && !isNamed(argss.head.head)) - List(new AssignOrNamedArg(Ident(nme.value), argss.head.head)) - else argss.head val annScope = annType.decls .filter(sym => sym.isMethod && !sym.isConstructor && sym.isJavaDefined) val names = new scala.collection.mutable.HashSet[Symbol] + def hasValue = names exists (_.name == nme.value) names ++= (if (isJava) annScope.iterator else typedFun.tpe.params.iterator) + val args = argss match { + case List(List(arg)) if !isNamed(arg) && hasValue => + List(new AssignOrNamedArg(Ident(nme.value), arg)) + case as :: _ => as + } + val nvPairs = args map { case arg @ AssignOrNamedArg(Ident(name), rhs) => val sym = if (isJava) annScope.lookup(name) @@ -3529,7 +3577,7 @@ trait Typers extends Adaptations with Tags { val Function(arg :: Nil, rhs) = typed(func, mode, funcType) rhs.substituteSymbols(arg.symbol :: Nil, selfsym :: Nil) - } + } def annInfo(t: Tree): AnnotationInfo = t match { case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => @@ -3920,7 +3968,7 @@ trait Typers extends Adaptations with Tags { def wrapErrors(tree: Tree, typeTree: Typer => Tree): Tree = silent(typeTree) orElse (err => DynamicRewriteError(tree, err)) - } + } final def deindentTyping() = context.typingIndentLevel -= 2 final def indentTyping() = context.typingIndentLevel += 2 @@ -3951,7 +3999,7 @@ trait Typers extends Adaptations with Tags { else lookupInOwner(qual.tpe.typeSymbol, name) orElse { NotAMemberError(tree, qual, name) NoSymbol - } + } ) def typedAnnotated(atd: Annotated): Tree = { @@ -4169,7 +4217,8 @@ trait Typers extends Adaptations with Tags { val selector = tree.selector val cases = tree.cases if (selector == EmptyTree) { - if (newPatternMatching && (pt.typeSymbol == PartialFunctionClass)) (new MatchFunTyper(tree, cases, mode, pt)).translated + if (newPatternMatching && (pt.typeSymbol == PartialFunctionClass)) + synthesizePartialFunction(newTermName(context.unit.fresh.newName("x")), tree.pos, tree, mode, pt) else { val arity = if (isFunctionType(pt)) pt.normalize.typeArgs.length - 1 else 1 val params = for (i <- List.range(0, arity)) yield @@ -4361,7 +4410,7 @@ trait Typers extends Adaptations with Tags { } silent(_.doTypedApply(tree, fun, args, mode, pt)) orElse onError - } + } def normalTypedApply(tree: Tree, fun: Tree, args: List[Tree]) = { val stableApplication = (fun.symbol ne null) && fun.symbol.isMethod && fun.symbol.isStable @@ -4425,7 +4474,7 @@ trait Typers extends Adaptations with Tags { } } - // convert new Array[T](len) to evidence[ClassTag[T]].newArray(len) + // convert new Array[T](len) to evidence[ClassTag[T]].newArray(len) // convert new Array^N[T](len) for N > 1 to evidence[ClassTag[Array[...Array[T]...]]].newArray(len) // where Array HK gets applied (N-1) times object ArrayInstantiation { @@ -4590,15 +4639,15 @@ trait Typers extends Adaptations with Tags { def handleMissing: Tree = { def errorTree = tree match { case _ if !forInteractive => tree - case Select(_, _) => treeCopy.Select(tree, qual, name) - case SelectFromTypeTree(_, _) => treeCopy.SelectFromTypeTree(tree, qual, name) - } + case Select(_, _) => treeCopy.Select(tree, qual, name) + case SelectFromTypeTree(_, _) => treeCopy.SelectFromTypeTree(tree, qual, name) + } def asTypeSelection = ( if (context.owner.enclosingTopLevelClass.isJavaDefined && name.isTypeName) { atPos(tree.pos)(gen.convertToSelectFromType(qual, name)) match { case EmptyTree => None case tree1 => Some(typed1(tree1, mode, pt)) - } + } } else None ) @@ -4624,7 +4673,7 @@ trait Typers extends Adaptations with Tags { } else { val tree1 = tree match { - case Select(_, _) => treeCopy.Select(tree, qual, name) + case Select(_, _) => treeCopy.Select(tree, qual, name) case SelectFromTypeTree(_, _) => treeCopy.SelectFromTypeTree(tree, qual, name) } val (result, accessibleError) = silent(_.makeAccessible(tree1, sym, qual.tpe, qual)) match { @@ -4694,15 +4743,15 @@ trait Typers extends Adaptations with Tags { val tree1 = // temporarily use `filter` and an alternative for `withFilter` if (name == nme.withFilter) silent(_ => typedSelect(tree, qual1, name)) orElse { _ => - silent(_ => typed1(Select(qual1, nme.filter) setPos tree.pos, mode, pt)) match { - case SilentResultValue(result2) => - unit.deprecationWarning( - tree.pos, "`withFilter' method does not yet exist on " + qual1.tpe.widen + - ", using `filter' method instead") - result2 - case SilentTypeError(err) => - WithFilterError(tree, err) - } + silent(_ => typed1(Select(qual1, nme.filter) setPos tree.pos, mode, pt)) match { + case SilentResultValue(result2) => + unit.deprecationWarning( + tree.pos, "`withFilter' method does not yet exist on " + qual1.tpe.widen + + ", using `filter' method instead") + result2 + case SilentTypeError(err) => + WithFilterError(tree, err) + } } else typedSelect(tree, qual1, name) @@ -4748,7 +4797,7 @@ trait Typers extends Adaptations with Tags { setError(tree) } - // ignore current variable scope in patterns to enforce linearity + // ignore current variable scope in patterns to enforce linearity val startContext = if (mode.inNone(PATTERNmode | TYPEPATmode)) context else context.outer val nameLookup = tree.symbol match { case NoSymbol => startContext.lookupSymbol(name, qualifies) @@ -4762,25 +4811,25 @@ trait Typers extends Adaptations with Tags { inEmptyPackage orElse lookupInRoot(name) match { case NoSymbol => issue(SymbolNotFoundError(tree, name, context.owner, startContext)) case sym => typed1(tree setSymbol sym, mode, pt) - } + } case LookupSucceeded(qual, sym) => (// this -> Foo.this if (sym.isThisSym) typed1(This(sym.owner) setPos tree.pos, mode, pt) - // Inferring classOf type parameter from expected type. Otherwise an - // actual call to the stubbed classOf method is generated, returning null. + // Inferring classOf type parameter from expected type. Otherwise an + // actual call to the stubbed classOf method is generated, returning null. else if (isPredefMemberNamed(sym, nme.classOf) && pt.typeSymbol == ClassClass && pt.typeArgs.nonEmpty) - typedClassOf(tree, TypeTree(pt.typeArgs.head)) - else { + typedClassOf(tree, TypeTree(pt.typeArgs.head)) + else { val pre1 = if (sym.owner.isPackageClass) sym.owner.thisType else if (qual == EmptyTree) NoPrefix else qual.tpe val tree1 = if (qual == EmptyTree) tree else atPos(tree.pos)(Select(atPos(tree.pos.focusStart)(qual), name)) val (tree2, pre2) = makeAccessible(tree1, sym, pre1, qual) - // SI-5967 Important to replace param type A* with Seq[A] when seen from from a reference, to avoid - // inference errors in pattern matching. + // SI-5967 Important to replace param type A* with Seq[A] when seen from from a reference, to avoid + // inference errors in pattern matching. stabilize(tree2, pre2, mode, pt) modifyType dropIllegalStarTypes }) setAttachments tree.attachments + } } - } def typedIdentOrWildcard(tree: Ident) = { val name = tree.name @@ -4916,7 +4965,7 @@ trait Typers extends Adaptations with Tags { def typedUnApply(tree: UnApply) = { val fun1 = typed(tree.fun) - val tpes = formalTypes(unapplyTypeList(tree.fun.symbol, fun1.tpe, tree.args.length), tree.args.length) + val tpes = formalTypes(unapplyTypeList(tree.fun.pos, tree.fun.symbol, fun1.tpe, tree.args.length), tree.args.length) val args1 = map2(tree.args, tpes)(typedPattern) treeCopy.UnApply(tree, fun1, args1) setType pt } @@ -4931,7 +4980,7 @@ trait Typers extends Adaptations with Tags { s"""|This catches all Throwables, which often has undesirable consequences. |If intentional, use `case $name : Throwable` to clear this warning.""".stripMargin ) - } + } val finalizer1 = if (tree.finalizer.isEmpty) tree.finalizer @@ -5393,8 +5442,8 @@ trait Typers extends Adaptations with Tags { case Some(tree1) => transformed -= tree; tree1 case None => typed(tree, mode, pt) } + } } -} object TypersStats { import scala.reflect.internal.TypesStats._ diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index 3e76da4fdc..e95184ac6d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -33,12 +33,12 @@ trait Unapplies extends ast.TreeDSL /** returns type list for return type of the extraction * @see extractorFormalTypes */ - def unapplyTypeList(ufn: Symbol, ufntpe: Type, nbSubPats: Int) = { + def unapplyTypeList(pos: Position, ufn: Symbol, ufntpe: Type, nbSubPats: Int) = { assert(ufn.isMethod, ufn) //Console.println("utl "+ufntpe+" "+ufntpe.typeSymbol) ufn.name match { case nme.unapply | nme.unapplySeq => - val (formals, _) = extractorFormalTypes(unapplyUnwrap(ufntpe), nbSubPats, ufn) + val (formals, _) = extractorFormalTypes(pos, unapplyUnwrap(ufntpe), nbSubPats, ufn) if (formals == null) throw new TypeError(s"$ufn of type $ufntpe cannot extract $nbSubPats sub-patterns") else formals case _ => throw new TypeError(ufn+" is not an unapply or unapplySeq") diff --git a/src/forkjoin/scala/concurrent/forkjoin/ForkJoinPool.java b/src/forkjoin/scala/concurrent/forkjoin/ForkJoinPool.java index 65654be69b..8dbca6da4b 100644 --- a/src/forkjoin/scala/concurrent/forkjoin/ForkJoinPool.java +++ b/src/forkjoin/scala/concurrent/forkjoin/ForkJoinPool.java @@ -1372,7 +1372,7 @@ public class ForkJoinPool extends AbstractExecutorService { } if (ex != null) // rethrow - U.throwException(ex); + ForkJoinTask.rethrow(ex); } diff --git a/src/forkjoin/scala/concurrent/forkjoin/ForkJoinTask.java b/src/forkjoin/scala/concurrent/forkjoin/ForkJoinTask.java index 15c60118b3..839fd26b39 100644 --- a/src/forkjoin/scala/concurrent/forkjoin/ForkJoinTask.java +++ b/src/forkjoin/scala/concurrent/forkjoin/ForkJoinTask.java @@ -595,6 +595,30 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { } } } + + /** + * A version of "sneaky throw" to relay exceptions + */ + static void rethrow(final Throwable ex) { + if (ex != null) { + if (ex instanceof Error) + throw (Error)ex; + if (ex instanceof RuntimeException) + throw (RuntimeException)ex; + ForkJoinTask.<RuntimeException>uncheckedThrow(ex); + } + } + + /** + * The sneaky part of sneaky throw, relying on generics + * limitations to evade compiler complaints about rethrowing + * unchecked exceptions + */ + @SuppressWarnings("unchecked") static <T extends Throwable> + void uncheckedThrow(Throwable t) throws T { + if (t != null) + throw (T)t; // rely on vacuous cast + } /** * Throws exception, if any, associated with the given status. @@ -604,7 +628,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { (s == EXCEPTIONAL) ? getThrowableException() : null); if (ex != null) - U.throwException(ex); + ForkJoinTask.rethrow(ex); } // public methods @@ -742,7 +766,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { } } if (ex != null) - U.throwException(ex); + ForkJoinTask.rethrow(ex); } /** @@ -799,7 +823,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { } } if (ex != null) - U.throwException(ex); + ForkJoinTask.rethrow(ex); return tasks; } diff --git a/src/library/scala/collection/TraversableOnce.scala b/src/library/scala/collection/TraversableOnce.scala index 82cf1d1198..2de0fdd38c 100644 --- a/src/library/scala/collection/TraversableOnce.scala +++ b/src/library/scala/collection/TraversableOnce.scala @@ -146,6 +146,20 @@ trait TraversableOnce[+A] extends Any with GenTraversableOnce[A] { def foldRight[B](z: B)(op: (A, B) => B): B = reversed.foldLeft(z)((x, y) => op(y, x)) + /** Applies a binary operator to all elements of this $coll, + * going left to right. + * $willNotTerminateInf + * $orderDependentFold + * + * @param op the binary operator. + * @tparam B the result type of the binary operator. + * @return the result of inserting `op` between consecutive elements of this $coll, + * going left to right: + * {{{ + * op( op( ... op(x_1, x_2) ..., x_{n-1}), x_n) + * }}} + * where `x,,1,,, ..., x,,n,,` are the elements of this $coll. + * @throws `UnsupportedOperationException` if this $coll is empty. */ def reduceLeft[B >: A](op: (B, A) => B): B = { if (isEmpty) throw new UnsupportedOperationException("empty.reduceLeft") diff --git a/src/library/scala/collection/generic/IsTraversableLike.scala b/src/library/scala/collection/generic/IsTraversableLike.scala index b45279229b..c70772d8f9 100644 --- a/src/library/scala/collection/generic/IsTraversableLike.scala +++ b/src/library/scala/collection/generic/IsTraversableLike.scala @@ -9,26 +9,97 @@ package scala.collection package generic -/** Type class witnessing that a collection representation type `Repr` has - * elements of type `A` and has a conversion to `GenTraversableLike[A, Repr]`. +/** A trait which can be used to avoid code duplication when defining extension + * methods that should be applicable both to existing Scala collections (i.e., + * types extending `GenTraversableLike`) as well as other (potentially user-defined) + * types that could be converted to a Scala collection type. This trait + * makes it possible to treat Scala collections and types that can be implicitly + * converted to a collection type uniformly. For example, one can provide + * extension methods that work both on collection types and on `String`s (`String`s + * do not extend `GenTraversableLike`, but can be converted to `GenTraversableLike`) * - * This type enables simple enrichment of `GenTraversable`s with extension - * methods which can make full use of the mechanics of the Scala collections - * framework in their implementation. + * `IsTraversable` provides two members: + * + * 1. type member `A`, which represents the element type of the target `GenTraversableLike[A, Repr]` + * 1. value member `conversion`, which provides a way to convert between the type we wish to add extension methods to, `Repr`, and `GenTraversableLike[A, Repr]`. + * + * ===Usage=== + * + * One must provide `IsTraversableLike` as an implicit parameter type of an implicit + * conversion. Its usage is shown below. Our objective in the following example + * is to provide a generic extension method `mapReduce` to any type that extends + * or can be converted to `GenTraversableLike`. In our example, this includes + * `String`. * - * Example usage, * {{{ - * class FilterMapImpl[A, Repr](val r: GenTraversableLike[A, Repr]) { - * final def filterMap[B, That](f: A => Option[B])(implicit cbf: CanBuildFrom[Repr, B, That]): That = - * r.flatMap(f(_).toSeq) + * import scala.collection.GenTraversableLike + * import scala.collection.generic.IsTraversableLike + * + * class ExtensionMethods[A, Repr](coll: GenTraversableLike[A, Repr]) { + * def mapReduce[B](mapper: A => B)(reducer: (B, B) => B): B = { + * val iter = coll.toIterator + * var res = mapper(iter.next()) + * while (iter.hasNext) + * res = reducer(res, mapper(iter.next())) + * res + * } * } - * implicit def filterMap[Repr, A](r: Repr)(implicit fr: IsTraversableOnce[Repr]): FilterMapImpl[fr.A,Repr] = - * new FilterMapImpl(fr.conversion(r)) * - * val l = List(1, 2, 3, 4, 5) - * List(1, 2, 3, 4, 5) filterMap (i => if(i % 2 == 0) Some(i) else None) - * // == List(2, 4) - * }}} + * implicit def withExtensions[Repr](coll: Repr)(implicit traversable: IsTraversableLike[Repr]) = + * new ExtensionMethods(traversable.conversion(coll)) + * + * // See it in action! + * List(1, 2, 3).mapReduce(_ * 2)(_ + _) // res0: Int = 12 + * "Yeah, well, you know, that's just, like, your opinion, man.".mapReduce(x => 1)(_ + _) // res1: Int = 59 + *}}} + * + * Here, we begin by creating a class `ExtensionMethods` which contains our + * `mapReduce` extension method. Note that `ExtensionMethods` takes a constructor + * argument `coll` of type `GenTraversableLike[A, Repr]`, where `A` represents the + * element type and `Repr` represents (typically) the collection type. The + * implementation of `mapReduce` itself is straightforward. + * + * The interesting bit is the implicit conversion `withExtensions`, which + * returns an instance of `ExtensionMethods`. This implicit conversion can + * only be applied if there is an implicit value `traversable` of type + * `IsTraversableLike[Repr]` in scope. Since `IsTraversableLike` provides + * value member `conversion`, which gives us a way to convert between whatever + * type we wish to add an extension method to (in this case, `Repr`) and + * `GenTraversableLike[A, Repr]`, we can now convert `coll` from type `Repr` + * to `GenTraversableLike[A, Repr]`. This allows us to create an instance of + * the `ExtensionMethods` class, which we pass our new + * `GenTraversableLike[A, Repr]` to. + * + * When the `mapReduce` method is called on some type of which it is not + * a member, implicit search is triggered. Because implicit conversion + * `withExtensions` is generic, it will be applied as long as an implicit + * value of type `IsTraversableLike[Repr]` can be found. Given that + * `IsTraversableLike` contains implicit members that return values of type + * `IsTraversableLike`, this requirement is typically satisfied, and the chain + * of interactions described in the previous paragraph is set into action. + * (See the `IsTraversableLike` companion object, which contains a precise + * specification of the available implicits.) + * + * ''Note'': Currently, it's not possible to combine the implicit conversion and + * the class with the extension methods into an implicit class due to + * limitations of type inference. + * + * ===Implementing `IsTraversableLike` for New Types=== + * + * One must simply provide an implicit value of type `IsTraversableLike` + * specific to the new type, or an implicit conversion which returns an + * instance of `IsTraversableLike` specific to the new type. + * + * Below is an example of an implementation of the `IsTraversableLike` trait + * where the `Repr` type is `String`. + * + *{{{ + * implicit val stringRepr: IsTraversableLike[String] { type A = Char } = + * new IsTraversableLike[String] { + * type A = Char + * val conversion = implicitly[String => GenTraversableLike[Char, String]] + * } + *}}} * * @author Miles Sabin * @author J. Suereth diff --git a/src/manual/scala/man1/scaladoc.scala b/src/manual/scala/man1/scaladoc.scala index 34c58b6b8e..1737c5efa0 100644 --- a/src/manual/scala/man1/scaladoc.scala +++ b/src/manual/scala/man1/scaladoc.scala @@ -77,8 +77,8 @@ object scaladoc extends Command { CmdOption("doc-source-url", Argument("url")), "Define a URL to be concatenated with source locations for link to source files."), Definition( - CmdOption("doc-external-uris", Argument("external-doc")), - "Define comma-separated list of file://classpath_entry_path#doc_URL URIs for linking to external dependencies."))), + CmdOption("doc-external-doc", Argument("external-doc")), + "Define a comma-separated list of classpath_entry_path#doc_URL pairs describing external dependencies."))), Section("Compiler Options", DefinitionList( diff --git a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala index d5ed9dab5b..3ea8cff989 100644 --- a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala +++ b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala @@ -47,4 +47,6 @@ abstract class MutableSettings extends AbsSettings { def XnoPatmatAnalysis: BooleanSetting def XfullLubs: BooleanSetting def breakCycles: BooleanSetting + def companionsInPkgObjs: BooleanSetting + } diff --git a/src/reflect/scala/reflect/runtime/Settings.scala b/src/reflect/scala/reflect/runtime/Settings.scala index ba524f4df2..9472cefbbf 100644 --- a/src/reflect/scala/reflect/runtime/Settings.scala +++ b/src/reflect/scala/reflect/runtime/Settings.scala @@ -43,6 +43,7 @@ private[reflect] class Settings extends MutableSettings { val uniqid = new BooleanSetting(false) val verbose = new BooleanSetting(false) val breakCycles = new BooleanSetting(false) + val companionsInPkgObjs = new BooleanSetting(false) val Yrecursion = new IntSetting(0) val maxClassfileName = new IntSetting(255) |