diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala | 238 |
1 files changed, 128 insertions, 110 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala index b1e02cb062..49786813e8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala @@ -32,9 +32,6 @@ import Flags.{ CASE => _, _ } d => body)))))(scrut) TODO: - - optimizer loops on virtpatmat compiler? - - - don't orElse a failure case at the end if there's a default case - implement spec more closely (see TODO's below) - fix inlining of methods in nested objects @@ -139,6 +136,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => // must use type `tp`, which is provided by extractor's result, not the type expected by binder, // as b.info may be based on a Typed type ascription, which has not been taken into account yet by the translation // (it will later result in a type test when `tp` is not a subtype of `b.info`) + // TODO: can we simplify this, together with the Bound case? (extractor.subPatBinders, extractor.subPatTypes).zipped foreach { case (b, tp) => b setInfo tp } // println("changing "+ b +" : "+ b.info +" -> "+ tp); // println("translateExtractorPattern checking parameter type: "+ (patBinder, patBinder.info.widen, extractor.paramType, patBinder.info.widen <:< extractor.paramType)) @@ -215,12 +213,8 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => and it binds the variable name to that value. **/ case Bound(subpatBinder, p) => - // TreeMaker with empty list of trees only performs the substitution subpatBinder --> patBinder - // println("rebind "+ subpatBinder +" to "+ patBinder) - withSubPats(List(SubstOnlyTreeMaker(Substitution(subpatBinder, CODE.REF(patBinder)))), - // the symbols are markers that may be used to refer to the result of the extractor in which the corresponding tree is nested - // it's the responsibility of the treemaker to replace this symbol by a reference that - // selects that result on the function symbol of the flatMap call that binds to the result of this extractor + // replace subpatBinder by patBinder (as if the Bind was not there) + withSubPats(List(SubstOnlyTreeMaker(subpatBinder, patBinder)), // must be patBinder, as subpatBinder has the wrong info: even if the bind assumes a better type, this is not guaranteed until we cast (patBinder, p) ) @@ -651,6 +645,9 @@ defined class Foo */ lazy val optimizingCodeGen = matchingMonadType.typeSymbol eq OptionClass abstract class TreeMaker { + /** captures the scope and the value of the bindings in patterns + * important *when* the substitution happens (can't accumulate and do at once after the full matcher has been constructed) + */ def substitution: Substitution = if (currSub eq null) localSubstitution else currSub @@ -668,7 +665,6 @@ defined class Foo */ // build Tree that chains `next` after the current extractor def chainBefore(next: Tree, pt: Type): Tree - def treesToHoist: List[Tree] = Nil } case class TrivialTreeMaker(tree: Tree) extends TreeMaker { @@ -682,20 +678,13 @@ defined class Foo */ atPos(body.pos)(substitution(pmgen.one(body, body.tpe, matchPt))) // since SubstOnly treemakers are dropped, need to do it here } - case class SubstOnlyTreeMaker(localSubstitution: Substitution) extends TreeMaker { + case class SubstOnlyTreeMaker(prevBinder: Symbol, nextBinder: Symbol) extends TreeMaker { + val localSubstitution = Substitution(prevBinder, CODE.REF(nextBinder)) def chainBefore(next: Tree, pt: Type): Tree = substitution(next) } abstract class FunTreeMaker extends TreeMaker { val nextBinder: Symbol - - // for CSE (used iff optimizingCodeGen) - // TODO: factor this out -- don't mutate treemakers - var reused: Boolean = false - def reusedBinders: List[Symbol] = Nil - override def treesToHoist: List[Tree] = { import CODE._ - reusedBinders map { b => VAL(b) === pmgen.mkZero(b.info) } - } } abstract class FreshFunTreeMaker extends FunTreeMaker { @@ -706,44 +695,12 @@ defined class Foo */ lazy val localSubstitution = Substitution(List(prevBinder), List(CODE.REF(nextBinder))) } - // TODO: factor out optimization-specific stuff into codegen - abstract class CondTreeMaker extends FreshFunTreeMaker { import CODE._ + abstract class CondTreeMaker extends FreshFunTreeMaker { val cond: Tree val res: Tree - // for CSE (used iff optimizingCodeGen) - // must set reused before! - override lazy val reusedBinders = if(reused) List(freshSym(pos, BooleanClass.tpe, "rc") setFlag MUTABLE, nextBinder setFlag MUTABLE) else Nil - def storedCond = reusedBinders(0) - def storedRes = reusedBinders(1) - def chainBefore(next: Tree, pt: Type): Tree = - if (!reused) - atPos(pos)(pmgen.flatMapCond(cond, res, nextBinder, nextBinderTp, substitution(next))) - else { // for CSE (used iff optimizingCodeGen) - IF (cond) THEN BLOCK( - storedCond === TRUE, - storedRes === res, - substitution(next).duplicate // TODO: finer-grained dup'ing - ) ELSE pmgen.zero - } - } - - // for CSE (used iff optimizingCodeGen) - case class ReusingCondTreeMaker(dropped_priors: List[(TreeMaker, Option[TreeMaker])]) extends TreeMaker { import CODE._ - lazy val localSubstitution = { - val (from, to) = dropped_priors.collect {case (dropped: CondTreeMaker, Some(prior: CondTreeMaker)) => (dropped.nextBinder, REF(prior.storedRes))}.unzip - val oldSubs = dropped_priors.collect {case (dropped: TreeMaker, _) => dropped.substitution} - oldSubs.foldLeft(Substitution(from, to))(_ >> _) - } - - def chainBefore(next: Tree, pt: Type): Tree = { - val cond = REF(dropped_priors.reverse.collectFirst{case (_, Some(ctm: CondTreeMaker)) => ctm}.get.storedCond) - - IF (cond) THEN BLOCK( - substitution(next).duplicate // TODO: finer-grained duplication -- MUST duplicate though, or we'll get VerifyErrors since sharing trees confuses lambdalift, and its confusion it emits illegal casts (diagnosed by Grzegorz: checkcast T ; invokevirtual S.m, where T not a subtype of S) - ) ELSE pmgen.zero - } + atPos(pos)(pmgen.flatMapCond(cond, res, nextBinder, nextBinderTp, substitution(next))) } /** @@ -754,12 +711,13 @@ defined class Foo */ * in this function's body, and all the subsequent ones, references to the symbols in `from` will be replaced by the corresponding tree in `to` */ case class ExtractorTreeMaker(extractor: Tree, extraCond: Option[Tree], nextBinder: Symbol, localSubstitution: Substitution)(extractorReturnsBoolean: Boolean) extends FunTreeMaker { - def chainBefore(next: Tree, pt: Type): Tree = atPos(extractor.pos)( - if (extractorReturnsBoolean) pmgen.flatMapCond(extractor, CODE.UNIT, nextBinder, nextBinder.info.widen, substitution(condAndNext(next))) - else pmgen.flatMap(extractor, pmgen.fun(nextBinder, substitution(condAndNext(next)))) - ) - - private def condAndNext(next: Tree): Tree = extraCond map (pmgen.condOptimized(_, next)) getOrElse next + def chainBefore(next: Tree, pt: Type): Tree = { + val condAndNext = extraCond map (pmgen.condOptimized(_, next)) getOrElse next + atPos(extractor.pos)( + if (extractorReturnsBoolean) pmgen.flatMapCond(extractor, CODE.UNIT, nextBinder, nextBinder.info.widen, substitution(condAndNext)) + else pmgen.flatMap(extractor, nextBinder, substitution(condAndNext)) + ) + } override def toString = "X"+(extractor, nextBinder) } @@ -768,10 +726,7 @@ defined class Foo */ case class ProductExtractorTreeMaker(prevBinder: Symbol, extraCond: Option[Tree], localSubstitution: Substitution) extends TreeMaker { import CODE._ def chainBefore(next: Tree, pt: Type): Tree = { val nullCheck = REF(prevBinder) OBJ_NE NULL - val cond = extraCond match { - case None => nullCheck - case Some(c) => nullCheck AND c - } + val cond = extraCond map (nullCheck AND _) getOrElse nullCheck pmgen.condOptimized(cond, substitution(next)) } @@ -957,6 +912,8 @@ defined class Foo */ override def toString = testedPath +" (<: && ==) "+ pt +"#"+ id } +//// CSE + /** a flow-sensitive, generalised, common sub-expression elimination * reuse knowledge from performed tests * the only sub-expressions we consider are the conditions and results of the three tests (type, type&equality, equality) @@ -1031,7 +988,7 @@ defined class Foo */ | GuardTreeMaker(_) | ProductExtractorTreeMaker(_, Some(_), _) => Havoc case AlternativesTreeMaker(_, _, _) => Havoc // TODO: can do better here - case SubstOnlyTreeMaker(_) => Top + case SubstOnlyTreeMaker(_, _) => Top case BodyTreeMaker(_, _) => Havoc }, tm) } @@ -1067,7 +1024,11 @@ defined class Foo */ // then, collapse these contiguous sequences of reusing tests // store the result of the final test and the intermediate results in hoisted mutable variables (TODO: optimize: don't store intermediate results that aren't used) // replace each reference to a variable originally bound by a collapsed test by a reference to the hoisted variable - testss map { tests => + val reused = new collection.mutable.HashMap[TreeMaker, ReusedCondTreeMaker] + var okToCall = false + val reusedOrOrig = (tm: TreeMaker) => {assert(okToCall); reused.getOrElse(tm, tm)} + + val res = testss map { tests => var currDeps = Set[Cond]() val (sharedPrefix, suffix) = tests span { test => (test.cond eq Top) || (for( @@ -1079,18 +1040,66 @@ defined class Foo */ } val collapsedTreeMakers = if (sharedPrefix.nonEmpty) { // even sharing prefixes of length 1 brings some benefit (overhead-percentage for compiler: 26->24%, lib: 19->16%) - for (test <- sharedPrefix; reusedTest <- test.reuses; if reusedTest.treeMaker.isInstanceOf[FunTreeMaker]) - reusedTest.treeMaker.asInstanceOf[FunTreeMaker].reused = true + for (test <- sharedPrefix; reusedTest <- test.reuses) reusedTest.treeMaker match { + case reusedCTM: CondTreeMaker => reused(reusedCTM) = ReusedCondTreeMaker(reusedCTM) + case _ => + } + // println("sharedPrefix: "+ sharedPrefix) for (lastShared <- sharedPrefix.reverse.dropWhile(_.cond eq Top).headOption; lastReused <- lastShared.reuses) - yield ReusingCondTreeMaker(sharedPrefix map (t => (t.treeMaker, t.reuses map (_.treeMaker)))) :: suffix.map(_.treeMaker) + yield ReusingCondTreeMaker(sharedPrefix, reusedOrOrig) :: suffix.map(_.treeMaker) } else None collapsedTreeMakers getOrElse tests.map(_.treeMaker) // sharedPrefix need not be empty (but it only contains Top-tests, which are dropped above) } + okToCall = true // TODO: remove (debugging) + + res mapConserve (_ mapConserve reusedOrOrig) } + object ReusedCondTreeMaker { + def apply(orig: CondTreeMaker) = new ReusedCondTreeMaker(orig.prevBinder, orig.nextBinder, orig.cond, orig.res, orig.pos) + } + class ReusedCondTreeMaker(prevBinder: Symbol, val nextBinder: Symbol, cond: Tree, res: Tree, pos: Position) extends TreeMaker { import CODE._ + lazy val localSubstitution = Substitution(List(prevBinder), List(CODE.REF(nextBinder))) + lazy val storedCond = freshSym(pos, BooleanClass.tpe, "rc") setFlag MUTABLE + lazy val treesToHoist: List[Tree] = { + nextBinder setFlag MUTABLE + List(storedCond, nextBinder) map { b => VAL(b) === pmgen.mkZero(b.info) } + } + + // TODO: finer-grained duplication + def chainBefore(next: Tree, pt: Type): Tree = + atPos(pos)(pmgen.flatMapCondStored(cond, storedCond, res, nextBinder, substitution(next).duplicate)) + } + + case class ReusingCondTreeMaker(sharedPrefix: List[Test], toReused: TreeMaker => TreeMaker) extends TreeMaker { import CODE._ + lazy val dropped_priors = sharedPrefix map (t => (toReused(t.treeMaker), t.reuses map (test => toReused(test.treeMaker)))) + lazy val localSubstitution = { + val (from, to) = dropped_priors.collect { + case (dropped: CondTreeMaker, Some(prior: ReusedCondTreeMaker)) => + (dropped.nextBinder, REF(prior.nextBinder)) + }.unzip + val oldSubs = dropped_priors.collect { + case (dropped: TreeMaker, _) => + dropped.substitution + } + oldSubs.foldLeft(Substitution(from, to))(_ >> _) + } + + def chainBefore(next: Tree, pt: Type): Tree = { + val cond = REF(dropped_priors.reverse.collectFirst{case (_, Some(ctm: ReusedCondTreeMaker)) => ctm}.get.storedCond) + + IF (cond) THEN BLOCK( + substitution(next).duplicate // TODO: finer-grained duplication -- MUST duplicate though, or we'll get VerifyErrors since sharing trees confuses lambdalift, and its confusion it emits illegal casts (diagnosed by Grzegorz: checkcast T ; invokevirtual S.m, where T not a subtype of S) + ) ELSE pmgen.zero + } + } + + +//// DCE + // TODO: non-trivial dead-code elimination // e.g., the following match should compile to a simple instanceof: // case class Ident(name: String) @@ -1101,18 +1110,7 @@ defined class Foo */ } - def removeSubstOnly(makers: List[TreeMaker]) = makers filterNot (_.isInstanceOf[SubstOnlyTreeMaker]) - - // a foldLeft to accumulate the localSubstitution left-to-right - // it drops SubstOnly tree makers, since their only goal in life is to propagate substitutions to the next tree maker, which is fullfilled by propagateSubstitution - def propagateSubstitution(treeMakers: List[TreeMaker], initial: Substitution): List[TreeMaker] = { - var accumSubst: Substitution = initial - treeMakers foreach { maker => - maker incorporateOuterSubstitution accumSubst - accumSubst = maker.substitution - } - removeSubstOnly(treeMakers) - } +//// SWITCHES object SwitchablePattern { def unapply(pat: Tree) = pat match { case Literal(Constant((_: Byte ) | (_: Short) | (_: Int ) | (_: Char ))) => true // TODO: Java 7 allows strings in switches @@ -1205,6 +1203,20 @@ defined class Foo */ def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): List[List[TreeMaker]] = doCSE(prevBinder, doDCE(prevBinder, cases, pt), pt) + + def removeSubstOnly(makers: List[TreeMaker]) = makers filterNot (_.isInstanceOf[SubstOnlyTreeMaker]) + + // a foldLeft to accumulate the localSubstitution left-to-right + // it drops SubstOnly tree makers, since their only goal in life is to propagate substitutions to the next tree maker, which is fullfilled by propagateSubstitution + def propagateSubstitution(treeMakers: List[TreeMaker], initial: Substitution): List[TreeMaker] = { + var accumSubst: Substitution = initial + treeMakers foreach { maker => + maker incorporateOuterSubstitution accumSubst + accumSubst = maker.substitution + } + removeSubstOnly(treeMakers) + } + // calls propagateSubstitution on the treemakers def combineCases(scrut: Tree, scrutSym: Symbol, casesRaw: List[List[TreeMaker]], pt: Type, owner: Symbol): Tree = fixerUpper(owner, scrut.pos){ val casesUnOpt = casesRaw map (propagateSubstitution(_, EmptySubstitution)) // drops SubstOnlyTreeMakers, since their effect is now contained in the TreeMakers that follow them @@ -1232,12 +1244,15 @@ defined class Foo */ val combinedCases = cases.map(combineExtractors(_, pt)).reduceLeft(pmgen.typedOrElse(optPt)) - toHoist = (for (treeMakers <- cases; tm <- treeMakers; hoisted <- tm.treesToHoist) yield hoisted).toList + toHoist = ( + for (treeMakers <- cases) + yield treeMakers.collect{case tm: ReusedCondTreeMaker => tm.treesToHoist} + ).flatten.flatten.toList - (pmgen.fun(scrutSym, combinedCases), hasDefault) + (combinedCases, hasDefault) } else (pmgen.zero, false) - val expr = pmgen.runOrElse(scrut, matcher, scrutSym.info, if (isFullyDefined(pt)) pt else NoType, hasDefault) + val expr = pmgen.runOrElse(scrut, scrutSym, matcher, if (isFullyDefined(pt)) pt else NoType, hasDefault) if (toHoist isEmpty) expr else Block(toHoist, expr) } @@ -1248,8 +1263,6 @@ defined class Foo */ def combineExtractors(treeMakers: List[TreeMaker], pt: Type): Tree = treeMakers.foldRight (EmptyTree: Tree) (_.chainBefore(_, pt)) - - // TODO: do this during tree construction, but that will require tracking the current owner in treemakers // TODO: assign more fine-grained positions // fixes symbol nesting, assigns positions @@ -1328,9 +1341,10 @@ defined class Foo */ // codegen relevant to the structure of the translation (how extractors are combined) trait AbsCodeGen { import CODE.UNIT - def runOrElse(scrut: Tree, matcher: Tree, scrutTp: Type, resTp: Type, hasDefault: Boolean): Tree - def flatMap(a: Tree, b: Tree): Tree + def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, hasDefault: Boolean): Tree + def flatMap(prev: Tree, b: Symbol, next: Tree): Tree def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, nextBinderTp: Type, next: Tree): Tree + def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree def flatMapGuard(cond: Tree, next: Tree): Tree def fun(arg: Symbol, body: Tree): Tree def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree @@ -1359,7 +1373,8 @@ defined class Foo */ trait MatchingStrategyGen { self: CommonCodeGen with MatchingStrategyGen with MonadInstGen => // methods in MatchingStrategy (the monad companion) -- used directly in translation - def runOrElse(scrut: Tree, matcher: Tree, scrutTp: Type, resTp: Type, hasDefault: Boolean): Tree = genTypeApply(matchingStrategy DOT vpmName.runOrElse, scrutTp, resTp) APPLY (scrut) APPLY (matcher) // matchingStrategy.runOrElse(scrut)(matcher) + def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, hasDefault: Boolean): Tree + = genTypeApply(matchingStrategy DOT vpmName.runOrElse, scrutSym.info, resTp) APPLY (scrut) APPLY (fun(scrutSym, matcher)) // matchingStrategy.runOrElse(scrut)(matcher) // *only* used to wrap the RHS of a body (isDefinedAt synthesis relies on this) def one(res: Tree, bodyPt: Type, matchPt: Type): Tree = (matchingStrategy DOT vpmName.one) (_asInstanceOf(res, bodyPt, force = true)) // matchingStrategy.one(res), like one, but blow this one away for isDefinedAt (since it's the RHS of a case) def zero: Tree = matchingStrategy DOT vpmName.zero // matchingStrategy.zero @@ -1368,13 +1383,14 @@ defined class Foo */ trait MonadInstGen { self: CommonCodeGen with MatchingStrategyGen with MonadInstGen => // methods in the monad instance -- used directly in translation - def flatMap(a: Tree, b: Tree): Tree = (a DOT vpmName.flatMap)(b) + def flatMap(prev: Tree, b: Symbol, next: Tree): Tree = (prev DOT vpmName.flatMap)(fun(b, next)) def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree = (genTypeApply(thisCase DOT vpmName.orElse, pt)) APPLY (elseCase) // TODO: the trees generated by flatMapCond and flatMapGuard may need to be distinguishable by exhaustivity checking -- they aren't right now def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, - nextBinderTp: Type, next: Tree): Tree = flatMap(guard(cond, res, nextBinderTp), fun(nextBinder, next)) + nextBinderTp: Type, next: Tree): Tree = flatMap(guard(cond, res, nextBinderTp), nextBinder, next) def flatMapGuard(guardTree: Tree, next: Tree): Tree = flatMapCond(guardTree, CODE.UNIT, freshSym(guardTree.pos, UnitClass.tpe), UnitClass.tpe, next) + def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree = throw new UnsupportedOperationException("Can't optimize under user-defined monad.") } // when we know we're targetting Option, do some inlining the optimizer won't do @@ -1394,18 +1410,17 @@ defined class Foo */ @inline private def dontStore(tp: Type) = (tp.typeSymbol eq UnitClass) || (tp.typeSymbol eq NothingClass) lazy val keepGoing = freshSym(NoPosition, BooleanClass.tpe, "keepGoing") setFlag MUTABLE lazy val matchRes = freshSym(NoPosition, AnyClass.tpe, "matchRes") setFlag MUTABLE - override def runOrElse(scrut: Tree, matcher: Tree, scrutTp: Type, resTp: Type, hasDefault: Boolean) = { - val Function(List(x: ValDef), body) = matcher + override def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, hasDefault: Boolean) = { matchRes.info = if (resTp ne NoType) resTp.widen else AnyClass.tpe // we don't always know resTp, and it might be AnyVal, in which case we can't assign NULL if (dontStore(resTp)) matchRes resetFlag MUTABLE // don't assign to Unit-typed var's, in fact, make it a val -- conveniently also works around SI-5245 BLOCK( - VAL(zeroSym) === REF(NoneModule), // TODO: can we just get rid of explicitly emitted zero? don't know how to do that as a local rewrite... - VAL(x.symbol) === scrut, // reuse the symbol of the function's argument to avoid creating a fresh one and substituting it for x.symbol in body -- the owner structure is repaired by fixerUpper + VAL(zeroSym) === REF(NoneModule), // TODO: can we just get rid of explicitly emitted zero? don't know how to do that as a local rewrite... + VAL(scrutSym) === scrut, // reuse the symbol of the function's argument to avoid creating a fresh one and substituting it for scrutSym in `matcher` -- the owner structure is repaired by fixerUpper VAL(matchRes) === mkZero(matchRes.info), // must cast to deal with GADT typing, hence the private mkZero above VAL(keepGoing) === TRUE, - body, + matcher, if(hasDefault) REF(matchRes) - else (IF (REF(keepGoing)) THEN MATCHERROR(REF(x.symbol)) ELSE REF(matchRes)) + else (IF (REF(keepGoing)) THEN MATCHERROR(REF(scrutSym)) ELSE REF(matchRes)) ) } @@ -1424,20 +1439,16 @@ defined class Foo */ // guard is only used by flatMapCond and flatMapGuard, which are overridden override def guard(c: Tree, then: Tree, tp: Type): Tree = throw new NotImplementedError("guard is never called by optimizing codegen") - override def flatMap(opt: Tree, fun: Tree): Tree = fun match { - case Function(List(x: ValDef), body) => - val tp = inMatchMonad(x.symbol.tpe) - val vs = freshSym(opt.pos, tp, "o") - val isEmpty = tp member vpmName.isEmpty - val get = tp member vpmName.get - val v = VAL(vs) === opt - - BLOCK( - v, - IF (vs DOT isEmpty) THEN zero ELSE typedSubst(body, List(x.symbol), List(vs DOT get)) // must be isEmpty and get as we don't control the target of the call (could be the result of a user-defined extractor) - ) - case _ => println("huh?") - (opt DOT vpmName.flatMap)(fun) + override def flatMap(prev: Tree, b: Symbol, next: Tree): Tree = { + val tp = inMatchMonad(b.tpe) + val prevSym = freshSym(prev.pos, tp, "o") + val isEmpty = tp member vpmName.isEmpty + val get = tp member vpmName.get + + BLOCK( + VAL(prevSym) === prev, + IF (prevSym DOT isEmpty) THEN zero ELSE typedSubst(next, List(b), List(prevSym DOT get)) // must be isEmpty and get as we don't control the target of the call (could be the result of a user-defined extractor) + ) } override def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree = { @@ -1453,6 +1464,13 @@ defined class Foo */ next ) ELSE zero + override def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree = + IF (cond) THEN BLOCK( + condSym === TRUE, + nextBinder === res, + next + ) ELSE zero + override def flatMapGuard(guardTree: Tree, next: Tree): Tree = IF (guardTree) THEN next ELSE zero } |