diff options
47 files changed, 597 insertions, 188 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index 5cb43575b8..96146b7343 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -253,6 +253,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/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/matching/Patterns.scala b/src/compiler/scala/tools/nsc/matching/Patterns.scala index 99b72fa26e..f116a7c4c7 100644 --- a/src/compiler/scala/tools/nsc/matching/Patterns.scala +++ b/src/compiler/scala/tools/nsc/matching/Patterns.scala @@ -402,7 +402,7 @@ trait Patterns extends ast.TreeDSL { case _ => toPats(args) } - def resTypes = analyzer.unapplyTypeList(unfn.symbol, unfn.tpe, args.length) + def resTypes = analyzer.unapplyTypeList(unfn.pos, unfn.symbol, unfn.tpe, args.length) def resTypesString = resTypes match { case Nil => "Boolean" case xs => xs.mkString(", ") diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index cfc7f14210..b820d10ddc 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -85,7 +85,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.)") @@ -174,6 +175,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 55230f9685..9908bd689e 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -613,7 +613,7 @@ 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) diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 10003723fd..ec3a0a0ef7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -956,7 +956,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 fc61aec0e3..2c2aa03d24 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -73,7 +73,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 @@ -87,11 +87,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 diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index fa8aff5cdd..d8cfd5a765 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -1478,14 +1478,9 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL 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 def and(a: Tree, b: Tree): Tree = a AND b - // 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(t: Tree, tp: Type): Tree = if (t.tpe != NoType && t.isTyped && typesConform(t.tpe, tp)) t else mkCast(t, tp) - def _asInstanceOf(b: Symbol, tp: Type): Tree = if (typesConform(b.info, tp)) REF(b) else mkCast(REF(b), tp) + def _asInstanceOf(t: Tree, tp: Type): Tree = if (t.tpe != NoType && t.isTyped && typesConform(t.tpe, tp)) t else gen.mkCastPreservingAnnotations(t, 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) // if (typesConform(b.info, tpX)) { patmatDebug("warning: emitted spurious isInstanceOf: "+(b, tp)); TRUE } @@ -3527,7 +3522,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL override def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type, matchFailGenOverride: Option[Tree => Tree], unchecked: Boolean): Option[Tree] = { import CODE._ val regularSwitchMaker = new RegularSwitchMaker(scrutSym, matchFailGenOverride, unchecked) // TODO: if patterns allow switch but the type of the scrutinee doesn't, cast (type-test) the scrutinee to the corresponding switchable type and switch on the result - if (regularSwitchMaker.switchableTpe(scrutSym.tpe)) { + if (regularSwitchMaker.switchableTpe(scrutSym.tpe.dealias)) { // TODO: switch to dealiasWiden in 2.11 val caseDefsWithDefault = regularSwitchMaker(cases map {c => (scrutSym, c)}, pt) if (caseDefsWithDefault isEmpty) None // not worth emitting a switch. else { diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 386eec207a..b4c5365516 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -996,6 +996,10 @@ trait Typers extends Modes with Adaptations with Tags { object variantToSkolem extends VariantTypeMap { def apply(tp: Type) = mapOver(tp) match { case TypeRef(NoPrefix, tpSym, Nil) if variance != 0 && 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 == 1) 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) @@ -1018,7 +1022,7 @@ trait Typers extends Modes with 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 @@ -1891,7 +1895,33 @@ trait Typers extends Modes with 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 @@ -2526,11 +2556,7 @@ trait Typers extends Modes with Adaptations with Tags { } // body1 = checkNoEscaping.locals(context.scope, pt, body1) - val treeWithSkolems = treeCopy.CaseDef(cdef, pat1, guard1, body1) setType body1.tpe - - new TypeMapTreeSubstituter(deskolemizeGADTSkolems).traverse(treeWithSkolems) - - treeWithSkolems // now without skolems, actually + treeCopy.CaseDef(cdef, pat1, guard1, body1) setType body1.tpe } // undo adaptConstrPattern's evil deeds, as they confuse the old pattern matcher @@ -2565,7 +2591,10 @@ trait Typers extends Modes with Adaptations with Tags { val casesAdapted = if (!needAdapt) casesTyped else casesTyped map (adaptCase(_, mode, resTp)) - treeCopy.Match(tree, selector1, casesAdapted) setType resTp + val matchTyped = treeCopy.Match(tree, selector1, casesAdapted) setType resTp + if (!newPatternMatching) // TODO: remove this in 2.11 -- only needed for old pattern matcher + new TypeMapTreeSubstituter(deskolemizeGADTSkolems).traverse(matchTyped) + matchTyped } // match has been typed -- virtualize it if we're feeling experimental @@ -2588,174 +2617,177 @@ trait Typers extends Modes with 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: Int, pt0: 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 pt = deskolemizeGADTSkolems(pt0) - 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 - - private val isPartial = pt.typeSymbol == PartialFunctionClass - assert(isPartial) - - private val anonClass = context.owner.newAnonymousFunctionClass(tree.pos) - private val funThis = This(anonClass) - - anonClass addAnnotation AnnotationInfo(SerialVersionUIDAttr.tpe, List(Literal(Constant(0))), List()) - - def deriveFormals = - if (targs.isEmpty) Nil - else targs.init - - def mkParams(methodSym: Symbol, formals: List[Type] = deriveFormals) = - if (formals.isEmpty || !formals.forall(isFullyDefined)) { MissingParameterTypeAnonMatchError(tree, pt); Nil } - else methodSym newSyntheticValueParams formals - - 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) } - } + /** 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: Int, pt0: Type): Tree = { + assert(pt0.typeSymbol == PartialFunctionClass, s"PartialFunction synthesis for match in $tree requires PartialFunction expected type, but got $pt0.") - import CODE._ + val pt = deskolemizeGADTSkolems(pt0) + val targs = pt.normalize.typeArgs - // 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_typed)).duplicate.asInstanceOf[CaseDef]) else Nil - // println("casesTrue "+ casesTrue) - def parentsPartial(targs: List[Type]) = addSerializable(appliedType(AbstractPartialFunctionClass.typeConstructor, targs)) + // 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) + } - 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) + // NOTE: resTp still might not be fully defined + val argTp :: resTp :: Nil = targs - if (selector eq EmptyTree) EmptyTree - else { - methodSym setInfoAndEnter MethodType(paramSyms, AnyClass.tpe) + // 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) - 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 anonClass = (context.owner + newAnonymousFunctionClass tree.pos + addAnnotation AnnotationInfo(SerialVersionUIDAttr.tpe, List(Literal(Constant(0))), List())) - val match_ = methodBodyTyper.typedMatch(gen.mkUnchecked(selector), cases, mode, ptRes) - val resTp = match_.tpe + import CODE._ - 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) + val Match(sel, cases) = tree - DefDef(methodSym, methodBodyTyper.virtualizedMatch(match_, mode, resTp)) - } - } - - // def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = + // 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_typed)).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 = { - // 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) // 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) - - 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 A1 = methodSym newTypeParameter (newTypeName("A1")) setInfo TypeBounds.upper(argTp) + val x = mkParam(methodSym, A1.tpe) - val paramSyms = List(x, default) - methodSym setInfoAndEnter polyType(List(A1, B1), MethodType(paramSyms, B1.tpe)) + // 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 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 paramSyms = List(x, default) + methodSym setInfo polyType(List(A1, B1), MethodType(paramSyms, B1.tpe)) - val match_ = methodBodyTyper.typedMatch(gen.mkUnchecked(selector), cases, mode, ptRes) - val resTp = match_.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 _) - 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 match_ = methodBodyTyper.typedMatch(selector, cases, mode, resTp) - match_ setType B1.tpe + val matchResTp = match_.tpe + B1 setInfo TypeBounds.lower(matchResTp) // patch info - // 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) + match_ setType B1.tpe - DefDef(methodSym, body) - } + // 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 isDefinedAt(x: $argTp): Boolean = ${`$selector match { $casesTrue ` updateAttachment DefaultOverrideMatchAttachment(FALSE_typed)}` def isDefinedAtMethod = { val methodSym = anonClass.newMethod(nme.isDefinedAt, tree.pos.makeTransparent, FINAL) - val paramSyms = mkParams(methodSym) - val selector = mkSel(paramSyms) + val paramSym = mkParam(methodSym) - 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) + 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) - val match_ = methodBodyTyper.typedMatch(gen.mkUnchecked(selector), casesTrue, mode, BooleanClass.tpe) - val body = methodBodyTyper.virtualizedMatch(match_ updateAttachment DefaultOverrideMatchAttachment(FALSE_typed), mode, BooleanClass.tpe) + val match_ = methodBodyTyper.typedMatch(selector, casesTrue, mode, BooleanClass.tpe) - DefDef(methodSym, body) - } + val matchWithDefault = match_ updateAttachment DefaultOverrideMatchAttachment(FALSE_typed) + DefDef(methodSym, methodBodyTyper.virtualizedMatch(matchWithDefault, mode, BooleanClass.tpe)) } - 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) + // 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) - 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 - } - } + methodSym setInfo MethodType(List(paramSym), 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) + methodBodyTyper.context.scope enter paramSym + + val match_ = methodBodyTyper.typedMatch(selector, cases, mode, resTp) + + val matchResTp = match_.tpe + methodSym setInfo MethodType(List(paramSym), matchResTp) // patch info + + (DefDef(methodSym, methodBodyTyper.virtualizedMatch(match_, mode, matchResTp)), matchResTp) + } + + def parents(resTp: Type) = addSerializable(appliedType(AbstractPartialFunctionClass.typeConstructor, List(argTp, resTp))) - // 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: Int, 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 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) - // 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 + // 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 } - override def mkSel(params: List[Symbol]) = sel.duplicate + // patch info to the class's definitive info + anonClass setInfo ClassInfoType(parents(matchResTp), newScope, anonClass) + List(applyMeth, isDefinedAtMethod) + } + + members foreach (m => anonClass.info.decls enter m.symbol) + + val typedBlock = typedPos(tree.pos, mode, pt) { + Block(ClassDef(anonClass, NoMods, ListOfNil, members, tree.pos.focus), atPos(tree.pos.focus)(New(anonClass.tpe))) + } + + 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)) + } } + /** * @param fun ... * @param mode ... @@ -2807,14 +2839,17 @@ trait Typers extends Modes with 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) @@ -3465,7 +3500,7 @@ trait Typers extends Modes with 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) @@ -3637,15 +3672,18 @@ trait Typers extends Modes with 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) @@ -4347,7 +4385,8 @@ trait Typers extends Modes with 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 @@ -5311,7 +5350,7 @@ trait Typers extends Modes with 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 } diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index a34d7389bf..5782d7bbca 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -34,12 +34,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 d53d000e90..a448ac2c09 100644 --- a/src/library/scala/collection/TraversableOnce.scala +++ b/src/library/scala/collection/TraversableOnce.scala @@ -148,6 +148,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/library/scala/collection/immutable/Range.scala b/src/library/scala/collection/immutable/Range.scala index 802e16605d..02c10700b1 100644 --- a/src/library/scala/collection/immutable/Range.scala +++ b/src/library/scala/collection/immutable/Range.scala @@ -77,6 +77,7 @@ extends scala.collection.AbstractSeq[Int] final val terminalElement = start + numRangeElements * step override def last = if (isEmpty) Nil.last else lastElement + override def head = if (isEmpty) Nil.head else start override def min[A1 >: Int](implicit ord: Ordering[A1]): Int = if (ord eq Ordering.Int) { diff --git a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala index 81368df7a6..ec3501d5bc 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 XoldPatmat: BooleanSetting def XnoPatmatAnalysis: BooleanSetting def XfullLubs: BooleanSetting + def companionsInPkgObjs: BooleanSetting + } diff --git a/src/reflect/scala/reflect/runtime/Settings.scala b/src/reflect/scala/reflect/runtime/Settings.scala index 0e0cf3fc40..2d5b76f094 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 printtypes = new BooleanSetting(false) val uniqid = new BooleanSetting(false) val verbose = new BooleanSetting(false) + val companionsInPkgObjs = new BooleanSetting(false) val Yrecursion = new IntSetting(0) val maxClassfileName = new IntSetting(255) diff --git a/test/files/neg/t5189_inferred.check b/test/files/neg/t5189_inferred.check new file mode 100644 index 0000000000..9cc5dcc242 --- /dev/null +++ b/test/files/neg/t5189_inferred.check @@ -0,0 +1,6 @@ +t5189_inferred.scala:7: error: type mismatch; + found : scala.collection.immutable.Nil.type + required: ?A1 where type ?A1 + f(Invariant(arr): Covariant[Any])(0) = Nil + ^ +one error found diff --git a/test/files/neg/t5189_inferred.scala b/test/files/neg/t5189_inferred.scala new file mode 100644 index 0000000000..e4e8765445 --- /dev/null +++ b/test/files/neg/t5189_inferred.scala @@ -0,0 +1,8 @@ +trait Covariant[+A] +case class Invariant[A](xs: Array[A]) extends Covariant[A] + +class Test { + val arr = Array("abc") + def f[A](v: Covariant[A]) /*inferred!*/ = v match { case Invariant(xs) => xs } + f(Invariant(arr): Covariant[Any])(0) = Nil +}
\ No newline at end of file diff --git a/test/files/neg/t5340.check b/test/files/neg/t5340.check new file mode 100644 index 0000000000..2de19293c4 --- /dev/null +++ b/test/files/neg/t5340.check @@ -0,0 +1,6 @@ +t5340.scala:17: error: type mismatch; + found : MyApp.r.E + required: MyApp.s.E + println(b: s.E) + ^ +one error found diff --git a/test/files/neg/t5340.scala b/test/files/neg/t5340.scala new file mode 100644 index 0000000000..b283f13338 --- /dev/null +++ b/test/files/neg/t5340.scala @@ -0,0 +1,29 @@ +class Poly { + class E + object E { + implicit def conv(value: Any): E = sys.error("") + } +} + +object MyApp { + val r: Poly = sys.error("") + val s: Poly = sys.error("") + val b: r.E = sys.error("") + + // okay + s.E.conv(b): s.E + + // compilation fails with error below + println(b: s.E) + + // amb prefix: MyApp.s.type#class E MyApp.r.type#class E + // amb prefix: MyApp.s.type#class E MyApp.r.type#class E + // ../test/pending/run/t5310.scala:17: error: type mismatch; + // found : MyApp.r.E + // required: MyApp.s.E + // println(b: s.E) + // ^ + + // The type error is as expected, but the `amb prefix` should be logged, + // rather than printed to standard out. +} diff --git a/test/files/neg/t5440.check b/test/files/neg/t5440.check new file mode 100644 index 0000000000..a862350a05 --- /dev/null +++ b/test/files/neg/t5440.check @@ -0,0 +1,5 @@ +t5440.scala:3: error: match may not be exhaustive. +It would fail on the following inputs: (List(_), Nil), (Nil, List(_)) + (list1, list2) match { + ^ +one error found diff --git a/test/files/neg/t5440.flags b/test/files/neg/t5440.flags new file mode 100644 index 0000000000..e8fb65d50c --- /dev/null +++ b/test/files/neg/t5440.flags @@ -0,0 +1 @@ +-Xfatal-warnings
\ No newline at end of file diff --git a/test/files/neg/t5440.scala b/test/files/neg/t5440.scala new file mode 100644 index 0000000000..d9cf5d6252 --- /dev/null +++ b/test/files/neg/t5440.scala @@ -0,0 +1,7 @@ +object Test { + def merge(list1: List[Long], list2: List[Long]): Boolean = + (list1, list2) match { + case (hd1::_, hd2::_) => true + case (Nil, Nil) => true + } +}
\ No newline at end of file diff --git a/test/files/neg/t5954.check b/test/files/neg/t5954.check new file mode 100644 index 0000000000..3ca47cd430 --- /dev/null +++ b/test/files/neg/t5954.check @@ -0,0 +1,16 @@ +t5954.scala:36: error: implementation restriction: package object A cannot contain case class D. Instead, class D should be placed directly in package A. + case class D() + ^ +t5954.scala:35: error: implementation restriction: package object A cannot contain companion object C. Instead, object C should be placed directly in package A. + object C + ^ +t5954.scala:34: error: implementation restriction: package object A cannot contain companion trait C. Instead, trait C should be placed directly in package A. + trait C + ^ +t5954.scala:33: error: implementation restriction: package object A cannot contain companion object B. Instead, object B should be placed directly in package A. + object B + ^ +t5954.scala:32: error: implementation restriction: package object A cannot contain companion class B. Instead, class B should be placed directly in package A. + class B + ^ +5 errors found diff --git a/test/files/neg/t5954.scala b/test/files/neg/t5954.scala new file mode 100644 index 0000000000..9e6f5392c7 --- /dev/null +++ b/test/files/neg/t5954.scala @@ -0,0 +1,46 @@ +// if you ever think you've fixed the underlying reason for the implementation restrictions +// imposed by SI-5954, then here's a test that should pass with two "succes"es +// +//import scala.tools.partest._ +// +//object Test extends DirectTest { +// def code = ??? +// +// def problemCode = """ +// package object A { +// class B +// object B +// case class C() +// } +// """ +// +// def compileProblemCode() = { +// val classpath = List(sys.props("partest.lib"), testOutput.path) mkString sys.props("path.separator") +// compileString(newCompiler("-cp", classpath, "-d", testOutput.path))(problemCode) +// } +// +// def show() : Unit = { +// for (i <- 0 until 2) { +// compileProblemCode() +// println(s"success ${i + 1}") +// } +// } +//} + +package object A { + // these should be prevented by the implementation restriction + class B + object B + trait C + object C + case class D() + // all the rest of these should be ok + class E + object F + val g = "omg" + var h = "wtf" + def i = "lol" + type j = String + class K(val k : Int) extends AnyVal + implicit class L(val l : Int) +} diff --git a/test/files/neg/t6082.check b/test/files/neg/t6082.check new file mode 100644 index 0000000000..b68de5ce08 --- /dev/null +++ b/test/files/neg/t6082.check @@ -0,0 +1,13 @@ +t6082.scala:1: warning: Implementation restriction: subclassing Classfile does not +make your annotation visible at runtime. If that is what +you want, you must write the annotation class in Java. +class annot(notValue: String) extends annotation.ClassfileAnnotation + ^ +t6082.scala:2: error: classfile annotation arguments have to be supplied as named arguments +@annot("") class C + ^ +t6082.scala:2: error: annotation annot is missing argument notValue +@annot("") class C + ^ +one warning found +two errors found diff --git a/test/files/neg/t6082.scala b/test/files/neg/t6082.scala new file mode 100644 index 0000000000..30de91a4c9 --- /dev/null +++ b/test/files/neg/t6082.scala @@ -0,0 +1,2 @@ +class annot(notValue: String) extends annotation.ClassfileAnnotation +@annot("") class C
\ No newline at end of file diff --git a/test/files/neg/t6675-old-patmat.check b/test/files/neg/t6675-old-patmat.check new file mode 100644 index 0000000000..bc3920da06 --- /dev/null +++ b/test/files/neg/t6675-old-patmat.check @@ -0,0 +1,4 @@ +t6675-old-patmat.scala:10: error: extractor pattern binds a single value to a Product3 of type (Int, Int, Int) + "" match { case X(b) => b } // should warn under -Xlint. Not an error because of SI-6111 + ^ +one error found diff --git a/test/files/neg/t6675-old-patmat.flags b/test/files/neg/t6675-old-patmat.flags new file mode 100644 index 0000000000..604de64a62 --- /dev/null +++ b/test/files/neg/t6675-old-patmat.flags @@ -0,0 +1 @@ +-Xlint -Xfatal-warnings -Xoldpatmat
\ No newline at end of file diff --git a/test/files/neg/t6675-old-patmat.scala b/test/files/neg/t6675-old-patmat.scala new file mode 100644 index 0000000000..4d500b77ba --- /dev/null +++ b/test/files/neg/t6675-old-patmat.scala @@ -0,0 +1,13 @@ +object X { + def unapply(s: String): Option[(Int,Int,Int)] = Some((1,2,3)) +} + +object Y { + def unapplySeq(s: String): Option[Seq[(Int,Int,Int)]] = Some(Seq((1,2,3))) +} + +object Test { + "" match { case X(b) => b } // should warn under -Xlint. Not an error because of SI-6111 + + "" match { case Y(b) => b } // no warning +} diff --git a/test/files/neg/t6675.check b/test/files/neg/t6675.check new file mode 100644 index 0000000000..7b271de213 --- /dev/null +++ b/test/files/neg/t6675.check @@ -0,0 +1,4 @@ +t6675.scala:10: error: extractor pattern binds a single value to a Product3 of type (Int, Int, Int) + "" match { case X(b) => b } // should warn under -Xlint. Not an error because of SI-6111 + ^ +one error found diff --git a/test/files/neg/t6675.flags b/test/files/neg/t6675.flags new file mode 100644 index 0000000000..e93641e931 --- /dev/null +++ b/test/files/neg/t6675.flags @@ -0,0 +1 @@ +-Xlint -Xfatal-warnings
\ No newline at end of file diff --git a/test/files/neg/t6675.scala b/test/files/neg/t6675.scala new file mode 100644 index 0000000000..4d500b77ba --- /dev/null +++ b/test/files/neg/t6675.scala @@ -0,0 +1,13 @@ +object X { + def unapply(s: String): Option[(Int,Int,Int)] = Some((1,2,3)) +} + +object Y { + def unapplySeq(s: String): Option[Seq[(Int,Int,Int)]] = Some(Seq((1,2,3))) +} + +object Test { + "" match { case X(b) => b } // should warn under -Xlint. Not an error because of SI-6111 + + "" match { case Y(b) => b } // no warning +} diff --git a/test/files/neg/t6963.check b/test/files/neg/t6963.check new file mode 100644 index 0000000000..41cb796b0b --- /dev/null +++ b/test/files/neg/t6963.check @@ -0,0 +1,2 @@ +error: -Xmigration is deprecated: This setting is no longer useful and will be removed. Please remove it from your build. +one error found diff --git a/test/files/neg/t6963.flags b/test/files/neg/t6963.flags new file mode 100644 index 0000000000..0b6d71496a --- /dev/null +++ b/test/files/neg/t6963.flags @@ -0,0 +1 @@ +-Xmigration -deprecation -Xfatal-warnings
\ No newline at end of file diff --git a/test/files/neg/t6963.scala b/test/files/neg/t6963.scala new file mode 100644 index 0000000000..4da52764f5 --- /dev/null +++ b/test/files/neg/t6963.scala @@ -0,0 +1,3 @@ + +object test { +} diff --git a/test/files/pos/package-case.flags b/test/files/pos/package-case.flags new file mode 100644 index 0000000000..2f174c4732 --- /dev/null +++ b/test/files/pos/package-case.flags @@ -0,0 +1 @@ +-Ycompanions-in-pkg-objs diff --git a/test/files/pos/t2130-1.flags b/test/files/pos/t2130-1.flags new file mode 100644 index 0000000000..2f174c4732 --- /dev/null +++ b/test/files/pos/t2130-1.flags @@ -0,0 +1 @@ +-Ycompanions-in-pkg-objs diff --git a/test/files/pos/t2130-2.flags b/test/files/pos/t2130-2.flags new file mode 100644 index 0000000000..2f174c4732 --- /dev/null +++ b/test/files/pos/t2130-2.flags @@ -0,0 +1 @@ +-Ycompanions-in-pkg-objs diff --git a/test/files/pos/t3999b.flags b/test/files/pos/t3999b.flags new file mode 100644 index 0000000000..2f174c4732 --- /dev/null +++ b/test/files/pos/t3999b.flags @@ -0,0 +1 @@ +-Ycompanions-in-pkg-objs diff --git a/test/files/pos/t4052.flags b/test/files/pos/t4052.flags new file mode 100644 index 0000000000..2f174c4732 --- /dev/null +++ b/test/files/pos/t4052.flags @@ -0,0 +1 @@ +-Ycompanions-in-pkg-objs diff --git a/test/files/pos/t6925.scala b/test/files/pos/t6925.scala new file mode 100644 index 0000000000..862a6e9d0e --- /dev/null +++ b/test/files/pos/t6925.scala @@ -0,0 +1,9 @@ +class Test { + def f[T](xs: Set[T]) /* no expected type to trigger inference */ = + xs collect { case x => x } + + def g[T](xs: Set[T]): Set[T] = f[T](xs) // check that f's inferred type is Set[T] + + // check that this type checks: + List(1).flatMap(n => Set(1).collect { case w => w }) +}
\ No newline at end of file diff --git a/test/files/pos/t6925b.scala b/test/files/pos/t6925b.scala new file mode 100644 index 0000000000..ca25146dfc --- /dev/null +++ b/test/files/pos/t6925b.scala @@ -0,0 +1,18 @@ +// code *generated* by test/scaladoc/run/SI-5933.scala +// duplicated here because it's related to SI-6925 + +import language.higherKinds + +abstract class Base[M[_, _]] { + def foo[A, B]: M[(A, B), Any] +} + +class Derived extends Base[PartialFunction] { + def foo[AA, BB] /*: PartialFunction[(A, B) => Any]*/ = { case (a, b) => (a: AA, b: BB) } +} + +object Test { + lazy val lx = { println("hello"); 3 } + def test1(x: Int = lx) = ??? + def test2(x: Int = lx match { case 0 => 1; case 3 => 4 }) = ??? +}
\ No newline at end of file diff --git a/test/files/run/t6126.scala b/test/files/run/t6126.scala new file mode 100644 index 0000000000..d552d8e695 --- /dev/null +++ b/test/files/run/t6126.scala @@ -0,0 +1,8 @@ +trait LogLevelType +object Test { + type LogLevel = Int with LogLevelType + final val ErrorLevel = 1.asInstanceOf[Int with LogLevelType] + def main(args: Array[String]) { + List(ErrorLevel, ErrorLevel) + } +} diff --git a/test/files/run/t6955.check b/test/files/run/t6955.check new file mode 100644 index 0000000000..0cfbf08886 --- /dev/null +++ b/test/files/run/t6955.check @@ -0,0 +1 @@ +2 diff --git a/test/files/run/t6955.scala b/test/files/run/t6955.scala new file mode 100644 index 0000000000..2610acdec4 --- /dev/null +++ b/test/files/run/t6955.scala @@ -0,0 +1,26 @@ +import scala.tools.partest.IcodeTest + +class Switches { + type Tag = Byte + + def switchBad(i: Tag): Int = i match { // notice type of i is Tag = Byte + case 1 => 1 + case 2 => 2 + case 3 => 3 + case _ => 0 + } + + def switchOkay(i: Byte): Int = i match { // notice type of i is Byte + case 1 => 1 + case 2 => 2 + case 3 => 3 + case _ => 0 + } +} + +object Test extends IcodeTest { + // ensure we get two switches out of this -- ignore the rest of the output for robustness + // exclude the constant we emit for the "SWITCH ..." string below (we get the icode for all the code you see in this file) + override def show() = println(collectIcode("").filter(x => x.indexOf("SWITCH ...") >= 0 && x.indexOf("CONSTANT(") == -1).size) +} + |