diff options
25 files changed, 322 insertions, 147 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/DocComments.scala b/src/compiler/scala/tools/nsc/ast/DocComments.scala index c2530bd5c7..d0d0b4b5f4 100755 --- a/src/compiler/scala/tools/nsc/ast/DocComments.scala +++ b/src/compiler/scala/tools/nsc/ast/DocComments.scala @@ -209,10 +209,6 @@ trait DocComments { self: Global => for (tparam <- sym.typeParams) mergeSection(srcTParams get tparam.name.toString, dstTParams get tparam.name.toString) mergeSection(returnDoc(src, srcSections), returnDoc(dst, dstSections)) - if (sym.name.toString == "isEmpty") { - println(groupDoc(src, srcSections)) - println(groupDoc(dst, dstSections)) - } mergeSection(groupDoc(src, srcSections), groupDoc(dst, dstSections)) if (out.length == 0) dst diff --git a/src/compiler/scala/tools/nsc/doc/model/MemberLookup.scala b/src/compiler/scala/tools/nsc/doc/model/MemberLookup.scala index 277e86f9af..ab14498a7c 100644 --- a/src/compiler/scala/tools/nsc/doc/model/MemberLookup.scala +++ b/src/compiler/scala/tools/nsc/doc/model/MemberLookup.scala @@ -31,7 +31,6 @@ trait MemberLookup { linkTo = lookupInTemplate(pos, members, currentTpl) currentTpl = currentTpl.inTemplate } - if (currentTpl == null) println("\n\n\n\n\nnull found in:" + inTplOpt + "\n\n\n\n\n\n\n\n") } // (3) Look at external links diff --git a/src/compiler/scala/tools/nsc/matching/Patterns.scala b/src/compiler/scala/tools/nsc/matching/Patterns.scala index bbe22ca314..28dfd3fc77 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) + def resTypes = analyzer.unapplyTypeList(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/symtab/BrowsingLoaders.scala b/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala index 52e971f1e7..84d601bfed 100644 --- a/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala @@ -69,10 +69,18 @@ abstract class BrowsingLoaders extends SymbolLoaders { case _ => throw new MalformedInput(pkg.pos.point, "illegal tree node in package prefix: "+pkg) } + + private def inPackagePrefix(pkg: Tree)(op: => Unit): Unit = { + val oldPrefix = packagePrefix + addPackagePrefix(pkg) + op + packagePrefix = oldPrefix + } + override def traverse(tree: Tree): Unit = tree match { case PackageDef(pkg, body) => - addPackagePrefix(pkg) - body foreach traverse + inPackagePrefix(pkg) { body foreach traverse } + case ClassDef(_, name, _, _) => if (packagePrefix == root.fullName) { enterClass(root, name.toString, new SourcefileLoader(src)) diff --git a/src/compiler/scala/tools/nsc/transform/PostErasure.scala b/src/compiler/scala/tools/nsc/transform/PostErasure.scala index 151bc66a79..479bc2292e 100644 --- a/src/compiler/scala/tools/nsc/transform/PostErasure.scala +++ b/src/compiler/scala/tools/nsc/transform/PostErasure.scala @@ -52,7 +52,7 @@ trait PostErasure extends InfoTransform with TypingTransformers { List(Apply(Select(New(tpt2), nme.CONSTRUCTOR), List(arg2)))) if atPhase(currentRun.erasurePhase) { tpt1.tpe.typeSymbol.isDerivedValueClass && - (cmp == nme.EQ || cmp == nme.NE) && + (sel.symbol == Object_== || sel.symbol == Object_!=) && tpt2.tpe.typeSymbol == tpt1.tpe.typeSymbol } => val result = Apply(Select(arg1, cmp) setPos sel.pos, List(arg2)) setPos tree.pos diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala index d5bbc578fc..492273dfdc 100644 --- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala +++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala @@ -397,6 +397,9 @@ abstract class TailCalls extends Transform { case Apply(fun, arg :: Nil) if hasSynthCaseSymbol(fun) && tailLabels(fun.symbol) => traverse(arg) + case Apply(fun, args) if (fun.symbol == Boolean_or || fun.symbol == Boolean_and) => + traverseTrees(args) + // a translated casedef case LabelDef(_, _, body) if hasSynthCaseSymbol(tree) => traverse(body) diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index efc3d25ed0..5c0207e5c7 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -225,38 +225,15 @@ abstract class UnCurry extends InfoTransform } - /* Transform a function node (x_1,...,x_n) => body of type FunctionN[T_1, .., T_N, R] to + /** Transform a function node (x_1,...,x_n) => body of type FunctionN[T_1, .., T_N, R] to * * class $anon() extends AbstractFunctionN[T_1, .., T_N, R] with Serializable { * def apply(x_1: T_1, ..., x_N: T_n): R = body * } * new $anon() * - * transform a function node (x => body) of type PartialFunction[T, R] where - * body = expr match { case P_i if G_i => E_i }_i=1..n - * to: + * If `settings.XoldPatmat.value`, also synthesized AbstractPartialFunction subclasses (see synthPartialFunction). * - //TODO: correct code template below - * class $anon() extends AbstractPartialFunction[T, R] with Serializable { - * def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = (expr: @unchecked) match { - * case P_1 if G_1 => E_1 - * ... - * case P_n if G_n => E_n - * case _ => default(expr) - * } - * def isDefinedAt(x: T): boolean = (x: @unchecked) match { - * case P_1 if G_1 => true - * ... - * case P_n if G_n => true - * case _ => false - * } - * } - * new $anon() - * - * However, if one of the patterns P_i if G_i is a default pattern, - * drop the last default clause in the definition of `apply` and generate for `_isDefinedAt` instead - * - * def isDefinedAtCurrent(x: T): boolean = true */ def transformFunction(fun: Function): Tree = deEta(fun) match { @@ -300,6 +277,28 @@ abstract class UnCurry extends InfoTransform } + /** Transform a function node (x => body) of type PartialFunction[T, R] where + * body = expr match { case P_i if G_i => E_i }_i=1..n + * to (assuming none of the cases is a default case): + * + * class $anon() extends AbstractPartialFunction[T, R] with Serializable { + * def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = (expr: @unchecked) match { + * case P_1 if G_1 => E_1 + * ... + * case P_n if G_n => E_n + * case _ => default(expr) + * } + * def isDefinedAt(x: T): boolean = (x: @unchecked) match { + * case P_1 if G_1 => true + * ... + * case P_n if G_n => true + * case _ => false + * } + * } + * new $anon() + * + * If there's a default case, the original match is used for applyOrElse, and isDefinedAt returns `true` + */ def synthPartialFunction(fun: Function) = { if (!settings.XoldPatmat.value) debugwarn("Under the new pattern matching scheme, PartialFunction should have been synthesized during typers.") @@ -619,7 +618,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.unapplyTypeListFromReturnTypeSeq(fn.tpe)) + case nme.unapplySeq => transformArgs(tree.pos, fn.symbol, args, analyzer.unapplyTypeList(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/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 960c210649..bcbcb96400 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -49,6 +49,69 @@ trait Infer { } else formals1 } + /** Returns `(formals, formalsExpanded)` where `formalsExpanded` are the expected types + * for the `nbSubPats` sub-patterns of an extractor pattern, of which the corresponding + * unapply[Seq] call is assumed to have result type `resTp`. + * + * `formals` are the formal types before expanding a potential repeated parameter (must come last in `formals`, if at all) + * + * @throws TypeError when the unapply[Seq] definition is ill-typed + * @returns (null, null) when the expected number of sub-patterns cannot be satisfied by the given extractor + * + * From the spec: + * 8.1.8 ExtractorPatterns + * + * An extractor pattern x(p1, ..., pn) where n ≥ 0 is of the same syntactic form as a constructor pattern. + * However, instead of a case class, the stable identifier x denotes an object which has a member method named unapply or unapplySeq that matches the pattern. + * An unapply method in an object x matches the pattern x(p1, ..., pn) if it takes exactly one argument and one of the following applies: + * + * n = 0 and unapply’s result type is Boolean. + * + * n = 1 and unapply’s result type is Option[T], for some type T. + * the (only) argument pattern p1 is typed in turn with expected type T + * + * 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]) = { + val isUnapplySeq = unappSym.name == nme.unapplySeq + val booleanExtractor = resTp.typeSymbolDirect == BooleanClass + + @inline def seqToRepeatedChecked(tp: Type) = { + val toRepeated = seqToRepeated(tp) + if (tp eq toRepeated) throw new TypeError("(the last tuple-component of) the result type of an unapplySeq must be a Seq[_]") + else toRepeated + } + + val formals = + if (nbSubPats == 0 && booleanExtractor && !isUnapplySeq) Nil + else resTp.baseType(OptionClass).typeArgs match { + case optionTArg :: Nil => + if (nbSubPats == 1) + if (isUnapplySeq) List(seqToRepeatedChecked(optionTArg)) + else List(optionTArg) + // TODO: update spec to reflect we allow any ProductN, not just TupleN + else getProductArgs(optionTArg) match { + case Nil if isUnapplySeq => List(seqToRepeatedChecked(optionTArg)) + case tps if isUnapplySeq => tps.init :+ seqToRepeatedChecked(tps.last) + case tps => tps + } + case _ => + if (isUnapplySeq) + throw new TypeError(s"result type $resTp of unapplySeq defined in ${unappSym.owner+unappSym.owner.locationString} not in {Option[_], Some[_]}") + else + throw new TypeError(s"result type $resTp of unapply defined in ${unappSym.owner+unappSym.owner.locationString} not in {Boolean, Option[_], Some[_]}") + } + + // for unapplySeq, replace last vararg by as many instances as required by nbSubPats + val formalsExpanded = + if (isUnapplySeq && formals.nonEmpty) formalTypes(formals, nbSubPats) + else formals + + if (formalsExpanded.lengthCompare(nbSubPats) != 0) (null, null) + else (formals, formalsExpanded) + } + def actualTypes(actuals: List[Type], nformals: Int): List[Type] = if (nformals == 1 && !hasLength(actuals, 1)) List(if (actuals.isEmpty) UnitClass.tpe else tupleType(actuals)) @@ -445,7 +508,7 @@ trait Infer { val tvars = tparams map freshVar if (isConservativelyCompatible(restpe.instantiateTypeParams(tparams, tvars), pt)) map2(tparams, tvars)((tparam, tvar) => - instantiateToBound(tvar, varianceInTypes(formals)(tparam))) + instantiateToBound(tvar, inferVariance(formals, restpe)(tparam))) else tvars map (tvar => WildcardType) } @@ -575,13 +638,24 @@ trait Infer { "argument expression's type is not compatible with formal parameter type" + foundReqMsg(tp1, pt1)) } } + val targs = solvedTypes( - tvars, tparams, tparams map varianceInTypes(formals), + tvars, tparams, tparams map inferVariance(formals, restpe), false, lubDepth(formals) max lubDepth(argtpes) ) adjustTypeArgs(tparams, tvars, targs, restpe) } + /** Determine which variance to assume for the type paraneter. We first chose the variance + * that minimizes any formal parameters. If that is still undetermined, because the type parameter + * does not appear as a formal parameter type, then we pick the variance so that it minimizes the + * method's result type instead. + */ + private def inferVariance(formals: List[Type], restpe: Type)(tparam: Symbol): Int = { + val v = varianceInTypes(formals)(tparam) + if (v != VarianceFlags) v else varianceInType(restpe)(tparam) + } + private[typechecker] def followApply(tp: Type): Type = tp match { case NullaryMethodType(restp) => val restp1 = followApply(restp) @@ -1278,8 +1352,10 @@ trait Infer { } else { for (arg <- args) { if (sym == ArrayClass) check(arg, bound) - else if (arg.typeArgs.nonEmpty) () // avoid spurious warnings with higher-kinded types - else if (sym == NonLocalReturnControlClass) () // no way to suppress unchecked warnings on try/catch + // avoid spurious warnings with higher-kinded types + else if (arg.typeArgs exists (_.typeSymbol.isTypeParameterOrSkolem)) () + // no way to suppress unchecked warnings on try/catch + else if (sym == NonLocalReturnControlClass) () else arg match { case TypeRef(_, sym, _) if isLocalBinding(sym) => ; @@ -1423,7 +1499,7 @@ trait Infer { ) // Intentionally *not* using `Type#typeSymbol` here, which would normalize `tp` - // and collect symbols from the result type of any resulting `PolyType`s, which + // and collect symbols from the result type of any resulting `PolyType`s, which // are not free type parameters of `tp`. // // Contrast with `isFreeTypeParamNoSkolem`. @@ -1456,7 +1532,7 @@ trait Infer { 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 noAlternatives = alts0.isEmpty + val noAlternatives = alts0.isEmpty val alts1 = if (noAlternatives) alts else alts0 //println("trying "+alts1+(alts1 map (_.tpe))+(alts1 map (_.locationString))+" for "+pt) @@ -1614,7 +1690,7 @@ trait Infer { val saved = context.state var fallback = false context.setBufferErrors() - // We cache the current buffer because it is impossible to + // We cache the current buffer because it is impossible to // distinguish errors that occurred before entering tryTwice // and our first attempt in 'withImplicitsDisabled'. If the // first attempt fails we try with implicits on *and* clean diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index 43edad3576..8b7c70c048 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -218,11 +218,6 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL if(phase.id >= currentRun.uncurryPhase.id) debugwarn("running translateMatch at "+ phase +" on "+ selector +" match "+ cases) patmatDebug("translating "+ cases.mkString("{", "\n", "}")) - def repeatedToSeq(tp: Type): Type = (tp baseType RepeatedParamClass) match { - case TypeRef(_, RepeatedParamClass, arg :: Nil) => seqType(arg) - case _ => tp - } - val start = Statistics.startTimer(patmatNanos) val selectorTp = repeatedToSeq(elimAnonymousClass(selector.tpe.widen.withoutAnnotations)) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 7dff055172..80e7d0d474 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2749,6 +2749,14 @@ trait Typers extends Modes with Adaptations with Tags { def typedArgs(args: List[Tree], mode: Int) = args mapConserve (arg => typedArg(arg, mode, 0, WildcardType)) + /** Type trees in `args0` against corresponding expected type in `adapted0`. + * + * The mode in which each argument is typed is derived from `mode` and + * whether the arg was originally by-name or var-arg (need `formals0` for that) + * the default is by-val, of course. + * + * (docs reverse-engineered -- AM) + */ def typedArgs(args0: List[Tree], mode: Int, formals0: List[Type], adapted0: List[Type]): List[Tree] = { val sticky = onlyStickyModes(mode) def loop(args: List[Tree], formals: List[Type], adapted: List[Type]): List[Tree] = { @@ -3157,12 +3165,13 @@ trait Typers extends Modes with Adaptations with Tags { if (fun1.tpe.isErroneous) duplErrTree else { - val formals0 = unapplyTypeList(fun1.symbol, fun1.tpe) - val formals1 = formalTypes(formals0, args.length) + val resTp = fun1.tpe.finalResultType.normalize + val nbSubPats = args.length - if (!sameLength(formals1, args)) duplErrorTree(WrongNumberArgsPatternError(tree, fun)) + val (formals, formalsExpanded) = extractorFormalTypes(resTp, nbSubPats, fun1.symbol) + if (formals == null) duplErrorTree(WrongNumberArgsPatternError(tree, fun)) else { - val args1 = typedArgs(args, mode, formals0, formals1) + val args1 = typedArgs(args, mode, formals, formalsExpanded) // This used to be the following (failing) assert: // assert(isFullyDefined(pt), tree+" ==> "+UnApply(fun1, args1)+", pt = "+pt) // I modified as follows. See SI-1048. @@ -3884,40 +3893,51 @@ trait Typers extends Modes with Adaptations with Tags { } } - def typedBind(name: Name, body: Tree) = { - var vble = tree.symbol - def typedBindType(name: TypeName) = { - assert(body == EmptyTree, context.unit + " typedBind: " + name.debugString + " " + body + " " + body.getClass) - if (vble == NoSymbol) - vble = - if (isFullyDefined(pt)) - context.owner.newAliasType(name, tree.pos) setInfo pt - else - context.owner.newAbstractType(name, tree.pos) setInfo TypeBounds.empty - val rawInfo = vble.rawInfo - vble = if (vble.name == tpnme.WILDCARD) context.scope.enter(vble) - else namer.enterInScope(vble) - tree setSymbol vble setType vble.tpe - } - def typedBindTerm(name: TermName) = { - if (vble == NoSymbol) - vble = context.owner.newValue(name, tree.pos) - if (vble.name.toTermName != nme.WILDCARD) { - if ((mode & ALTmode) != 0) - VariableInPatternAlternativeError(tree) - vble = namer.enterInScope(vble) - } - val body1 = typed(body, mode, pt) - vble.setInfo( - if (treeInfo.isSequenceValued(body)) seqType(body1.tpe) - else body1.tpe) - treeCopy.Bind(tree, name, body1) setSymbol vble setType body1.tpe // burak, was: pt - } + def typedBind(name: Name, body: Tree) = name match { - case x: TypeName => typedBindType(x) - case x: TermName => typedBindTerm(x) + case name: TypeName => assert(body == EmptyTree, context.unit + " typedBind: " + name.debugString + " " + body + " " + body.getClass) + val sym = + if (tree.symbol != NoSymbol) tree.symbol + else { + if (isFullyDefined(pt)) + context.owner.newAliasType(name, tree.pos) setInfo pt + else + context.owner.newAbstractType(name, tree.pos) setInfo TypeBounds.empty + } + + if (name != tpnme.WILDCARD) namer.enterInScope(sym) + else context.scope.enter(sym) + + tree setSymbol sym setType sym.tpe + + case name: TermName => + val sym = + if (tree.symbol != NoSymbol) tree.symbol + else context.owner.newValue(name, tree.pos) + + if (name != nme.WILDCARD) { + if ((mode & ALTmode) != 0) VariableInPatternAlternativeError(tree) + namer.enterInScope(sym) + } + + val body1 = typed(body, mode, pt) + val symTp = + if (treeInfo.isSequenceValued(body)) seqType(body1.tpe) + else body1.tpe + sym setInfo symTp + + // have to imperatively set the symbol for this bind to keep it in sync with the symbols used in the body of a case + // when type checking a case we imperatively update the symbols in the body of the case + // those symbols are bound by the symbols in the Binds in the pattern of the case, + // so, if we set the symbols in the case body, but not in the patterns, + // then re-type check the casedef (for a second try in typedApply for example -- SI-1832), + // we are no longer in sync: the body has symbols set that do not appear in the patterns + // since body1 is not necessarily equal to body, we must return a copied tree, + // but we must still mutate the original bind + tree setSymbol sym + treeCopy.Bind(tree, name, body1) setSymbol sym setType body1.tpe } - } + def typedArrayValue(elemtpt: Tree, elems: List[Tree]) = { val elemtpt1 = typedType(elemtpt, mode) @@ -4869,7 +4889,7 @@ trait Typers extends Modes with Adaptations with Tags { case UnApply(fun, args) => val fun1 = typed(fun) - val tpes = formalTypes(unapplyTypeList(fun.symbol, fun1.tpe), args.length) + val tpes = formalTypes(unapplyTypeList(fun.symbol, fun1.tpe, args.length), args.length) val args1 = map2(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 ad936ac39d..d508e10813 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -31,59 +31,18 @@ trait Unapplies extends ast.TreeDSL // moduleClass symbol of the companion module. class ClassForCaseCompanionAttachment(val caseClass: ClassDef) - /** returns type list for return type of the extraction */ - def unapplyTypeList(ufn: Symbol, ufntpe: Type) = { + /** returns type list for return type of the extraction + * @see extractorFormalTypes + */ + def unapplyTypeList(ufn: Symbol, ufntpe: Type, nbSubPats: Int) = { assert(ufn.isMethod, ufn) //Console.println("utl "+ufntpe+" "+ufntpe.typeSymbol) ufn.name match { - case nme.unapply => unapplyTypeListFromReturnType(ufntpe) - case nme.unapplySeq => unapplyTypeListFromReturnTypeSeq(ufntpe) - case _ => throw new TypeError(ufn+" is not an unapply or unapplySeq") - } - } - /** (the inverse of unapplyReturnTypeSeq) - * for type Boolean, returns Nil - * for type Option[T] or Some[T]: - * - returns T0...Tn if n>0 and T <: Product[T0...Tn]] - * - returns T otherwise - */ - def unapplyTypeListFromReturnType(tp1: Type): List[Type] = { - val tp = unapplyUnwrap(tp1) - tp.typeSymbol match { // unapplySeqResultToMethodSig - case BooleanClass => Nil - case OptionClass | SomeClass => - val prod = tp.typeArgs.head -// the spec doesn't allow just any subtype of Product, it *must* be TupleN[...] -- see run/virtpatmat_extends_product.scala -// this breaks plenty of stuff, though... -// val targs = -// if (isTupleType(prod)) getProductArgs(prod) -// else List(prod) - val targs = getProductArgs(prod) - - if (targs.isEmpty || targs.tail.isEmpty) List(prod) // special n == 0 || n == 1 - else targs // n > 1 - case _ => - throw new TypeError("result type "+tp+" of unapply not in {Boolean, Option[_], Some[_]}") - } - } - - /** let type be the result type of the (possibly polymorphic) unapply method - * for type Option[T] or Some[T] - * -returns T0...Tn-1,Tn* if n>0 and T <: Product[T0...Tn-1,Seq[Tn]]], - * -returns R* if T = Seq[R] - */ - def unapplyTypeListFromReturnTypeSeq(tp1: Type): List[Type] = { - val tp = unapplyUnwrap(tp1) - tp.typeSymbol match { - case OptionClass | SomeClass => - val ts = unapplyTypeListFromReturnType(tp1) - val last1 = (ts.last baseType SeqClass) match { - case TypeRef(pre, SeqClass, args) => typeRef(pre, RepeatedParamClass, args) - case _ => throw new TypeError("last not seq") - } - ts.init :+ last1 - case _ => - throw new TypeError("result type "+tp+" of unapply not in {Option[_], Some[_]}") + case nme.unapply | nme.unapplySeq => + val (formals, _) = extractorFormalTypes(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/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index d9b63529eb..90aa0b732c 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -397,6 +397,16 @@ trait Definitions extends api.StandardDefinitions { case _ => false } + def repeatedToSeq(tp: Type): Type = (tp baseType RepeatedParamClass) match { + case TypeRef(_, RepeatedParamClass, arg :: Nil) => seqType(arg) + case _ => tp + } + + def seqToRepeated(tp: Type): Type = (tp baseType SeqClass) match { + case TypeRef(_, SeqClass, arg :: Nil) => scalaRepeatedType(arg) + case _ => tp + } + def isPrimitiveArray(tp: Type) = tp match { case TypeRef(_, ArrayClass, arg :: Nil) => isPrimitiveValueClass(arg.typeSymbol) case _ => false diff --git a/test/files/neg/t5845.check b/test/files/neg/t5845.check index 8c6100d6de..c0b402fccb 100644 --- a/test/files/neg/t5845.check +++ b/test/files/neg/t5845.check @@ -1,7 +1,4 @@ -t5845.scala:9: error: value +++ is not a member of Int - println(5 +++ 5) - ^ t5845.scala:15: error: value +++ is not a member of Int println(5 +++ 5) ^ -two errors found +one error found diff --git a/test/files/neg/t997.check b/test/files/neg/t997.check index c9fe0de756..186095f44a 100644 --- a/test/files/neg/t997.check +++ b/test/files/neg/t997.check @@ -1,13 +1,7 @@ -t997.scala:7: error: wrong number of arguments for object Foo -"x" match { case Foo(a) => Console.println(a) } - ^ -t997.scala:7: error: not found: value a -"x" match { case Foo(a) => Console.println(a) } - ^ t997.scala:13: error: wrong number of arguments for object Foo "x" match { case Foo(a, b, c) => Console.println((a,b,c)) } ^ t997.scala:13: error: not found: value a "x" match { case Foo(a, b, c) => Console.println((a,b,c)) } ^ -four errors found +two errors found diff --git a/test/files/neg/t997.scala b/test/files/neg/t997.scala index 42b46174d6..e8d10f4317 100644 --- a/test/files/neg/t997.scala +++ b/test/files/neg/t997.scala @@ -3,7 +3,7 @@ object Foo { def unapply(x : String) = Some(Pair(x, x)) } object Test extends App { -// Prints 'x'; ought not to compile (or maybe a should be the Pair?). +// Prints '(x, x)'. Should compile as per SI-6111. "x" match { case Foo(a) => Console.println(a) } // Prints '(x,x)' as expected. diff --git a/test/files/neg/unchecked2.check b/test/files/neg/unchecked2.check new file mode 100644 index 0000000000..2c0be9ce00 --- /dev/null +++ b/test/files/neg/unchecked2.check @@ -0,0 +1,19 @@ +unchecked2.scala:2: error: non variable type-argument Int in type Option[Int] is unchecked since it is eliminated by erasure + Some(123).isInstanceOf[Option[Int]] + ^ +unchecked2.scala:3: error: non variable type-argument String in type Option[String] is unchecked since it is eliminated by erasure + Some(123).isInstanceOf[Option[String]] + ^ +unchecked2.scala:4: error: non variable type-argument List[String] in type Option[List[String]] is unchecked since it is eliminated by erasure + Some(123).isInstanceOf[Option[List[String]]] + ^ +unchecked2.scala:5: error: non variable type-argument List[Int => String] in type Option[List[Int => String]] is unchecked since it is eliminated by erasure + Some(123).isInstanceOf[Option[List[Int => String]]] + ^ +unchecked2.scala:6: error: non variable type-argument (String, Double) in type Option[(String, Double)] is unchecked since it is eliminated by erasure + Some(123).isInstanceOf[Option[(String, Double)]] + ^ +unchecked2.scala:7: error: non variable type-argument String => Double in type Option[String => Double] is unchecked since it is eliminated by erasure + Some(123).isInstanceOf[Option[String => Double]] + ^ +6 errors found diff --git a/test/files/neg/unchecked2.flags b/test/files/neg/unchecked2.flags new file mode 100644 index 0000000000..144ddac9d3 --- /dev/null +++ b/test/files/neg/unchecked2.flags @@ -0,0 +1 @@ +-unchecked -Xfatal-warnings diff --git a/test/files/neg/unchecked2.scala b/test/files/neg/unchecked2.scala new file mode 100644 index 0000000000..a2e757e1dc --- /dev/null +++ b/test/files/neg/unchecked2.scala @@ -0,0 +1,8 @@ +object Test { + Some(123).isInstanceOf[Option[Int]] + Some(123).isInstanceOf[Option[String]] + Some(123).isInstanceOf[Option[List[String]]] + Some(123).isInstanceOf[Option[List[Int => String]]] + Some(123).isInstanceOf[Option[(String, Double)]] + Some(123).isInstanceOf[Option[String => Double]] +} diff --git a/test/files/pos/t1439.scala b/test/files/pos/t1439.scala index 68a7332b2a..0efcc74b65 100644 --- a/test/files/pos/t1439.scala +++ b/test/files/pos/t1439.scala @@ -2,7 +2,7 @@ class View[C[A]] { } object Test { - null match { + (null: Any) match { case v: View[_] => } } diff --git a/test/files/pos/t1832.scala b/test/files/pos/t1832.scala new file mode 100644 index 0000000000..c7b1ffb838 --- /dev/null +++ b/test/files/pos/t1832.scala @@ -0,0 +1,8 @@ +trait Cloning { + trait Foo + def fn(g: Any => Unit): Foo + + implicit def mkStar(i: Int) = new { def *(a: Foo): Foo = null } + + val pool = 4 * fn { case ghostSYMBOL: Int => ghostSYMBOL * 2 } +}
\ No newline at end of file diff --git a/test/files/pos/t4881.scala b/test/files/pos/t4881.scala new file mode 100644 index 0000000000..46cfad9793 --- /dev/null +++ b/test/files/pos/t4881.scala @@ -0,0 +1,31 @@ +class Contra[-T] +trait A +trait B extends A +trait C extends B + +// test improved variance inference: first try formals to see in which variance positions the type param appears; +// only when that fails to determine variance, look at result type +object Test { + def contraLBUB[a >: C <: A](): Contra[a] = null + def contraLB[a >: C](): Contra[a] = null + +{ + val x = contraLBUB() //inferred Contra[C] instead of Contra[A] + val x1: Contra[A] = x +} + +{ + val x = contraLB() //inferred Contra[C] instead of Contra[Any] + val x1: Contra[Any] = x +} + +{ + val x = contraLBUB // make sure it does the same thing as its ()-less counterpart + val x1: Contra[A] = x +} + +{ + val x = contraLB + val x1: Contra[Any] = x +} +} diff --git a/test/files/pos/t6089b.scala b/test/files/pos/t6089b.scala new file mode 100644 index 0000000000..ff7ca157eb --- /dev/null +++ b/test/files/pos/t6089b.scala @@ -0,0 +1,18 @@ +// this crazy code simply tries to nest pattern matches so that the last call is in a tricky-to-determine +// tail position (my initial tightenign of tailpos detection for SI-6089 ruled this out) +class BKTree { + @annotation.tailrec + final def -?-[AA](a: AA): Boolean = this match { + case BKTreeEmpty => false + case BKTreeNode(v) => { + val d = 1 + d == 0 || ( Map(1 -> this,2 -> this,3 -> this) get d match { + case None => false + case Some(w) => w -?- a // can tail call here (since || is shortcutting) + }) + } + } +} + +object BKTreeEmpty extends BKTree +case class BKTreeNode[A](v: A) extends BKTree
\ No newline at end of file diff --git a/test/files/run/t6090.scala b/test/files/run/t6090.scala new file mode 100644 index 0000000000..e7dbb36a05 --- /dev/null +++ b/test/files/run/t6090.scala @@ -0,0 +1,6 @@ +class X { def ==(other: X) = true } +class V(val x: X) extends AnyVal +object Test extends { + def main(args: Array[String]) = + assert((new V(new X) == new V(new X))) +} diff --git a/test/files/run/t6111.check b/test/files/run/t6111.check new file mode 100644 index 0000000000..7fd2e33526 --- /dev/null +++ b/test/files/run/t6111.check @@ -0,0 +1,2 @@ +(8,8) +(x,x) diff --git a/test/files/run/t6111.scala b/test/files/run/t6111.scala new file mode 100644 index 0000000000..7cceea1d09 --- /dev/null +++ b/test/files/run/t6111.scala @@ -0,0 +1,26 @@ +// slightly overkill, but a good test case for implicit resolution in extractor calls, +// along with the real fix: an extractor pattern with 1 sub-pattern should type check for all extractors +// that return Option[T], whatever T (even if it's a tuple) +object Foo { + def unapply[S, T](scrutinee: S)(implicit witness: FooHasType[S, T]): Option[T] = scrutinee match { + case i: Int => Some((i, i).asInstanceOf[T]) + } +} + +class FooHasType[S, T] +object FooHasType { + implicit object int extends FooHasType[Int, (Int, Int)] +} + +// resurrected from neg/t997 +object Foo997 { def unapply(x : String): Option[(String, String)] = Some((x, x)) } + +object Test extends App { + val x = 8 + println(x match { + case Foo(p) => p // p should be a pair of Int + }) + + // Prints '(x, x)' + "x" match { case Foo997(a) => println(a) } +}
\ No newline at end of file |