From db2e756e0e12a09f162266e7be13ec03a133a44c Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Sat, 17 Aug 2013 09:46:12 -0700 Subject: Stylistic cleanups in patmat. Should be behaviorally neutral. --- .../tools/nsc/transform/patmat/MatchCodeGen.scala | 10 ++--- .../nsc/transform/patmat/MatchTranslation.scala | 46 ++++++++++++---------- .../nsc/transform/patmat/MatchTreeMaking.scala | 12 +++++- .../nsc/transform/patmat/PatternMatching.scala | 5 ++- .../tools/nsc/typechecker/PatternTypers.scala | 24 +++++------ 5 files changed, 57 insertions(+), 40 deletions(-) diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala index 8eab776f3d..77a6b3940c 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala @@ -145,9 +145,8 @@ trait MatchCodeGen extends Interface { * if keepGoing is false, the result Some(x) of the naive translation is encoded as matchRes == x */ def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], matchFailGen: Option[Tree => Tree]): Tree = { - val matchEnd = newSynthCaseLabel("matchEnd") val matchRes = NoSymbol.newValueParameter(newTermName("x"), NoPosition, newFlags = SYNTHETIC) setInfo restpe.withoutAnnotations - matchEnd setInfo MethodType(List(matchRes), restpe) + val matchEnd = newSynthCaseLabel("matchEnd") setInfo MethodType(List(matchRes), restpe) def newCaseSym = newSynthCaseLabel("case") setInfo MethodType(Nil, restpe) var _currCase = newCaseSym @@ -159,7 +158,6 @@ trait MatchCodeGen extends Interface { LabelDef(currCase, Nil, mkCase(new OptimizedCasegen(matchEnd, nextCase))) } - // must compute catchAll after caseLabels (side-effects nextCase) // catchAll.isEmpty iff no synthetic default case needed (the (last) user-defined case is a default) // if the last user-defined case is a default, it will never jump to the next case; it will go immediately to matchEnd @@ -173,9 +171,9 @@ trait MatchCodeGen extends Interface { val scrutDef = if(scrutSym ne NoSymbol) List(VAL(scrutSym) === scrut) else Nil // for alternatives // the generated block is taken apart in TailCalls under the following assumptions - // the assumption is once we encounter a case, the remainder of the block will consist of cases - // the prologue may be empty, usually it is the valdef that stores the scrut - // val (prologue, cases) = stats span (s => !s.isInstanceOf[LabelDef]) + // the assumption is once we encounter a case, the remainder of the block will consist of cases + // the prologue may be empty, usually it is the valdef that stores the scrut + // val (prologue, cases) = stats span (s => !s.isInstanceOf[LabelDef]) Block( scrutDef ++ caseDefs ++ catchAllDef, LabelDef(matchEnd, List(matchRes), REF(matchRes)) diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala index 4f89287bf5..c14b8919dd 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala @@ -538,23 +538,17 @@ trait MatchTranslation extends CpsPatternHacks { // no need to check unless it's an unapplySeq and the minimal length is non-trivially satisfied if (!isSeq || (expectedLength < minLenToCheck)) None else Some(expectedLength) - } // TODO: to be called when there's a def unapplyProd(x: T): U // U must have N members _1,..., _N -- the _i are type checked, call their type Ti, - // // for now only used for case classes -- pretending there's an unapplyProd that's the identity (and don't call it) class ExtractorCallProd(fun: Tree, args: List[Tree]) extends ExtractorCall(args) { // TODO: fix the illegal type bound in pos/t602 -- type inference messes up before we get here: /*override def equals(x$1: Any): Boolean = ... val o5: Option[com.mosol.sl.Span[Any]] = // Span[Any] --> Any is not a legal type argument for Span! */ - // private val orig = fun match {case tpt: TypeTree => tpt.original case _ => fun} - // private val origExtractorTp = unapplyMember(orig.symbol.filter(sym => reallyExists(unapplyMember(sym.tpe))).tpe).tpe - // private val extractorTp = if (wellKinded(fun.tpe)) fun.tpe else existentialAbstraction(origExtractorTp.typeParams, origExtractorTp.resultType) - // debug.patmat("ExtractorCallProd: "+ (fun.tpe, existentialAbstraction(origExtractorTp.typeParams, origExtractorTp.resultType))) - // debug.patmat("ExtractorCallProd: "+ (fun.tpe, args map (_.tpe))) + private def constructorTp = fun.tpe def isTyped = fun.isTyped @@ -562,7 +556,8 @@ trait MatchTranslation extends CpsPatternHacks { // to which type should the previous binder be casted? def paramType = constructorTp.finalResultType - def isSeq: Boolean = rawSubPatTypes.nonEmpty && isRepeatedParamType(rawSubPatTypes.last) + def isSeq = isVarArgTypes(rawSubPatTypes) + protected def rawSubPatTypes = constructorTp.paramTypes /** Create the TreeMaker that embodies this extractor call @@ -604,6 +599,7 @@ trait MatchTranslation extends CpsPatternHacks { def paramType = tpe.paramTypes.head def resultType = tpe.finalResultType def isSeq = extractorCall.symbol.name == nme.unapplySeq + def isBool = resultType =:= BooleanTpe /** Create the TreeMaker that embodies this extractor call * @@ -616,10 +612,22 @@ trait MatchTranslation extends CpsPatternHacks { * Perhaps it hasn't reached critical mass, but it would already clean things up a touch. */ def treeMaker(patBinderOrCasted: Symbol, binderKnownNonNull: Boolean, pos: Position): TreeMaker = { - // the extractor call (applied to the binder bound by the flatMap corresponding to the previous (i.e., enclosing/outer) pattern) + // the extractor call (applied to the binder bound by the flatMap corresponding + // to the previous (i.e., enclosing/outer) pattern) val extractorApply = atPos(pos)(spliceApply(patBinderOrCasted)) - val binder = freshSym(pos, pureType(resultInMonad)) // can't simplify this when subPatBinders.isEmpty, since UnitTpe is definitely wrong when isSeq, and resultInMonad should always be correct since it comes directly from the extractor's result type - ExtractorTreeMaker(extractorApply, lengthGuard(binder), binder)(subPatBinders, subPatRefs(binder), resultType.typeSymbol == BooleanClass, checkedLength, patBinderOrCasted, ignoredSubPatBinders) + // can't simplify this when subPatBinders.isEmpty, since UnitTpe is definitely + // wrong when isSeq, and resultInMonad should always be correct since it comes + // directly from the extractor's result type + val binder = freshSym(pos, pureType(resultInMonad)) + + ExtractorTreeMaker(extractorApply, lengthGuard(binder), binder)( + subPatBinders, + subPatRefs(binder), + isBool, + checkedLength, + patBinderOrCasted, + ignoredSubPatBinders + ) } override protected def seqTree(binder: Symbol): Tree = @@ -636,19 +644,17 @@ trait MatchTranslation extends CpsPatternHacks { object splice extends Transformer { override def transform(t: Tree) = t match { case Apply(x, List(i @ Ident(nme.SELECTOR_DUMMY))) => - treeCopy.Apply(t, x, List(CODE.REF(binder).setPos(i.pos))) - case _ => super.transform(t) + treeCopy.Apply(t, x, List(CODE.REF(binder) setPos i.pos)) + case _ => + super.transform(t) } } - splice.transform(extractorCallIncludingDummy) + splice transform extractorCallIncludingDummy } // what's the extractor's result type in the monad? // turn an extractor's result type into something `monadTypeToSubPatTypesAndRefs` understands - protected lazy val resultInMonad: Type = if(!hasLength(tpe.paramTypes, 1)) ErrorType else { - if (resultType.typeSymbol == BooleanClass) UnitTpe - else matchMonadResult(resultType) - } + protected lazy val resultInMonad: Type = if (isBool) UnitTpe else matchMonadResult(resultType) // the type of "get" protected lazy val rawSubPatTypes = if (resultInMonad.typeSymbol eq UnitClass) Nil @@ -670,7 +676,7 @@ trait MatchTranslation extends CpsPatternHacks { case Ident(nme.WILDCARD) => true case Star(WildcardPattern()) => true case x: Ident => treeInfo.isVarPattern(x) - case Alternative(ps) => ps forall (WildcardPattern.unapply(_)) + case Alternative(ps) => ps forall unapply case EmptyTree => true case _ => false } @@ -680,7 +686,7 @@ trait MatchTranslation extends CpsPatternHacks { def unapply(pat: Tree): Boolean = pat match { case Bind(nme.WILDCARD, _) => true // don't skip when binding an interesting symbol! case Ident(nme.WILDCARD) => true - case Alternative(ps) => ps forall (PatternBoundToUnderscore.unapply(_)) + case Alternative(ps) => ps forall unapply case Typed(PatternBoundToUnderscore(), _) => true case _ => false } diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala index baccdcf544..921c3ca1b5 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala @@ -201,6 +201,16 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { def extraStoredBinders: Set[Symbol] = Set() + debug.patmat(s""" + |ExtractorTreeMaker($extractor, $extraCond, $nextBinder) { + | $subPatBinders + | $subPatRefs + | $extractorReturnsBoolean + | $checkedLength + | $prevBinder + | $ignoredSubPatBinders + |}""".stripMargin) + def chainBefore(next: Tree)(casegen: Casegen): Tree = { val condAndNext = extraCond match { case Some(cond) => @@ -426,7 +436,7 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { case _ if testedBinder.info.widen <:< expectedTp => // if the expected type is a primitive value type, it cannot be null and it cannot have an outer pointer // since the types conform, no further checking is required - if (expectedTp.typeSymbol.isPrimitiveValueClass) tru + if (isPrimitiveValueType(expectedTp)) tru // have to test outer and non-null only when it's a reference type else if (expectedTp <:< AnyRefTpe) { // do non-null check first to ensure we won't select outer on null diff --git a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala index b37b1d8550..aa923b1059 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala @@ -94,7 +94,7 @@ trait Debugging { // TODO: the inliner fails to inline the closures to debug.patmat unless the method is nested in an object object debug { val printPatmat = global.settings.Ypatmatdebug.value - @inline final def patmat(s: => String) = if (printPatmat) println(s) + @inline final def patmat(s: => String) = if (printPatmat) Console.err.println(s) @inline final def patmatResult[T](s: => String)(result: T): T = { if (printPatmat) Console.err.println(s + ": " + result) result @@ -103,7 +103,8 @@ trait Debugging { } trait Interface extends ast.TreeDSL { - import global.{newTermName, analyzer, Type, ErrorType, Symbol, Tree} + import global._ + import definitions._ import analyzer.Typer // 2.10/2.11 compatibility diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala index 6b4098761a..9416fcb651 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala @@ -313,23 +313,25 @@ trait PatternTypers { def wrapClassTagUnapply(uncheckedPattern: Tree, classTagExtractor: Tree, pt: Type): Tree = { // TODO: disable when in unchecked match - // we don't create a new Context for a Match, so find the CaseDef, then go out one level and navigate back to the match that has this case - // val thisCase = context.nextEnclosing(_.tree.isInstanceOf[CaseDef]) - // val unchecked = thisCase.outer.tree.collect{case Match(selector, cases) if cases contains thisCase => selector} match { - // case List(Typed(_, tpt)) if tpt.tpe hasAnnotation UncheckedClass => true - // case t => println("outer tree: "+ (t, thisCase, thisCase.outer.tree)); false - // } - // println("wrapClassTagUnapply"+ (!isPastTyper && infer.containsUnchecked(pt), pt, uncheckedPattern)) - // println("wrapClassTagUnapply: "+ extractor) - // println(util.Position.formatMessage(uncheckedPattern.pos, "made unchecked type test into a checked one", true)) - + // we don't create a new Context for a Match, so find the CaseDef, + // then go out one level and navigate back to the match that has this case val args = List(uncheckedPattern) val app = atPos(uncheckedPattern.pos)(Apply(classTagExtractor, args)) // must call doTypedUnapply directly, as otherwise we get undesirable rewrites // and re-typechecks of the target of the unapply call in PATTERNmode, // this breaks down when the classTagExtractor (which defineds the unapply member) is not a simple reference to an object, // but an arbitrary tree as is the case here - doTypedUnapply(app, classTagExtractor, classTagExtractor, args, PATTERNmode, pt) + val res = doTypedUnapply(app, classTagExtractor, classTagExtractor, args, PATTERNmode, pt) + + log(sm""" + |wrapClassTagUnapply { + | pattern: $uncheckedPattern + | extract: $classTagExtractor + | pt: $pt + | res: $res + |}""".trim) + + res } // if there's a ClassTag that allows us to turn the unchecked type test for `pt` into a checked type test -- cgit v1.2.3