diff options
17 files changed, 250 insertions, 28 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 0429e295b4..91db1bb92a 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -732,6 +732,7 @@ self => def removeAsPlaceholder(name: Name) { placeholderParams = placeholderParams filter (_.name != name) } + def errorParam = makeParam(nme.ERROR, errorTypeTree setPos o2p(tree.pos.end)) tree match { case Ident(name) => removeAsPlaceholder(name) @@ -739,9 +740,19 @@ self => case Typed(Ident(name), tpe) if tpe.isType => // get the ident! removeAsPlaceholder(name) makeParam(name.toTermName, tpe) + case build.SyntacticTuple(as) => + val arity = as.length + val example = analyzer.exampleTuplePattern(as map { case Ident(name) => name; case _ => nme.EMPTY }) + val msg = + sm"""|not a legal formal parameter. + |Note: Tuples cannot be directly destructured in method or function parameters. + | Either create a single parameter accepting the Tuple${arity}, + | or consider a pattern matching anonymous function: `{ case $example => ... }""" + syntaxError(tree.pos, msg, skipIt = false) + errorParam case _ => syntaxError(tree.pos, "not a legal formal parameter", skipIt = false) - makeParam(nme.ERROR, errorTypeTree setPos o2p(tree.pos.end)) + errorParam } } diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala index 45698c0c76..8ff7824159 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala @@ -233,9 +233,6 @@ trait MatchOptimization extends MatchTreeMaking with MatchAnalysis { def defaultBody: Tree def defaultCase(scrutSym: Symbol = defaultSym, guard: Tree = EmptyTree, body: Tree = defaultBody): CaseDef - private def sequence[T](xs: List[Option[T]]): Option[List[T]] = - if (xs exists (_.isEmpty)) None else Some(xs.flatten) - object GuardAndBodyTreeMakers { def unapply(tms: List[TreeMaker]): Option[(Tree, Tree)] = { tms match { diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 7ecc2be9be..2472f63993 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -408,11 +408,28 @@ trait ContextErrors { setError(tree) } - def MissingParameterTypeError(fun: Tree, vparam: ValDef, pt: Type) = + def MissingParameterTypeError(fun: Tree, vparam: ValDef, pt: Type, withTupleAddendum: Boolean) = { + def issue(what: String) = { + val addendum: String = fun match { + case Function(params, _) if withTupleAddendum => + val funArity = params.length + val example = analyzer.exampleTuplePattern(params map (_.name)) + (pt baseType FunctionClass(1)) match { + case TypeRef(_, _, arg :: _) if arg.typeSymbol == TupleClass(funArity) && funArity > 1 => + sm"""| + |Note: The expected type requires a one-argument function accepting a $funArity-Tuple. + | Consider a pattern matching anoynmous function, `{ case $example => ... }`""" + case _ => "" + } + case _ => "" + } + issueNormalTypeError(vparam, what + addendum) + } if (vparam.mods.isSynthetic) fun match { case Function(_, Match(_, _)) => MissingParameterTypeAnonMatchError(vparam, pt) - case _ => issueNormalTypeError(vparam, "missing parameter type for expanded function " + fun) - } else issueNormalTypeError(vparam, "missing parameter type") + case _ => issue("missing parameter type for expanded function " + fun) + } else issue("missing parameter type") + } def MissingParameterTypeAnonMatchError(vparam: Tree, pt: Type) = issueNormalTypeError(vparam, "missing parameter type for expanded function\n"+ diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 695a1e2e24..b801b644fb 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -109,6 +109,22 @@ trait TypeDiagnostics { case x => x.toString } + /** + * [a, b, c] => "(a, b, c)" + * [a, B] => "(param1, param2)" + * [a, B, c] => "(param1, ..., param2)" + */ + final def exampleTuplePattern(names: List[Name]): String = { + val arity = names.length + val varPatterNames: Option[List[String]] = sequence(names map { + case name if nme.isVariableName(name) => Some(name.decode) + case _ => None + }) + def parenthesize(a: String) = s"($a)" + def genericParams = (Seq("param1") ++ (if (arity > 2) Seq("...") else Nil) ++ Seq(s"param$arity")) + parenthesize(varPatterNames.getOrElse(genericParams).mkString(", ")) + } + def alternatives(tree: Tree): List[Type] = tree.tpe match { case OverloadedType(pre, alternatives) => alternatives map pre.memberType case _ => Nil diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 6d799b0098..fb252c3058 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2906,6 +2906,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper else if (argpts.lengthCompare(numVparams) != 0) WrongNumberOfParametersError(fun, argpts) else { + var issuedMissingParameterTypeError = false foreach2(fun.vparams, argpts) { (vparam, argpt) => if (vparam.tpt.isEmpty) { vparam.tpt.tpe = @@ -2923,7 +2924,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } case _ => } - MissingParameterTypeError(fun, vparam, pt) + MissingParameterTypeError(fun, vparam, pt, withTupleAddendum = !issuedMissingParameterTypeError) + issuedMissingParameterTypeError = true ErrorType } if (!vparam.tpt.pos.isDefined) vparam.tpt setPos vparam.pos.focus @@ -3835,7 +3837,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // as we don't know which alternative to choose... here we do map2Conserve(args, tparams) { //@M! the polytype denotes the expected kind - (arg, tparam) => typedHigherKindedType(arg, mode, GenPolyType(tparam.typeParams, AnyTpe)) + (arg, tparam) => typedHigherKindedType(arg, mode, Kind.FromParams(tparam.typeParams)) } } else // @M: there's probably something wrong when args.length != tparams.length... (triggered by bug #320) // Martin, I'm using fake trees, because, if you use args or arg.map(typedType), @@ -4879,14 +4881,19 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (sameLength(tparams, args)) { // @M: kind-arity checking is done here and in adapt, full kind-checking is in checkKindBounds (in Infer) - val args1 = - if (!isComplete) - args mapConserve (typedHigherKindedType(_, mode)) - // if symbol hasn't been fully loaded, can't check kind-arity - else map2Conserve(args, tparams) { (arg, tparam) => - //@M! the polytype denotes the expected kind - typedHigherKindedType(arg, mode, GenPolyType(tparam.typeParams, AnyTpe)) + val args1 = map2Conserve(args, tparams) { (arg, tparam) => + def ptParams = Kind.FromParams(tparam.typeParams) + + // if symbol hasn't been fully loaded, can't check kind-arity except when we're in a pattern, + // where we can (we can't take part in F-Bounds) and must (SI-8023) + val pt = if (mode.typingPatternOrTypePat) { + tparam.initialize; ptParams } + else if (isComplete) ptParams + else Kind.Wildcard + + typedHigherKindedType(arg, mode, pt) + } val argtypes = args1 map (_.tpe) foreach2(args, tparams) { (arg, tparam) => @@ -5072,8 +5079,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // @M maybe the well-kindedness check should be done when checking the type arguments conform to the type parameters' bounds? val args1 = if (sameLength(args, tparams)) map2Conserve(args, tparams) { - //@M! the polytype denotes the expected kind - (arg, tparam) => typedHigherKindedType(arg, mode, GenPolyType(tparam.typeParams, AnyTpe)) + (arg, tparam) => typedHigherKindedType(arg, mode, Kind.FromParams(tparam.typeParams)) } else { //@M this branch is correctly hit for an overloaded polymorphic type. It also has to handle erroneous cases. @@ -5443,9 +5449,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper /** Types a (fully parameterized) type tree */ def typedType(tree: Tree): Tree = typedType(tree, NOmode) - /** Types a higher-kinded type tree -- pt denotes the expected kind*/ + /** Types a higher-kinded type tree -- pt denotes the expected kind and must be one of `Kind.WildCard` and `Kind.FromParams` */ def typedHigherKindedType(tree: Tree, mode: Mode, pt: Type): Tree = - if (pt.typeParams.isEmpty) typedType(tree, mode) // kind is known and it's * + if (pt != Kind.Wildcard && pt.typeParams.isEmpty) typedType(tree, mode) // kind is known and it's * else context withinTypeConstructorAllowed typed(tree, NOmode, pt) def typedHigherKindedType(tree: Tree, mode: Mode): Tree = diff --git a/src/library/scala/collection/immutable/Vector.scala b/src/library/scala/collection/immutable/Vector.scala index 0a100578c0..c7da447f72 100644 --- a/src/library/scala/collection/immutable/Vector.scala +++ b/src/library/scala/collection/immutable/Vector.scala @@ -59,7 +59,7 @@ object Vector extends IndexedSeqFactory[Vector] { * @define mayNotTerminateInf * @define willNotTerminateInf */ -final class Vector[+A](private[collection] val startIndex: Int, private[collection] val endIndex: Int, focus: Int) +final class Vector[+A] private[immutable] (private[collection] val startIndex: Int, private[collection] val endIndex: Int, focus: Int) extends AbstractSeq[A] with IndexedSeq[A] with GenericTraversableTemplate[A, Vector] @@ -227,7 +227,7 @@ override def companion: GenericCompanion[Vector] = Vector val ri = this.reverseIterator while (ri.hasNext) v = ri.next +: v v.asInstanceOf[That] - case _ => super.++(that) + case _ => super.++(again) } } } diff --git a/src/library/scala/util/matching/Regex.scala b/src/library/scala/util/matching/Regex.scala index 439b30e714..22dbb37789 100644 --- a/src/library/scala/util/matching/Regex.scala +++ b/src/library/scala/util/matching/Regex.scala @@ -188,9 +188,9 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends * @param s The string to match * @return The matches */ - def unapplySeq(s: CharSequence): Option[Seq[String]] = { + def unapplySeq(s: CharSequence): Option[List[String]] = { val m = pattern matcher s - if (runMatcher(m)) Some(1 to m.groupCount map m.group) + if (runMatcher(m)) Some((1 to m.groupCount).toList map m.group) else None } @@ -225,10 +225,10 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends * @param c The Char to match * @return The match */ - def unapplySeq(c: Char): Option[Seq[Char]] = { + def unapplySeq(c: Char): Option[List[Char]] = { val m = pattern matcher c.toString if (runMatcher(m)) { - if (m.groupCount > 0) Some(m group 1) else Some(Nil) + if (m.groupCount > 0) Some((m group 1).toList) else Some(Nil) } else None } @@ -238,9 +238,9 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends * Otherwise, this Regex is applied to the previously matched input, * and the result of that match is used. */ - def unapplySeq(m: Match): Option[Seq[String]] = + def unapplySeq(m: Match): Option[List[String]] = if (m.matched == null) None - else if (m.matcher.pattern == this.pattern) Some(1 to m.groupCount map m.group) + else if (m.matcher.pattern == this.pattern) Some((1 to m.groupCount).toList map m.group) else unapplySeq(m.matched) /** Tries to match target. diff --git a/src/reflect/scala/reflect/internal/Kinds.scala b/src/reflect/scala/reflect/internal/Kinds.scala index d48a6c6322..8ae201f045 100644 --- a/src/reflect/scala/reflect/internal/Kinds.scala +++ b/src/reflect/scala/reflect/internal/Kinds.scala @@ -326,6 +326,8 @@ trait Kinds { private[internal] object StringState { def empty: StringState = StringState(Seq()) } + def FromParams(tparams: List[Symbol]): Type = GenPolyType(tparams, AnyTpe) + def Wildcard: Type = WildcardType } class ProperTypeKind(val bounds: TypeBounds) extends Kind { import Kind.StringState diff --git a/src/reflect/scala/reflect/internal/util/Collections.scala b/src/reflect/scala/reflect/internal/util/Collections.scala index 738baddc08..7cc2952c96 100644 --- a/src/reflect/scala/reflect/internal/util/Collections.scala +++ b/src/reflect/scala/reflect/internal/util/Collections.scala @@ -244,6 +244,11 @@ trait Collections { true } + final def sequence[A](as: List[Option[A]]): Option[List[A]] = { + if (as.exists (_.isEmpty)) None + else Some(as.flatten) + } + final def transposeSafe[A](ass: List[List[A]]): Option[List[List[A]]] = try { Some(ass.transpose) } catch { diff --git a/test/files/neg/missing-param-type-tuple.check b/test/files/neg/missing-param-type-tuple.check new file mode 100644 index 0000000000..bc46ba1023 --- /dev/null +++ b/test/files/neg/missing-param-type-tuple.check @@ -0,0 +1,31 @@ +missing-param-type-tuple.scala:3: error: missing parameter type +Note: The expected type requires a one-argument function accepting a 2-Tuple. + Consider a pattern matching anoynmous function, `{ case (a, b) => ... }` + val x: ((Int, Int)) => Int = (a, b) => 0 + ^ +missing-param-type-tuple.scala:3: error: missing parameter type + val x: ((Int, Int)) => Int = (a, b) => 0 + ^ +missing-param-type-tuple.scala:5: error: missing parameter type +Note: The expected type requires a one-argument function accepting a 3-Tuple. + Consider a pattern matching anoynmous function, `{ case (param1, ..., param3) => ... }` + val y: ((Int, Int, Int)) => Int = (a, b, !!) => 0 + ^ +missing-param-type-tuple.scala:5: error: missing parameter type + val y: ((Int, Int, Int)) => Int = (a, b, !!) => 0 + ^ +missing-param-type-tuple.scala:5: error: missing parameter type + val y: ((Int, Int, Int)) => Int = (a, b, !!) => 0 + ^ +missing-param-type-tuple.scala:7: error: missing parameter type +Note: The expected type requires a one-argument function accepting a 3-Tuple. + Consider a pattern matching anoynmous function, `{ case (param1, ..., param3) => ... }` + val z: ((Int, Int, Int)) => Int = (a, NotAVariablePatternName, c) => 0 + ^ +missing-param-type-tuple.scala:7: error: missing parameter type + val z: ((Int, Int, Int)) => Int = (a, NotAVariablePatternName, c) => 0 + ^ +missing-param-type-tuple.scala:7: error: missing parameter type + val z: ((Int, Int, Int)) => Int = (a, NotAVariablePatternName, c) => 0 + ^ +8 errors found diff --git a/test/files/neg/missing-param-type-tuple.scala b/test/files/neg/missing-param-type-tuple.scala new file mode 100644 index 0000000000..72c0c82034 --- /dev/null +++ b/test/files/neg/missing-param-type-tuple.scala @@ -0,0 +1,8 @@ +class C { + + val x: ((Int, Int)) => Int = (a, b) => 0 + + val y: ((Int, Int, Int)) => Int = (a, b, !!) => 0 + + val z: ((Int, Int, Int)) => Int = (a, NotAVariablePatternName, c) => 0 +} diff --git a/test/files/neg/not-a-legal-formal-parameter-tuple.check b/test/files/neg/not-a-legal-formal-parameter-tuple.check new file mode 100644 index 0000000000..2b906b8ff3 --- /dev/null +++ b/test/files/neg/not-a-legal-formal-parameter-tuple.check @@ -0,0 +1,19 @@ +not-a-legal-formal-parameter-tuple.scala:2: error: not a legal formal parameter. +Note: Tuples cannot be directly destructured in method or function parameters. + Either create a single parameter accepting the Tuple2, + or consider a pattern matching anonymous function: `{ case (a, b) => ... } + val x: ((Int, Int) => Int) = (((a, b)) => a) + ^ +not-a-legal-formal-parameter-tuple.scala:3: error: not a legal formal parameter. +Note: Tuples cannot be directly destructured in method or function parameters. + Either create a single parameter accepting the Tuple2, + or consider a pattern matching anonymous function: `{ case (param1, param2) => ... } + val y: ((Int, Int, Int) => Int) = (((a, !!)) => a) + ^ +not-a-legal-formal-parameter-tuple.scala:4: error: not a legal formal parameter. +Note: Tuples cannot be directly destructured in method or function parameters. + Either create a single parameter accepting the Tuple3, + or consider a pattern matching anonymous function: `{ case (param1, ..., param3) => ... } + val z: ((Int, Int, Int) => Int) = (((a, NotAPatternVariableName, c)) => a) + ^ +three errors found diff --git a/test/files/neg/not-a-legal-formal-parameter-tuple.scala b/test/files/neg/not-a-legal-formal-parameter-tuple.scala new file mode 100644 index 0000000000..c7a13557df --- /dev/null +++ b/test/files/neg/not-a-legal-formal-parameter-tuple.scala @@ -0,0 +1,5 @@ +class C { + val x: ((Int, Int) => Int) = (((a, b)) => a) + val y: ((Int, Int, Int) => Int) = (((a, !!)) => a) + val z: ((Int, Int, Int) => Int) = (((a, NotAPatternVariableName, c)) => a) +} diff --git a/test/files/pos/t8023.scala b/test/files/pos/t8023.scala new file mode 100644 index 0000000000..86824084ed --- /dev/null +++ b/test/files/pos/t8023.scala @@ -0,0 +1,22 @@ +import language._ + + +object Test { + def foo = (null: Any) match { + case a: A[k] => + // error: kinds of the type arguments (k) do not conform to the + // expected kinds of the type parameters (type K) in class B. + new B[k]() + } +} + +class A[K[L[_]]] + +class B[K[M[_]]] + + +object Test2 { + def foo = (null: Any) match { + case a: A[k] => new B[k]() // this one worked before as the info of `A` was complete + } +} diff --git a/test/files/pos/t8023b.scala b/test/files/pos/t8023b.scala new file mode 100644 index 0000000000..94c9b2f8d2 --- /dev/null +++ b/test/files/pos/t8023b.scala @@ -0,0 +1,2 @@ +// this fails with naive attempts to fix SI-8023 +trait T[A <: T[A]] diff --git a/test/junit/scala/collection/VectorTest.scala b/test/junit/scala/collection/VectorTest.scala new file mode 100644 index 0000000000..e9c4d44a72 --- /dev/null +++ b/test/junit/scala/collection/VectorTest.scala @@ -0,0 +1,51 @@ +package scala.collection.mutable + +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.junit.Test +import scala.collection.mutable + +@RunWith(classOf[JUnit4]) +/* Test for SI-8014 and ++ in general */ +class VectorTest { + val noVec = Vector.empty[Int] + val smallVec = Vector.range(0,3) + val bigVec = Vector.range(0,64) + val smsm = Vector.tabulate(2 * smallVec.length)(i => (i % smallVec.length)) + val smbig = Vector.tabulate(smallVec.length + bigVec.length)(i => + if (i < smallVec.length) i else i - smallVec.length + ) + val bigsm = Vector.tabulate(smallVec.length + bigVec.length)(i => + if (i < bigVec.length) i else i - bigVec.length + ) + val bigbig = Vector.tabulate(2 * bigVec.length)(i => (i % bigVec.length)) + + + val vecs = List(noVec, smallVec, bigVec) + val ans = List( + vecs, + List(smallVec, smsm, smbig), + List(bigVec, bigsm, bigbig) + ) + + @Test + def vectorCat() { + val cats = vecs.map(a => vecs.map(a ++ _)) + assert( cats == ans ) + } + + @Test + def iteratorCat() { + def its = vecs.map(_.toList.toIterator) + val cats = vecs.map(a => its.map(a ++ _)) + println(cats) + assert( cats == ans ) + } + + @Test + def arrayCat() { + val ars = vecs.map(_.toArray) + val cats = vecs.map(a => ars.map(a ++ _)) + assert( cats == ans ) + } +} diff --git a/test/junit/scala/util/matching/RegexTest.scala b/test/junit/scala/util/matching/RegexTest.scala new file mode 100644 index 0000000000..d25842cc57 --- /dev/null +++ b/test/junit/scala/util/matching/RegexTest.scala @@ -0,0 +1,30 @@ + +package scala.util.matching + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(classOf[JUnit4]) +class RegexTest { + @Test def t8022CharSequence(): Unit = { + val full = """.*: (.)$""".r + val text = " When I use this operator: *" + // Testing 2.10.x compatibility of the return types of unapplySeq + val x :: Nil = full.unapplySeq(text: Any).get + val y :: Nil = full.unapplySeq(text: CharSequence).get + assertEquals("*", x) + assertEquals("*", y) + } + + @Test def t8022Match(): Unit = { + val R = """(\d)""".r + val matchh = R.findFirstMatchIn("a1").get + // Testing 2.10.x compatibility of the return types of unapplySeq + val x :: Nil = R.unapplySeq(matchh: Any).get + val y :: Nil = R.unapplySeq(matchh).get + assertEquals("1", x) + assertEquals("1", y) + } +} |