From f5ed914fe5ae18a884cd2ab8b2cdede7cf11da94 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Tue, 12 Feb 2013 11:24:14 -0800 Subject: [PATCH] re-align 2.10.x's pattern matcher with master's The diff was mostly code cleanup, so most of that was propagated from master to 2.10.x. The remaining diff is encapsulated in compatibility stubs (see below). (There was also the on/off potential for the 2.10.x "new" pattern matcher. The old one is gone in 2.11, so no turning off new patmat, of course.) The stubs: ``` protected final def dealiasWiden(tp: Type) = tp.dealias // 2.11: dealiasWiden protected final def mkTRUE = CODE.TRUE_typed // 2.11: CODE.TRUE protected final def mkFALSE = CODE.FALSE_typed // 2.11: CODE.FALSE protected final def hasStableSymbol(p: Tree) = p.hasSymbol && p.symbol.isStable // 2.11: p.hasSymbolField && p.symbol.isStable protected final def devWarning(str: String) = global.debugwarn(str) // 2.11: omit ``` --- .../nsc/typechecker/PatternMatching.scala | 130 +++++------------- 1 file changed, 37 insertions(+), 93 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index f7579ad24902..edece3fb6756 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -37,13 +37,11 @@ import scala.reflect.internal.Types * - recover exhaustivity/unreachability of user-defined extractors by partitioning the types they match on using an HList or similar type-level structure */ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL { // self: Analyzer => - import Statistics._ import PatternMatchingStats._ val global: Global // need to repeat here because otherwise last mixin defines global as // SymbolTable. If we had DOT this would not be an issue import global._ // the global environment - import definitions._ // standard classes and methods val phaseName: String = "patmat" @@ -109,6 +107,13 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL import definitions._ import analyzer._ //Typer + // 2.10/2.11 compatibility + protected final def dealiasWiden(tp: Type) = tp.dealias // 2.11: dealiasWiden + protected final def mkTRUE = CODE.TRUE_typed // 2.11: CODE.TRUE + protected final def mkFALSE = CODE.FALSE_typed // 2.11: CODE.FALSE + protected final def hasStableSymbol(p: Tree) = p.hasSymbol && p.symbol.isStable // 2.11: p.hasSymbolField && p.symbol.isStable + protected final def devWarning(str: String) = debugwarn(str) // 2.11: omit + object vpmName { val one = newTermName("one") val drop = newTermName("drop") @@ -189,7 +194,6 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL trait MatchTranslation extends MatchMonadInterface { self: TreeMakers with CodegenCore => import typer.{typed, context, silent, reallyExists} - // import typer.infer.containsUnchecked // Why is it so difficult to say "here's a name and a context, give me any // matching symbol in scope" ? I am sure this code is wrong, but attempts to @@ -279,7 +283,9 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // we don't transform after uncurry // (that would require more sophistication when generating trees, // and the only place that emits Matches after typers is for exception handling anyway) - if(phase.id >= currentRun.uncurryPhase.id) debugwarn("running translateMatch at "+ phase +" on "+ selector +" match "+ cases) + if (phase.id >= currentRun.uncurryPhase.id) + devWarning(s"running translateMatch past uncurry (at $phase) on $selector match $cases") + patmatDebug("translating "+ cases.mkString("{", "\n", "}")) val start = if (Statistics.canEnable) Statistics.startTimer(patmatNanos) else null @@ -300,7 +306,6 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL val pt = repeatedToSeq(ptUnCPS) // val packedPt = repeatedToSeq(typer.packedType(match_, context.owner)) - val selectorSym = freshSym(selector.pos, pureType(selectorTp)) setFlag treeInfo.SYNTH_CASE_FLAGS // pt = Any* occurs when compiling test/files/pos/annotDepMethType.scala with -Xexperimental @@ -488,7 +493,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL **/ // must treat Typed and Bind together -- we need to know the patBinder of the Bind pattern to get at the actual type case MaybeBoundTyped(subPatBinder, pt) => - val next = glb(List(patBinder.info.widen, pt)).normalize + val next = glb(List(dealiasWiden(patBinder.info), pt)).normalize // a typed pattern never has any subtrees noFurtherSubPats(TypeTestTreeMaker(subPatBinder, patBinder, pt, next)(pos)) @@ -561,59 +566,8 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// object ExtractorCall { - def apply(unfun: Tree, args: List[Tree]): ExtractorCall = new ExtractorCallRegular(unfun, args) - - def fromCaseClass(fun: Tree, args: List[Tree]): Option[ExtractorCall] = Some(new ExtractorCallProd(fun, args)) - - // THE PRINCIPLED SLOW PATH -- NOT USED - // generate a call to the (synthetically generated) extractor of a case class - // NOTE: it's an apply, not a select, since in general an extractor call may have multiple argument lists (including an implicit one) - // that we need to preserve, so we supply the scrutinee as Ident(nme.SELECTOR_DUMMY), - // and replace that dummy by a reference to the actual binder in translateExtractorPattern - def fromCaseClassUnapply(fun: Tree, args: List[Tree]): Option[ExtractorCall] = { - // TODO: can we rework the typer so we don't have to do all this twice? - // undo rewrite performed in (5) of adapt - val orig = fun match {case tpt: TypeTree => tpt.original case _ => fun} - val origSym = orig.symbol - val extractor = unapplyMember(origSym.filter(sym => reallyExists(unapplyMember(sym.tpe))).tpe) - - if((fun.tpe eq null) || fun.tpe.isError || (extractor eq NoSymbol)) { - None - } else { - // this is a tricky balance: pos/t602.scala, pos/sudoku.scala, run/virtpatmat_alts.scala must all be happy - // bypass typing at own risk: val extractorCall = Select(orig, extractor) setType caseClassApplyToUnapplyTp(fun.tpe) - // can't always infer type arguments (pos/t602): - /* case class Span[K <: Ordered[K]](low: Option[K]) { - override def equals(x: Any): Boolean = x match { - case Span((low0 @ _)) if low0 equals low => true - } - }*/ - // so... leave undetermined type params floating around if we have to - // (if we don't infer types, uninstantiated type params show up later: pos/sudoku.scala) - // (see also run/virtpatmat_alts.scala) - val savedUndets = context.undetparams - val extractorCall = try { - context.undetparams = Nil - silent(_.typed(Apply(Select(orig, extractor), List(Ident(nme.SELECTOR_DUMMY) setType fun.tpe.finalResultType)), EXPRmode, WildcardType), reportAmbiguousErrors = false) match { - case SilentResultValue(extractorCall) => extractorCall // if !extractorCall.containsError() - case _ => - // this fails to resolve overloading properly... - // Apply(typedOperator(Select(orig, extractor)), List(Ident(nme.SELECTOR_DUMMY))) // no need to set the type of the dummy arg, it will be replaced anyway - - // patmatDebug("funtpe after = "+ fun.tpe.finalResultType) - // patmatDebug("orig: "+(orig, orig.tpe)) - val tgt = typed(orig, EXPRmode | QUALmode | POLYmode, HasMember(extractor.name)) // can't specify fun.tpe.finalResultType as the type for the extractor's arg, - // as it may have been inferred incorrectly (see t602, where it's com.mosol.sl.Span[Any], instead of com.mosol.sl.Span[?K]) - // patmatDebug("tgt = "+ (tgt, tgt.tpe)) - val oper = typed(Select(tgt, extractor.name), EXPRmode | FUNmode | POLYmode | TAPPmode, WildcardType) - // patmatDebug("oper: "+ (oper, oper.tpe)) - Apply(oper, List(Ident(nme.SELECTOR_DUMMY))) // no need to set the type of the dummy arg, it will be replaced anyway - } - } finally context.undetparams = savedUndets - - Some(this(extractorCall, args)) // TODO: simplify spliceApply? - } - } + def apply(unfun: Tree, args: List[Tree]): ExtractorCall = new ExtractorCallRegular(unfun, args) + def fromCaseClass(fun: Tree, args: List[Tree]): Option[ExtractorCall] = Some(new ExtractorCallProd(fun, args)) } abstract class ExtractorCall(val args: List[Tree]) { @@ -900,7 +854,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL if (origTp == null || origTp == NoType) to // important: only type when actually substing and when original tree was typed // (don't need to use origTp as the expected type, though, and can't always do this anyway due to unknown type params stemming from polymorphic extractors) - else typer.typed(to, EXPRmode, WildcardType) + else typer.typed(to) override def transform(tree: Tree): Tree = { def subst(from: List[Symbol], to: List[Tree]): Tree = @@ -1197,7 +1151,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL type Result = Tree def and(a: Result, b: Result): Result = a AND b - def tru = TRUE_typed + def tru = mkTRUE def typeTest(testedBinder: Symbol, expectedTp: Type) = codegen._isInstanceOf(testedBinder, expectedTp) def nonNullTest(testedBinder: Symbol) = REF(testedBinder) OBJ_NE NULL def equalsTest(pat: Tree, testedBinder: Symbol) = codegen._equals(pat, testedBinder) @@ -1207,7 +1161,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL val expectedOuter = expectedTp.prefix match { case ThisType(clazz) => THIS(clazz) case pre if pre != NoType => REF(pre.prefix, pre.termSymbol) - case _ => TRUE_typed // fallback for SI-6183 + case _ => mkTRUE // fallback for SI-6183 } // ExplicitOuter replaces `Select(q, outerSym) OBJ_EQ expectedPrefix` by `Select(q, outerAccessor(outerSym.owner)) OBJ_EQ expectedPrefix` @@ -1360,10 +1314,10 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // one alternative may still generate multiple trees (e.g., an extractor call + equality test) // (for now,) alternatives may not bind variables (except wildcards), so we don't care about the final substitution built internally by makeTreeMakers val combinedAlts = altss map (altTreeMakers => - ((casegen: Casegen) => combineExtractors(altTreeMakers :+ TrivialTreeMaker(casegen.one(TRUE_typed)))(casegen)) + ((casegen: Casegen) => combineExtractors(altTreeMakers :+ TrivialTreeMaker(casegen.one(mkTRUE)))(casegen)) ) - val findAltMatcher = codegenAlt.matcher(EmptyTree, NoSymbol, BooleanClass.tpe)(combinedAlts, Some(x => FALSE_typed)) + val findAltMatcher = codegenAlt.matcher(EmptyTree, NoSymbol, BooleanClass.tpe)(combinedAlts, Some(x => mkFALSE)) codegenAlt.ifThenElseZero(findAltMatcher, substitution(next)) } } @@ -1506,10 +1460,8 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // local / context-free def _asInstanceOf(b: Symbol, tp: Type): Tree - def _asInstanceOf(t: Tree, tp: Type): Tree def _equals(checker: Tree, binder: Symbol): Tree def _isInstanceOf(b: Symbol, tp: Type): Tree - def and(a: Tree, b: Tree): Tree def drop(tgt: Tree)(n: Int): Tree def index(tgt: Tree)(i: Int): Tree def mkZero(tp: Type): Tree @@ -1523,7 +1475,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def flatMap(prev: Tree, b: Symbol, next: Tree): Tree def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, next: Tree): Tree def flatMapGuard(cond: Tree, next: Tree): Tree - def ifThenElseZero(c: Tree, then: Tree): Tree = IF (c) THEN then ELSE zero + def ifThenElseZero(c: Tree, thenp: Tree): Tree = IF (c) THEN thenp ELSE zero protected def zero: Tree } @@ -1551,18 +1503,14 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL abstract class CommonCodegen extends AbsCodegen { import CODE._ def fun(arg: Symbol, body: Tree): Tree = Function(List(ValDef(arg)), body) - def genTypeApply(tfun: Tree, args: Type*): Tree = if(args contains NoType) tfun else TypeApply(tfun, args.toList map TypeTree) def tupleSel(binder: Symbol)(i: Int): Tree = (REF(binder) DOT nme.productAccessorName(i)) // make tree that accesses the i'th component of the tuple referenced by binder def index(tgt: Tree)(i: Int): Tree = tgt APPLY (LIT(i)) def drop(tgt: Tree)(n: Int): Tree = (tgt DOT vpmName.drop) (LIT(n)) def _equals(checker: Tree, binder: Symbol): Tree = checker MEMBER_== REF(binder) // NOTE: checker must be the target of the ==, that's the patmat semantics for ya - def and(a: Tree, b: Tree): Tree = a AND b // the force is needed mainly to deal with the GADT typing hack (we can't detect it otherwise as tp nor pt need contain an abstract type, we're just casting wildly) - def _asInstanceOf(t: Tree, tp: Type): Tree = if (t.tpe != NoType && t.isTyped && typesConform(t.tpe, tp)) t else gen.mkCastPreservingAnnotations(t, tp) def _asInstanceOf(b: Symbol, tp: Type): Tree = if (typesConform(b.info, tp)) REF(b) else gen.mkCastPreservingAnnotations(REF(b), tp) def _isInstanceOf(b: Symbol, tp: Type): Tree = gen.mkIsInstanceOf(REF(b), tp.withoutAnnotations, true, false) - // if (typesConform(b.info, tpX)) { patmatDebug("warning: emitted spurious isInstanceOf: "+(b, tp)); TRUE } // duplicated out of frustration with cast generation def mkZero(tp: Type): Tree = { @@ -1592,8 +1540,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL import CODE._ def _match(n: Name): SelectStart = matchStrategy DOT n - private lazy val oneSig: Type = - typer.typed(_match(vpmName.one), EXPRmode | POLYmode | TAPPmode | FUNmode, WildcardType).tpe // TODO: error message + private lazy val oneSig: Type = typer.typedOperator(_match(vpmName.one)).tpe // TODO: error message } trait PureCodegen extends CodegenCore with PureMatchMonadInterface { @@ -1611,7 +1558,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // __match.zero protected def zero: Tree = _match(vpmName.zero) // __match.guard(`c`, `then`) - def guard(c: Tree, then: Tree): Tree = _match(vpmName.guard) APPLY (c, then) + def guard(c: Tree, thenp: Tree): Tree = _match(vpmName.guard) APPLY (c, thenp) //// methods in the monad instance -- used directly in translation // `prev`.flatMap(`b` => `next`) @@ -1905,7 +1852,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def toString(x: AnyRef) = if (x eq null) "" else x.toString if (cols.isEmpty || cols.tails.isEmpty) cols map toString else { - val (colStrs, colLens) = cols map {c => val s = toString(c); (s, s.length)} unzip + val colLens = cols map (c => toString(c).length) val maxLen = max(colLens) val avgLen = colLens.sum/colLens.length val goalLen = maxLen min avgLen*2 @@ -2174,7 +2121,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // throws an AnalysisBudget.Exception when the prop results in a CNF that's too big // TODO: be smarter/more efficient about this (http://lara.epfl.ch/w/sav09:tseitin_s_encoding) def eqFreePropToSolvable(p: Prop): Formula = { - def negationNormalFormNot(p: Prop, budget: Int = AnalysisBudget.max): Prop = + def negationNormalFormNot(p: Prop, budget: Int): Prop = if (budget <= 0) throw AnalysisBudget.exceeded else p match { case And(a, b) => Or(negationNormalFormNot(a, budget - 1), negationNormalFormNot(b, budget - 1)) @@ -2388,9 +2335,9 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL private[this] val id: Int = Var.nextId // private[this] var canModify: Option[Array[StackTraceElement]] = None - private[this] def ensureCanModify = {} //if (canModify.nonEmpty) patmatDebug("BUG!"+ this +" modified after having been observed: "+ canModify.get.mkString("\n")) + private[this] def ensureCanModify() = {} //if (canModify.nonEmpty) patmatDebug("BUG!"+ this +" modified after having been observed: "+ canModify.get.mkString("\n")) - private[this] def observed = {} //canModify = Some(Thread.currentThread.getStackTrace) + private[this] def observed() = {} //canModify = Some(Thread.currentThread.getStackTrace) // don't access until all potential equalities have been registered using registerEquality private[this] val symForEqualsTo = new scala.collection.mutable.HashMap[Const, Sym] @@ -2543,7 +2490,13 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL private lazy val equalitySyms = {observed; symForEqualsTo.values.toList} // don't call until all equalities have been registered and registerNull has been called (if needed) - def describe = toString + ": " + staticTp + domain.map(_.mkString(" ::= ", " | ", "// "+ symForEqualsTo.keys)).getOrElse(symForEqualsTo.keys.mkString(" ::= ", " | ", " | ...")) + " // = " + path + def describe = { + def domain_s = domain match { + case Some(d) => d mkString (" ::= ", " | ", "// "+ symForEqualsTo.keys) + case _ => symForEqualsTo.keys mkString (" ::= ", " | ", " | ...") + } + s"$this: ${staticTp}${domain_s} // = $path" + } override def toString = "V"+ id } @@ -2629,7 +2582,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // corresponds to a type test that does not imply any value-equality (well, except for outer checks, which we don't model yet) sealed class TypeConst(val tp: Type) extends Const { assert(!(tp =:= NullTp)) - private[this] val id: Int = Const.nextTypeId + /*private[this] val id: Int = */ Const.nextTypeId val wideTp = widenToClass(tp) def isValue = false @@ -2667,7 +2620,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL } val toString = - if (p.hasSymbol && p.symbol.isStable) p.symbol.name.toString // tp.toString + if (hasStableSymbol(p)) p.symbol.name.toString // tp.toString else p.toString //+"#"+ id Const.unique(narrowTp, new ValueConst(narrowTp, checkableType(wideTp), toString)) // must make wide type checkable so that it is comparable to types from TypeConst @@ -2677,7 +2630,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL sealed class ValueConst(val tp: Type, val wideTp: Type, override val toString: String) extends Const { // patmatDebug("VC"+(tp, wideTp, toString)) assert(!(tp =:= NullTp)) // TODO: assert(!tp.isStable) - private[this] val id: Int = Const.nextValueId + /*private[this] val id: Int = */Const.nextValueId def isValue = true } @@ -2904,7 +2857,6 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // when does the match fail? val matchFails = Not(\/(symbolicCases)) - val vars = gatherVariables(matchFails) // debug output: patmatDebug("analysing:") @@ -3002,8 +2954,6 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL v +"(="+ v.path +": "+ v.staticTpCheckable +") "+ assignment }.mkString("\n") - def modelString(model: Model) = varAssignmentString(modelToVarAssignment(model)) - // return constructor call when the model is a true counter example // (the variables don't take into account type information derived from other variables, // so, naively, you might try to construct a counter example like _ :: Nil(_ :: _, _ :: _), @@ -3643,7 +3593,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL override def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type, matchFailGenOverride: Option[Tree => Tree], unchecked: Boolean): Option[Tree] = { import CODE._ val regularSwitchMaker = new RegularSwitchMaker(scrutSym, matchFailGenOverride, unchecked) // TODO: if patterns allow switch but the type of the scrutinee doesn't, cast (type-test) the scrutinee to the corresponding switchable type and switch on the result - if (regularSwitchMaker.switchableTpe(scrutSym.tpe.dealias)) { // TODO: switch to dealiasWiden in 2.11 + if (regularSwitchMaker.switchableTpe(dealiasWiden(scrutSym.tpe))) { val caseDefsWithDefault = regularSwitchMaker(cases map {c => (scrutSym, c)}, pt) if (caseDefsWithDefault isEmpty) None // not worth emitting a switch. else { @@ -3662,7 +3612,6 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // for the catch-cases in a try/catch private object typeSwitchMaker extends SwitchMaker { val unchecked = false - def switchableTpe(tp: Type) = true val alternativesSupported = false // TODO: needs either back-end support of flattening of alternatives during typers val canJump = false @@ -3708,11 +3657,6 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL trait OptimizedCodegen extends CodegenCore with TypedSubstitution with OptimizedMatchMonadInterface { override def codegen: AbsCodegen = optimizedCodegen - // trait AbsOptimizedCodegen extends AbsCodegen { - // def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree - // } - // def optimizedCodegen: AbsOptimizedCodegen - // when we know we're targetting 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 @@ -3815,7 +3759,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree = ifThenElseZero(cond, BLOCK( - condSym === TRUE_typed, + condSym === mkTRUE, nextBinder === res, next ))