diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/transform/patmat')
6 files changed, 54 insertions, 30 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala index 49a4990722..62d9c497ba 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala @@ -38,7 +38,7 @@ trait Logic extends Debugging { padded.transpose.map(alignedColumns).transpose map (_.mkString(sep)) mkString(lineSep) } - // http://www.cis.upenn.edu/~cis510/tcl/chap3.pdf + // ftp://ftp.cis.upenn.edu/pub/cis511/public_html/Spring04/chap3.pdf // http://users.encs.concordia.ca/~ta_ahmed/ms_thesis.pdf // propositional logic with constants and equality trait PropositionalLogic { diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala index a11906ace1..1331eb6993 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala @@ -150,7 +150,11 @@ trait TreeAndTypeAnalysis extends Debugging { acc: List[List[Type]]): List[List[Type]] = wl match { case hd :: tl => val children = enumerateChildren(hd) - groupChildren(tl ++ children, acc :+ filterChildren(children)) + // put each trait in a new group, since traits could belong to the same + // group as a derived class + val (traits, nonTraits) = children.partition(_.isTrait) + val filtered = (traits.map(List(_)) ++ List(nonTraits)).map(filterChildren) + groupChildren(tl ++ children, acc ++ filtered) case Nil => acc } diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala index 06b39b035a..1642613b9b 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala @@ -134,7 +134,7 @@ trait MatchCodeGen extends Interface { trait OptimizedCodegen extends CodegenCore with TypedSubstitution with MatchMonadInterface { override def codegen: AbsCodegen = optimizedCodegen - // when we know we're targetting Option, do some inlining the optimizer won't do + // when we know we're targeting Option, do some inlining the optimizer won't do // for example, `o.flatMap(f)` becomes `if(o == None) None else f(o.get)`, similarly for orElse and guard // this is a special instance of the advanced inlining optimization that takes a method call on // an object of a type that only has two concrete subclasses, and inlines both bodies, guarded by an if to distinguish the two cases diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala index b3aef8a20e..cca8d2dbb8 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala @@ -286,8 +286,8 @@ trait MatchOptimization extends MatchTreeMaking with MatchAnalysis { else Apply(Ident(defaultLabel), Nil) val guardedBody = same.foldRight(jumpToDefault){ - // the last case may be un-guarded (we know it's the last one since fold's accum == jumpToDefault) - // --> replace jumpToDefault by the un-guarded case's body + // the last case may be unguarded (we know it's the last one since fold's accum == jumpToDefault) + // --> replace jumpToDefault by the unguarded case's body case (CaseDef(_, EmptyTree, b), `jumpToDefault`) => b case (cd@CaseDef(_, g, b), els) if isGuardedCase(cd) => If(g, b, els) } @@ -322,7 +322,7 @@ trait MatchOptimization extends MatchTreeMaking with MatchAnalysis { var remainingCases = cases val collapsed = scala.collection.mutable.ListBuffer.empty[CaseDef] - // when some of collapsed cases (except for the default case itself) did not include an un-guarded case + // when some of collapsed cases (except for the default case itself) did not include an unguarded case // we'll need to emit a labeldef for the default case var needDefault = false diff --git a/src/compiler/scala/tools/nsc/transform/patmat/PatternExpander.scala b/src/compiler/scala/tools/nsc/transform/patmat/PatternExpander.scala index e84ccbf754..1916050dd8 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/PatternExpander.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/PatternExpander.scala @@ -86,9 +86,25 @@ trait PatternExpander[Pattern, Type] { * @param fixed The non-sequence types which are extracted * @param repeated The sequence type which is extracted */ - final case class Extractor(whole: Type, fixed: List[Type], repeated: Repeated) { + final case class Extractor(whole: Type, fixed: List[Type], repeated: Repeated, typeOfSinglePattern: Type) { require(whole != NoType, s"expandTypes($whole, $fixed, $repeated)") + /** A pattern with arity-1 that doesn't match the arity of the Product-like result of the `get` method, + * will match that result in its entirety. Example: + * + * {{{ + * warning: there was one deprecation warning; re-run with -deprecation for details + * scala> object Extractor { def unapply(a: Any): Option[(Int, String)] = Some((1, "2")) } + * defined object Extractor + * + * scala> "" match { case Extractor(x: Int, y: String) => } + * + * scala> "" match { case Extractor(xy : (Int, String)) => } + * warning: there was one deprecation warning; re-run with -deprecation for details + * }}} + * */ + def asSinglePattern: Extractor = copy(fixed = List(typeOfSinglePattern)) + def productArity = fixed.length def hasSeq = repeated.exists def elementType = repeated.elementType diff --git a/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala b/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala index b1783dc81f..d4f44303bb 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala @@ -43,8 +43,9 @@ trait ScalacPatternExpanders { orElse definitions.elementType(ArrayClass, seq) ) } - def newExtractor(whole: Type, fixed: List[Type], repeated: Repeated): Extractor = - logResult(s"newExtractor($whole, $fixed, $repeated")(Extractor(whole, fixed, repeated)) + def newExtractor(whole: Type, fixed: List[Type], repeated: Repeated, typeOfSinglePattern: Type): Extractor = + logResult(s"newExtractor($whole, $fixed, $repeated, $typeOfSinglePattern")(Extractor(whole, fixed, repeated, typeOfSinglePattern)) + def newExtractor(whole: Type, fixed: List[Type], repeated: Repeated): Extractor = newExtractor(whole, fixed, repeated, tupleType(fixed)) // Turn Seq[A] into Repeated(Seq[A], A, A*) def repeatedFromSeq(seqType: Type): Repeated = { @@ -73,26 +74,27 @@ trait ScalacPatternExpanders { * Unfortunately the MethodType does not carry the information of whether * it was unapplySeq, so we have to funnel that information in separately. */ - def unapplyMethodTypes(whole: Type, result: Type, isSeq: Boolean): Extractor = { - val expanded = ( - if (result =:= BooleanTpe) Nil - else typeOfMemberNamedGet(result) match { + def unapplyMethodTypes(context: Context, whole: Type, result: Type, isSeq: Boolean): Extractor = { + if (result =:= BooleanTpe) newExtractor(whole, Nil, NoRepeated) + else { + val getResult = typeOfMemberNamedGet(result) + def noGetError() = { + val name = "unapply" + (if (isSeq) "Seq" else "") + context.error(context.tree.pos, s"The result type of an $name method must contain a member `get` to be used as an extractor pattern, no such member exists in ${result}") + } + val expanded = getResult match { + case global.NoType => noGetError(); Nil case rawGet if !hasSelectors(rawGet) => rawGet :: Nil case rawGet => typesOfSelectors(rawGet) } - ) - expanded match { - case init :+ last if isSeq => newExtractor(whole, init, repeatedFromSeq(last)) - case tps => newExtractor(whole, tps, NoRepeated) + expanded match { + case init :+ last if isSeq => newExtractor(whole, init, repeatedFromSeq(last), getResult) + case tps => newExtractor(whole, tps, NoRepeated, getResult) + } } } } object alignPatterns extends ScalacPatternExpander { - /** Converts a T => (A, B, C) extractor to a T => ((A, B, CC)) extractor. - */ - def tupleExtractor(extractor: Extractor): Extractor = - extractor.copy(fixed = tupleType(extractor.fixed) :: Nil) - private def validateAligned(context: Context, tree: Tree, aligned: Aligned): Aligned = { import aligned._ @@ -129,8 +131,8 @@ trait ScalacPatternExpanders { val isUnapply = sel.symbol.name == nme.unapply val extractor = sel.symbol.name match { - case nme.unapply => unapplyMethodTypes(firstParamType(fn.tpe), sel.tpe, isSeq = false) - case nme.unapplySeq => unapplyMethodTypes(firstParamType(fn.tpe), sel.tpe, isSeq = true) + case nme.unapply => unapplyMethodTypes(context, firstParamType(fn.tpe), sel.tpe, isSeq = false) + case nme.unapplySeq => unapplyMethodTypes(context, firstParamType(fn.tpe), sel.tpe, isSeq = true) case _ => applyMethodTypes(fn.tpe) } @@ -142,12 +144,14 @@ trait ScalacPatternExpanders { def acceptMessage = if (extractor.isErroneous) "" else s" to hold ${extractor.offeringString}" val requiresTupling = isUnapply && patterns.totalArity == 1 && productArity > 1 - if (requiresTupling && effectivePatternArity(args) == 1) { - val sym = sel.symbol.owner - currentRun.reporting.deprecationWarning(sel.pos, sym, s"${sym} expects $productArity patterns$acceptMessage but crushing into $productArity-tuple to fit single pattern (SI-6675)") - } - - val normalizedExtractor = if (requiresTupling) tupleExtractor(extractor) else extractor + val normalizedExtractor = if (requiresTupling) { + val tupled = extractor.asSinglePattern + if (effectivePatternArity(args) == 1 && isTupleType(extractor.typeOfSinglePattern)) { + val sym = sel.symbol.owner + currentRun.reporting.deprecationWarning(sel.pos, sym, s"${sym} expects $productArity patterns$acceptMessage but crushing into $productArity-tuple to fit single pattern (SI-6675)") + } + tupled + } else extractor validateAligned(context, fn, Aligned(patterns, normalizedExtractor)) } |