summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xsrc/compiler/scala/tools/nsc/ast/DocComments.scala4
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/MemberLookup.scala1
-rw-r--r--src/compiler/scala/tools/nsc/matching/Patterns.scala2
-rw-r--r--src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala12
-rw-r--r--src/compiler/scala/tools/nsc/transform/PostErasure.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/TailCalls.scala3
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala51
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala90
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala5
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala94
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Unapplies.scala59
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala10
-rw-r--r--test/files/neg/t5845.check5
-rw-r--r--test/files/neg/t997.check8
-rw-r--r--test/files/neg/t997.scala2
-rw-r--r--test/files/neg/unchecked2.check19
-rw-r--r--test/files/neg/unchecked2.flags1
-rw-r--r--test/files/neg/unchecked2.scala8
-rw-r--r--test/files/pos/t1439.scala2
-rw-r--r--test/files/pos/t1832.scala8
-rw-r--r--test/files/pos/t4881.scala31
-rw-r--r--test/files/pos/t6089b.scala18
-rw-r--r--test/files/run/t6090.scala6
-rw-r--r--test/files/run/t6111.check2
-rw-r--r--test/files/run/t6111.scala26
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