diff options
-rw-r--r-- | src/compiler/scala/reflect/internal/StdNames.scala | 1 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/TreeGen.scala | 83 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/icode/GenICode.scala | 14 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/UnCurry.scala | 125 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala | 324 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 13 | ||||
-rw-r--r-- | src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala | 2 | ||||
-rw-r--r-- | src/library/scala/sys/process/BasicIO.scala | 5 | ||||
-rw-r--r-- | test/files/run/virtpatmat_partial.check | 4 | ||||
-rw-r--r-- | test/files/run/virtpatmat_partial.scala | 23 | ||||
-rw-r--r-- | test/files/run/virtpatmat_switch.scala | 8 | ||||
-rw-r--r-- | test/files/run/virtpatmat_try.check | 2 | ||||
-rw-r--r-- | test/files/run/virtpatmat_try.flags | 1 | ||||
-rw-r--r-- | test/files/run/virtpatmat_try.scala | 47 | ||||
-rw-r--r-- | test/files/specialized/SI-5005.scala | 6 |
16 files changed, 448 insertions, 212 deletions
diff --git a/src/compiler/scala/reflect/internal/StdNames.scala b/src/compiler/scala/reflect/internal/StdNames.scala index 1f67bbc0ac..b4c404a80c 100644 --- a/src/compiler/scala/reflect/internal/StdNames.scala +++ b/src/compiler/scala/reflect/internal/StdNames.scala @@ -330,6 +330,7 @@ trait StdNames extends NameManglers { self: SymbolTable => val freeValue : NameType = "freeValue" val genericArrayOps: NameType = "genericArrayOps" val get: NameType = "get" + val getOrElse: NameType = "getOrElse" val hasNext: NameType = "hasNext" val hashCode_ : NameType = if (forMSIL) "GetHashCode" else "hashCode" val hash_ : NameType = "hash" diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index 265d017653..3467292f28 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -13,7 +13,7 @@ import symtab.SymbolTable /** XXX to resolve: TreeGen only assumes global is a SymbolTable, but * TreeDSL at the moment expects a Global. Can we get by with SymbolTable? */ -abstract class TreeGen extends reflect.internal.TreeGen { +abstract class TreeGen extends reflect.internal.TreeGen with TreeDSL { val global: Global import global._ @@ -66,18 +66,81 @@ abstract class TreeGen extends reflect.internal.TreeGen { case _ => tree } - def withDefaultCase(matchExpr: Tree, defaultAction: Tree/*scrutinee*/ => Tree): Tree = matchExpr match { - case Match(scrutinee, cases) => - if (cases exists treeInfo.isDefaultCase) matchExpr - else { - val defaultCase = CaseDef(Ident(nme.WILDCARD), EmptyTree, defaultAction(scrutinee)) - Match(scrutinee, cases :+ defaultCase) + // must be kept in synch with the codegen in PatMatVirtualiser + object VirtualCaseDef { + def unapply(b: Block): Option[(Assign, Tree, Tree)] = b match { + case Block(List(assign@Assign(keepGoingLhs, falseLit), matchRes), zero) => Some((assign, matchRes, zero)) // TODO: check tree annotation + case _ => None + } + } + + // TODO: would be so much nicer if we would know during match-translation (i.e., type checking) + // whether we should emit missingCase-style apply (and isDefinedAt), instead of transforming trees post-factum + class MatchMatcher { + def caseMatch(orig: Tree, selector: Tree, cases: List[CaseDef], wrap: Tree => Tree): Tree = unknownTree(orig) + def caseVirtualizedMatch(orig: Tree, _match: Tree, targs: List[Tree], scrut: Tree, matcher: Tree): Tree = unknownTree(orig) + def caseVirtualizedMatchOpt(orig: Tree, zero: ValDef, x: ValDef, matchRes: ValDef, keepGoing: ValDef, stats: List[Tree], epilogue: Tree, wrap: Tree => Tree): Tree = unknownTree(orig) + + def apply(matchExpr: Tree): Tree = (matchExpr: @unchecked) match { + // old-style match or virtpatmat switch + case Match(selector, cases) => // println("simple match: "+ (selector, cases) + "for:\n"+ matchExpr ) + caseMatch(matchExpr, selector, cases, identity) + // old-style match or virtpatmat switch + case Block((vd: ValDef) :: Nil, orig@Match(selector, cases)) => // println("block match: "+ (selector, cases, vd) + "for:\n"+ matchExpr ) + caseMatch(matchExpr, selector, cases, m => copyBlock(matchExpr, List(vd), m)) + // virtpatmat + case Apply(Apply(TypeApply(Select(tgt, nme.runOrElse), targs), List(scrut)), List(matcher)) if opt.virtPatmat => // println("virt match: "+ (tgt, targs, scrut, matcher) + "for:\n"+ matchExpr ) + caseVirtualizedMatch(matchExpr, tgt, targs, scrut, matcher) + // optimized version of virtpatmat + case Block((zero: ValDef) :: (x: ValDef) :: (matchRes: ValDef) :: (keepGoing: ValDef) :: stats, epilogue) if opt.virtPatmat => // TODO: check tree annotation // println("virtopt match: "+ (zero, x, matchRes, keepGoing, stats) + "for:\n"+ matchExpr ) + caseVirtualizedMatchOpt(matchExpr, zero, x, matchRes, keepGoing, stats, epilogue, identity) + // optimized version of virtpatmat + case Block(outerStats, orig@Block((zero: ValDef) :: (x: ValDef) :: (matchRes: ValDef) :: (keepGoing: ValDef) :: stats, epilogue)) if opt.virtPatmat => // TODO: check tree annotation // println("virt opt block match: "+ (zero, x, matchRes, keepGoing, stats, outerStats) + "for:\n"+ matchExpr ) + caseVirtualizedMatchOpt(matchExpr, zero, x, matchRes, keepGoing, stats, epilogue, m => copyBlock(matchExpr, outerStats, m)) + case other => + unknownTree(other) + } + + def unknownTree(t: Tree): Tree = throw new MatchError(t) + def copyBlock(orig: Tree, stats: List[Tree], expr: Tree): Block = Block(stats, expr) + + def dropSyntheticCatchAll(cases: List[CaseDef]): List[CaseDef] = + if (!opt.virtPatmat) cases + else cases filter { + case CaseDef(pat, EmptyTree, Throw(Apply(Select(New(exTpt), nme.CONSTRUCTOR), _))) if (treeInfo.isWildcardArg(pat) && (exTpt.tpe.typeSymbol eq MatchErrorClass)) => false + case CaseDef(pat, guard, body) => true + } + } + + def withDefaultCase(matchExpr: Tree, defaultAction: Tree/*scrutinee*/ => Tree): Tree = { + object withDefaultTransformer extends MatchMatcher { + override def caseMatch(orig: Tree, selector: Tree, cases: List[CaseDef], wrap: Tree => Tree): Tree = { + val casesNoSynthCatchAll = dropSyntheticCatchAll(cases) + if (casesNoSynthCatchAll exists treeInfo.isDefaultCase) orig + else { + val defaultCase = CaseDef(Ident(nme.WILDCARD), EmptyTree, defaultAction(selector.duplicate)) + wrap(Match(selector, casesNoSynthCatchAll :+ defaultCase)) + } + } + override def caseVirtualizedMatch(orig: Tree, _match: Tree, targs: List[Tree], scrut: Tree, matcher: Tree): Tree = { import CODE._ + ((matcher APPLY (scrut)) DOT nme.getOrElse) APPLY (defaultAction(scrut.duplicate)) // TODO: pass targs } - case _ => - matchExpr - // [Martin] Adriaan: please fill in virtpatmat transformation here + override def caseVirtualizedMatchOpt(orig: Tree, zero: ValDef, x: ValDef, matchRes: ValDef, keepGoing: ValDef, stats: List[Tree], epilogue: Tree, wrap: Tree => Tree): Tree = { import CODE._ + wrap(Block( + zero :: + x :: + matchRes :: + keepGoing :: + stats, + // replace `if (keepGoing) throw new MatchError(...) else matchRes` by `if (keepGoing) ${defaultAction(`x`)} else matchRes` + (IF (REF(keepGoing.symbol)) THEN defaultAction(x.rhs.duplicate) ELSE REF(matchRes.symbol)) + )) + } + } + withDefaultTransformer(matchExpr) } + def mkCached(cvar: Symbol, expr: Tree): Tree = { val cvarRef = mkUnattributedRef(cvar) Block( diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index 6aee52a354..f2c725a5c2 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -393,15 +393,15 @@ abstract class GenICode extends SubComponent { for (CaseDef(pat, _, body) <- catches.reverse) yield { def genWildcardHandler(sym: Symbol): (Symbol, TypeKind, Context => Context) = (sym, kind, ctx => { - ctx.bb.emit(DROP(REFERENCE(sym))) + ctx.bb.emit(DROP(REFERENCE(sym))) // drop the loaded exception genLoad(body, ctx, kind) }) pat match { case Typed(Ident(nme.WILDCARD), tpt) => genWildcardHandler(tpt.tpe.typeSymbol) case Ident(nme.WILDCARD) => genWildcardHandler(ThrowableClass) - case Bind(name, _) => - val exception = ctx.method addLocal new Local(pat.symbol, toTypeKind(pat.symbol.tpe), false) + case Bind(_, _) => + val exception = ctx.method addLocal new Local(pat.symbol, toTypeKind(pat.symbol.tpe), false) // the exception will be loaded and stored into this local (pat.symbol.tpe.typeSymbol, kind, { ctx: Context => @@ -1054,7 +1054,7 @@ abstract class GenICode extends SubComponent { case Match(selector, cases) => debuglog("Generating SWITCH statement."); - var ctx1 = genLoad(selector, ctx, INT) + var ctx1 = genLoad(selector, ctx, INT) // TODO: Java 7 allows strings in switches (so, don't assume INT and don't convert the literals using intValue) val afterCtx = ctx1.newBlock var caseCtx: Context = null generatedType = toTypeKind(tree.tpe) @@ -2086,12 +2086,12 @@ abstract class GenICode extends SubComponent { exh }) else None - val exhs = handlers.map { handler => - val exh = this.newExceptionHandler(handler._1, handler._2, tree.pos) + val exhs = handlers.map { case (sym, kind, handler) => // def genWildcardHandler(sym: Symbol): (Symbol, TypeKind, Context => Context) = + val exh = this.newExceptionHandler(sym, kind, tree.pos) var ctx1 = outerCtx.enterExceptionHandler(exh) ctx1.addFinalizer(finalizer, finalizerCtx) loadException(ctx1, exh, tree.pos) - ctx1 = handler._3(ctx1) + ctx1 = handler(ctx1) // emit finalizer val ctx2 = emitFinalizer(ctx1) ctx2.bb.closeWith(JUMP(afterCtx.bb)) diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index 7f7f7e7b65..252b3ccffc 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -517,7 +517,7 @@ abstract class ExplicitOuter extends InfoTransform super.transform(treeCopy.Apply(tree, sel, outerVal :: args)) // entry point for pattern matcher translation - case mch: Match => + case mch: Match if (!opt.virtPatmat) => // don't use old pattern matcher as fallback when the user wants the virtualizing one matchTranslation(mch) case _ => diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index ab4a2141a9..841cd4a204 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -228,9 +228,9 @@ abstract class UnCurry extends InfoTransform * case P_1 if G_1 => E_1 * ... * case P_n if G_n => true - * case _ => this.missingCase(x) + * case _ => this.missingCase(expr) * } - * def isDefinedAtCurrent(x: T): boolean = (x: @unchecked) match { + * def _isDefinedAt(x: T): boolean = (x: @unchecked) match { * case P_1 if G_1 => true * ... * case P_n if G_n => true @@ -240,7 +240,7 @@ abstract class UnCurry extends InfoTransform * new $anon() * * However, if one of the patterns P_i if G_i is a default pattern, - * drop the last default clause in tghe definition of `apply` and generate for `isDefinedAtCurrent` instead + * drop the last default clause in the definition of `apply` and generate for `_isDefinedAt` instead * * def isDefinedAtCurrent(x: T): boolean = true */ @@ -291,73 +291,26 @@ abstract class UnCurry extends InfoTransform val substParam = new TreeSymSubstituter(fun.vparams map (_.symbol), params) def substTree[T <: Tree](t: T): T = substParam(resetLocalAttrs(t)) - // waiting here until we can mix case classes and extractors reliably (i.e., when virtpatmat becomes the default) - // object VirtPatmatOpt { - // object Last { - // def unapply[T](xs: List[T]) = xs.lastOption - // } - // // keep this in synch by what's generated by combineCases/runOrElse - // object MatcherBlock { - // def unapply(matcher: Tree): Option[(ValDef, ValDef, ValDef, ValDef, List[Tree])] = matcher match { // TODO: BUG the unapplySeq version of the case below does not seem to work in virtpatmat?? - // case Block((zero: ValDef) :: (x: ValDef) :: (matchRes: ValDef) :: (keepGoing: ValDef) :: stats, _) => Some(zero, x, matchRes, keepGoing, stats) - // case _ => None - // } - // } - // // TODO: virtpatmat use case: would be nice if could abstract over the repeated pattern more easily - // // case Block(Last(P)) => - // // case P => - // def unapply(matcher: Tree): Option[(ValDef, ValDef, ValDef, ValDef, List[Tree], Tree => Tree)] = matcher match { - // case MatcherBlock(zero, x, matchRes, keepGoing, stats) => Some(zero, x, matchRes, keepGoing, stats, identity[Tree]) - // case Block(outerStats, MatcherBlock(zero, x, matchRes, keepGoing, stats)) => Some(zero, x, matchRes, keepGoing, stats, inner => Block(outerStats, inner)) - // case b => treeBrowser browse b; None - // } - // } - - // TODO: optimize duplication, but make sure ValDef's introduced by wrap are treated correctly - def dupMatch(selector: Tree, cases: List[CaseDef], wrap: Match => Tree = identity) = { - def transformCase(cdef: CaseDef): CaseDef = - CaseDef(cdef.pat, cdef.guard, Literal(Constant(true))) - def defaultCase = CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false))) - - gen.mkUncheckedMatch( - if (cases exists treeInfo.isDefaultCase) Literal(Constant(true)) - else substTree(wrap(Match(selector, (cases map transformCase) :+ defaultCase)).duplicate) - ) - } + object isDefinedAtTransformer extends gen.MatchMatcher { + // TODO: optimize duplication, but make sure ValDef's introduced by wrap are treated correctly + override def caseMatch(orig: Tree, selector: Tree, cases: List[CaseDef], wrap: Tree => Tree): Tree = { + def transformCase(cdef: CaseDef): CaseDef = + CaseDef(cdef.pat, cdef.guard, Literal(Constant(true))) - def dupVirtMatch(zero: ValDef, x: ValDef, matchRes: ValDef, keepGoing: ValDef, stats: List[Tree], wrap: Block => Tree = identity) = { - object dropMatchResAssign extends Transformer { - // override val treeCopy = newStrictTreeCopier // will duplicate below - override def transform(tree: Tree): Tree = tree match { - // don't compute the result of the match -- remove the block for the RHS (emitted by pmgen.one), except for the assignment to keepGoing - case Block(List(matchRes, ass@Assign(keepGoingLhs, falseLit)), zero) if keepGoingLhs.symbol eq keepGoing.symbol => - Block(List(ass), zero) - case _ => - super.transform(tree) - } + def defaultCase = CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false))) + + val casesNoSynthCatchAll = dropSyntheticCatchAll(cases) + + gen.mkUncheckedMatch( + if (casesNoSynthCatchAll exists treeInfo.isDefaultCase) Literal(Constant(true)) + else substTree(wrap(Match(selector, (casesNoSynthCatchAll map transformCase) :+ defaultCase)).duplicate) + ) } - val statsNoMatchRes: List[Tree] = stats map (dropMatchResAssign.transform) toList - val idaBlock = wrap(Block( - zero :: - x :: - /* drop matchRes def */ - keepGoing :: - statsNoMatchRes, - NOT(REF(keepGoing.symbol)) // replace `if (keepGoing) throw new MatchError(...) else matchRes` by `!keepGoing` - )) - substTree(idaBlock.duplicate) // duplicate on block as a whole to ensure valdefs are properly cloned and substed - } - DefDef(m, (fun.body: @unchecked) match { - case Match(selector, cases) => - dupMatch(selector, cases) - case Block((vd: ValDef) :: Nil, Match(selector, cases)) => // can't factor this out using an extractor due to bugs in the old pattern matcher - dupMatch(selector, cases, m => Block(List(vd), m)) - // virtpatmat -- TODO: find a better way to keep this in synch with the code generated by patmatvirtualizer - case Apply(Apply(TypeApply(Select(tgt, nme.runOrElse), targs), args_scrut), args_pm) if opt.virtPatmat => + override def caseVirtualizedMatch(orig: Tree, _match: Tree, targs: List[Tree], scrut: Tree, matcher: Tree): Tree = { object noOne extends Transformer { override val treeCopy = newStrictTreeCopier // must duplicate everything - val one = tgt.tpe member newTermName("one") + val one = _match.tpe member newTermName("one") override def transform(tree: Tree): Tree = tree match { case Apply(fun, List(a)) if fun.symbol == one => // blow one's argument away since all we want to know is whether the match succeeds or not @@ -367,15 +320,34 @@ abstract class UnCurry extends InfoTransform super.transform(tree) } } - substTree(Apply(Apply(TypeApply(Select(tgt.duplicate, tgt.tpe.member(newTermName("isSuccess"))), targs map (_.duplicate)), args_scrut map (_.duplicate)), args_pm map (noOne.transform))) - // for the optimized version of virtpatmat - case Block((zero: ValDef) :: (x: ValDef) :: (matchRes: ValDef) :: (keepGoing: ValDef) :: stats, _) if opt.virtPatmat => - dupVirtMatch(zero, x, matchRes, keepGoing, stats) - case Block(outerStats, Block((zero: ValDef) :: (x: ValDef) :: (matchRes: ValDef) :: (keepGoing: ValDef) :: stats, _)) if opt.virtPatmat => // can't factor this out using an extractor due to bugs in the old pattern matcher - dupVirtMatch(zero, x, matchRes, keepGoing, stats, m => Block(outerStats, m)) - // case other => - // treeBrowser browse other - }) + substTree(Apply(Apply(TypeApply(Select(_match.duplicate, _match.tpe.member(newTermName("isSuccess"))), targs map (_.duplicate)), List(scrut.duplicate)), List(noOne.transform(matcher)))) + } + + override def caseVirtualizedMatchOpt(orig: Tree, zero: ValDef, x: ValDef, matchRes: ValDef, keepGoing: ValDef, stats: List[Tree], epilogue: Tree, wrap: Tree => Tree) = { + object dropMatchResAssign extends Transformer { + // override val treeCopy = newStrictTreeCopier // will duplicate below + override def transform(tree: Tree): Tree = tree match { + // don't compute the result of the match -- remove the block for the RHS (emitted by pmgen.one), except for the assignment to keepGoing + case gen.VirtualCaseDef(assignKeepGoing, matchRes, zero) if assignKeepGoing.lhs.symbol eq keepGoing.symbol => + Block(List(assignKeepGoing), zero) + case _ => + super.transform(tree) + } + } + val statsNoMatchRes: List[Tree] = stats map (dropMatchResAssign.transform) toList + val idaBlock = wrap(Block( + zero :: + x :: + /* drop matchRes def */ + keepGoing :: + statsNoMatchRes, + NOT(REF(keepGoing.symbol)) // replace `if (keepGoing) throw new MatchError(...) else matchRes` epilogue by `!keepGoing` + )) + substTree(idaBlock.duplicate) // duplicate on block as a whole to ensure valdefs are properly cloned and substed + } + } + + DefDef(m, isDefinedAtTransformer(fun.body)) } val members = @@ -679,7 +651,8 @@ abstract class UnCurry extends InfoTransform if (dd.symbol hasAnnotation VarargsClass) addJavaVarargsForwarders(dd, flatdd, tree) flatdd case Try(body, catches, finalizer) => - if (catches forall treeInfo.isCatchCase) tree + if (opt.virtPatmat) { if(catches exists (cd => !treeInfo.isCatchCase(cd))) debugwarn("VPM BUG! illegal try/catch "+ catches); tree } + else if (catches forall treeInfo.isCatchCase) tree else { val exname = unit.freshTermName("ex$") val cases = @@ -701,7 +674,7 @@ abstract class UnCurry extends InfoTransform } debuglog("rewrote try: " + catches + " ==> " + catchall); val catches1 = localTyper.typedCases( - tree, List(catchall), ThrowableClass.tpe, WildcardType) + List(catchall), ThrowableClass.tpe, WildcardType) treeCopy.Try(tree, body, catches1, finalizer) } case Apply(Apply(fn, args), args1) => diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala index 6d31243fd0..25dcc302ce 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala @@ -43,7 +43,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => val outer = newTermName("<outer>") val runOrElse = newTermName("runOrElse") val zero = newTermName("zero") - val __match = newTermName("__match") + val _match = newTermName("__match") // don't call it __match, since that will trigger virtual pattern matching... def counted(str: String, i: Int) = newTermName(str+i) } @@ -51,8 +51,8 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => object MatchTranslator { def apply(typer: Typer): MatchTranslation = { import typer._ - // typing `__match` to decide which MatchTranslator to create adds 4% to quick.comp.timer - newTyper(context.makeImplicit(reportAmbiguousErrors = false)).silent(_.typed(Ident(vpmName.__match), EXPRmode, WildcardType), reportAmbiguousErrors = false) match { + // typing `_match` to decide which MatchTranslator to create adds 4% to quick.comp.timer + newTyper(context.makeImplicit(reportAmbiguousErrors = false)).silent(_.typed(Ident(vpmName._match), EXPRmode, WildcardType), reportAmbiguousErrors = false) match { case SilentResultValue(ms) => new PureMatchTranslator(typer, ms) case _ => new OptimizingMatchTranslator(typer) } @@ -116,6 +116,10 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => trait MatchTranslation extends MatchMonadInterface { self: TreeMakers with CodegenCore => import typer.{typed, context, silent, reallyExists} + private def repeatedToSeq(tp: Type): Type = (tp baseType RepeatedParamClass) match { + case TypeRef(_, RepeatedParamClass, args) => appliedType(SeqClass.typeConstructor, args) + case _ => tp + } /** Implement a pattern match by turning its cases (including the implicit failure case) * into the corresponding (monadic) extractors, and combining them with the `orElse` combinator. @@ -133,11 +137,6 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => // and the only place that emits Matches after typers is for exception handling anyway) assert(phase.id <= currentRun.typerPhase.id, phase) - def repeatedToSeq(tp: Type): Type = (tp baseType RepeatedParamClass) match { - case TypeRef(_, RepeatedParamClass, args) => appliedType(SeqClass.typeConstructor, args) - case _ => tp - } - val scrutType = repeatedToSeq(elimAnonymousClass(scrut.tpe.widen)) val scrutSym = freshSym(scrut.pos, pureType(scrutType)) @@ -146,6 +145,47 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => combineCases(scrut, scrutSym, cases map translateCase(scrutSym, okPt), okPt, matchOwner) } + // return list of typed CaseDefs that are supported by the backend (typed/bind/wildcard) + // we don't have a global scrutinee -- the caught exception must be bound in each of the casedefs + // there's no need to check the scrutinee for null -- "throw null" becomes "throw new NullPointerException" + // try to simplify to a type-based switch, or fall back to a catch-all case that runs a normal pattern match + // unlike translateMatch, we type our result before returning it + def translateTry(caseDefs: List[CaseDef], pt: Type, pos: Position): List[CaseDef] = + // if they're already simple enough to be handled by the back-end, we're done + if (caseDefs forall treeInfo.isCatchCase) caseDefs + else { + val okPt = repeatedToSeq(pt) + val switch = { + val bindersAndCases = caseDefs map { caseDef => + // generate a fresh symbol for each case, hoping we'll end up emitting a type-switch (we don't have a global scrut there) + // if we fail to emit a fine-grained switch, have to do translateCase again with a single scrutSym (TODO: uniformize substitution on treemakers so we can avoid this) + val caseScrutSym = freshSym(pos, pureType(ThrowableClass.tpe)) + (caseScrutSym, propagateSubstitution(translateCase(caseScrutSym, okPt)(caseDef), EmptySubstitution)) + } + + (emitTypeSwitch(bindersAndCases, pt) map (_.map(fixerUpper(matchOwner, pos).apply(_).asInstanceOf[CaseDef]))) + } + + val catches = switch getOrElse { + val scrutSym = freshSym(pos, pureType(ThrowableClass.tpe)) + val casesNoSubstOnly = caseDefs map { caseDef => (propagateSubstitution(translateCase(scrutSym, okPt)(caseDef), EmptySubstitution))} + + val exSym = freshSym(pos, pureType(ThrowableClass.tpe), "ex") + + List( + atPos(pos) { + CaseDef( + Bind(exSym, Ident(nme.WILDCARD)), // TODO: does this need fixing upping? + EmptyTree, + combineCasesNoSubstOnly(CODE.REF(exSym), scrutSym, casesNoSubstOnly, pt, matchOwner, scrut => Throw(CODE.REF(exSym))) + ) + }) + } + + typer.typedCases(catches, ThrowableClass.tpe, WildcardType) + } + + /** The translation of `pat if guard => body` has two aspects: * 1) the substitution due to the variables bound by patterns @@ -668,6 +708,10 @@ class Foo(x: Other) { x._1 } // no error in this order def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Tree] = None + // for catch + def emitTypeSwitch(bindersAndCases: List[(Symbol, List[TreeMaker])], pt: Type): Option[List[CaseDef]] = + None + 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) @@ -788,6 +832,7 @@ class Foo(x: Other) { x._1 } // no error in this order } // implements the run-time aspects of (ยง8.2) (typedPattern has already done the necessary type transformations) + // TODO: normalize construction, which yields a combination of a EqualityTestTreeMaker (when necessary) and a TypeTestTreeMaker case class TypeAndEqualityTestTreeMaker(prevBinder: Symbol, patBinder: Symbol, pt: Type, pos: Position) extends CondTreeMaker { val nextBinderTp = glb(List(patBinder.info.widen, pt)) @@ -843,6 +888,10 @@ class Foo(x: Other) { x._1 } // no error in this order val cond = typeAndEqualityTest(patBinder, pt) val res = codegen._asInstanceOf(patBinder, nextBinderTp) + + // TODO: remove this + def isStraightTypeTest = cond match { case TypeApply(_, _) => cond.symbol == Any_isInstanceOf case _ => false } + override def toString = "TET"+(patBinder, pt) } @@ -926,25 +975,30 @@ class Foo(x: Other) { x._1 } // no error in this order } // 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 + def combineCases(scrut: Tree, scrutSym: Symbol, casesRaw: List[List[TreeMaker]], pt: Type, owner: Symbol): Tree = { + val casesNoSubstOnly = casesRaw map (propagateSubstitution(_, EmptySubstitution)) // drops SubstOnlyTreeMakers, since their effect is now contained in the TreeMakers that follow them + combineCasesNoSubstOnly(scrut, scrutSym, casesNoSubstOnly, pt, owner, CODE.MATCHERROR(_)) + } - emitSwitch(scrut, scrutSym, casesUnOpt, pt).getOrElse{ + def combineCasesNoSubstOnly(scrut: Tree, scrutSym: Symbol, casesNoSubstOnly: List[List[TreeMaker]], pt: Type, owner: Symbol, matchFail: Tree => Tree): Tree = fixerUpper(owner, scrut.pos){ + emitSwitch(scrut, scrutSym, casesNoSubstOnly, pt).getOrElse{ val (matcher, hasDefault, toHoist) = - if (casesUnOpt nonEmpty) { + if (casesNoSubstOnly nonEmpty) { // when specified, need to propagate pt explicitly (type inferencer can't handle it) val optPt = if (isFullyDefined(pt)) inMatchMonad(pt) else NoType - // do this check on casesUnOpt, since DCE will eliminate trivial cases like `case _ =>`, even if they're the last one + // do this check on casesNoSubstOnly, since DCE will eliminate trivial cases like `case _ =>`, even if they're the last one // exhaustivity and reachability must be checked before optimization as well - val hasDefault = casesUnOpt.nonEmpty && { - val nonTrivLast = casesUnOpt.last + // TODO: improve, a trivial type test before the body still makes for a default case + // ("trivial" depends on whether we're emitting a straight match or an exception, or more generally, any supertype of scrutSym.tpe is a no-op) + val hasDefault = casesNoSubstOnly.nonEmpty && { + val nonTrivLast = casesNoSubstOnly.last nonTrivLast.nonEmpty && nonTrivLast.head.isInstanceOf[BodyTreeMaker] } - val (cases, toHoist) = optimizeCases(scrutSym, casesUnOpt, pt) + val (cases, toHoist) = optimizeCases(scrutSym, casesNoSubstOnly, pt) val combinedCases = cases.map(combineExtractors(_, pt)).reduceLeft(codegen.typedOrElse(optPt)) @@ -952,7 +1006,11 @@ class Foo(x: Other) { x._1 } // no error in this order (combinedCases, hasDefault, toHoist) } else (codegen.zero, false, Nil) - val expr = codegen.runOrElse(scrut, scrutSym, matcher, if (isFullyDefined(pt)) pt else NoType, hasDefault) + // catch-all + val catchAll = + if (hasDefault) None // no need for a catch-all when there's already a default + else Some(matchFail) + val expr = codegen.runOrElse(scrut, scrutSym, matcher, if (isFullyDefined(pt)) pt else NoType, catchAll) if (toHoist isEmpty) expr else Block(toHoist, expr) } @@ -966,7 +1024,7 @@ class Foo(x: Other) { x._1 } // no error in this order // 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 - private def fixerUpper(origOwner: Symbol, pos: Position) = new Traverser { + protected def fixerUpper(origOwner: Symbol, pos: Position) = new Traverser { currentOwner = origOwner override def traverse(t: Tree) { @@ -1019,7 +1077,7 @@ class Foo(x: Other) { x._1 } // no error in this order // codegen relevant to the structure of the translation (how extractors are combined) trait AbsCodegen { - def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, hasDefault: Boolean): Tree + def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, catchAll: Option[Tree => Tree]): Tree def one(res: Tree, bodyPt: Type, matchPt: Type): Tree def zero: Tree def flatMap(prev: Tree, b: Symbol, next: Tree): Tree @@ -1098,10 +1156,10 @@ class Foo(x: Other) { x._1 } // no error in this order protected def matchMonadSym = oneSig.finalResultType.typeSymbol import CODE._ - def __match(n: Name): SelectStart = matchStrategy DOT n + 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 + typer.typed(_match(vpmName.one), EXPRmode | POLYmode | TAPPmode | FUNmode, WildcardType).tpe // TODO: error message } trait PureCodegen extends CodegenCore with PureMatchMonadInterface { @@ -1110,14 +1168,15 @@ class Foo(x: Other) { x._1 } // no error in this order object pureCodegen extends CommonCodegen { import CODE._ //// methods in MatchingStrategy (the monad companion) -- used directly in translation // __match.runOrElse(`scrut`)(`scrutSym` => `matcher`) - def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, hasDefault: Boolean): Tree - = __match(vpmName.runOrElse) APPLY (scrut) APPLY (fun(scrutSym, matcher)) + // TODO: consider catchAll, or virtualized matching will break in exception handlers + def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, catchAll: Option[Tree => Tree]): Tree + = _match(vpmName.runOrElse) APPLY (scrut) APPLY (fun(scrutSym, matcher)) // __match.one(`res`) - def one(res: Tree, bodyPt: Type, matchPt: Type): Tree = (__match(vpmName.one)) (res) + def one(res: Tree, bodyPt: Type, matchPt: Type): Tree = (_match(vpmName.one)) (res) // __match.zero - def zero: Tree = __match(vpmName.zero) + def zero: Tree = _match(vpmName.zero) // __match.guard(`c`, `then`) - def guard(c: Tree, then: Tree, tp: Type): Tree = __match(vpmName.guard) APPLY (c, then) + def guard(c: Tree, then: Tree, tp: Type): Tree = _match(vpmName.guard) APPLY (c, then) //// methods in the monad instance -- used directly in translation // `prev`.flatMap(`b` => `next`) @@ -1437,94 +1496,145 @@ class Foo(x: Other) { x._1 } // no error in this order } } - //// SWITCHES + //// SWITCHES -- TODO: operate on Tests rather than TreeMakers trait SwitchEmission extends TreeMakers with OptimizedMatchMonadInterface { self: CodegenCore => - object SwitchablePattern { def unapply(pat: Tree) = pat match { - case Literal(Constant((_: Byte ) | (_: Short) | (_: Int ) | (_: Char ))) => true // TODO: Java 7 allows strings in switches - case _ => false - }} - - // def isSwitchable(cases: List[(List[TreeMaker], Tree)]): Boolean = { - // def isSwitchableTreeMaker(tm: TreeMaker) = tm match { - // case tm@EqualityTestTreeMaker(_, SwitchablePattern(), _) => true - // case SubstOnlyTreeMaker(_) => true - // case AlternativesTreeMaker(_, altss, _) => altss forall (_.forall(isSwitchableTreeMaker)) - // case _ => false - // } - // } + abstract class SwitchMaker { + abstract class SwitchableTreeMakerExtractor { def unapply(x: TreeMaker): Option[Tree] } + val SwitchableTreeMaker: SwitchableTreeMakerExtractor + + def alternativesSupported: Boolean - private val switchableTpes = Set(ByteClass.tpe, ShortClass.tpe, IntClass.tpe, CharClass.tpe) + def isDefault(x: CaseDef): Boolean + def defaultSym: Symbol + def defaultBody: Tree + def defaultCase(scrutSym: Symbol = defaultSym, body: Tree = defaultBody): CaseDef - override def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Tree] = { - def sequence[T](xs: List[Option[T]]): Option[List[T]] = + private def sequence[T](xs: List[Option[T]]): Option[List[T]] = if (xs exists (_.isEmpty)) None else Some(xs.flatten) - def isSwitchableTpe(tpe: Type): Boolean = - switchableTpes contains tpe - def switchableConstToInt(x: Tree): Tree = { - val Literal(const) = x - const.tag match { - case IntTag => x - case ByteTag | ShortTag | CharTag => Literal(Constant(const.intValue)) + // empty list ==> failure + def apply(cases: List[(Symbol, List[TreeMaker])], pt: Type): List[CaseDef] = { + val caseDefs = cases map { case (scrutSym, makers) => + makers match { + // default case + case (btm@BodyTreeMaker(body, _)) :: Nil => + Some(defaultCase(scrutSym, btm.substitution(body))) + // constant (or typetest for typeSwitch) + case SwitchableTreeMaker(pattern) :: (btm@BodyTreeMaker(body, _)) :: Nil => + Some(CaseDef(pattern, EmptyTree, btm.substitution(body))) + // alternatives + case AlternativesTreeMaker(_, altss, _) :: (btm@BodyTreeMaker(body, _)) :: Nil if alternativesSupported => + val casePatterns = altss map { + case SwitchableTreeMaker(pattern) :: Nil => + Some(pattern) + case _ => + None + } + + sequence(casePatterns) map { patterns => + val substedBody = btm.substitution(body) + CaseDef(Alternative(patterns), EmptyTree, substedBody) + } + case _ => //println("can't emit switch for "+ makers) + None //failure (can't translate pattern to a switch) + } } - } - val caseDefs = cases map { makers => - removeSubstOnly(makers) match { - // default case (don't move this to unfold, as it may only occur on the top level, not as an alternative -- well, except in degenerate matches) - case (btm@BodyTreeMaker(body, _)) :: Nil => - Some(CaseDef(Ident(nme.WILDCARD), EmptyTree, btm.substitution(body))) - // constant - case (EqualityTestTreeMaker(_, const@SwitchablePattern(), _)) :: (btm@BodyTreeMaker(body, _)) :: Nil => - Some(CaseDef(switchableConstToInt(const), EmptyTree, btm.substitution(body))) - // alternatives - case AlternativesTreeMaker(_, altss, _) :: (btm@BodyTreeMaker(body, _)) :: Nil => // assert(currLabel.isEmpty && nextLabel.isEmpty) - val caseConstants = altss map { - case EqualityTestTreeMaker(_, const@SwitchablePattern(), _) :: Nil => - Some(switchableConstToInt(const)) - case _ => - None + (for( + caseDefs <- sequence(caseDefs)) yield + if (caseDefs exists isDefault) caseDefs + else { + caseDefs :+ defaultCase() } + ) getOrElse Nil + } + } - sequence(caseConstants) map { contants => - val substedBody = btm.substitution(body) - CaseDef(Alternative(contants), EmptyTree, substedBody) - } - case _ => - None //failure (can't translate pattern to a switch) + class RegularSwitchMaker(scrutSym: Symbol) extends SwitchMaker { + val switchableTpe = Set(ByteClass.tpe, ShortClass.tpe, IntClass.tpe, CharClass.tpe) + val alternativesSupported = true + + object SwitchablePattern { def unapply(pat: Tree): Option[Tree] = pat match { + case Literal(const@Constant((_: Byte ) | (_: Short) | (_: Int ) | (_: Char ))) => + Some(Literal(Constant(const.intValue))) // TODO: Java 7 allows strings in switches + case _ => None + }} + + object SwitchableTreeMaker extends SwitchableTreeMakerExtractor { + def unapply(x: TreeMaker): Option[Tree] = x match { + case EqualityTestTreeMaker(_, SwitchablePattern(const), _) => Some(const) + case _ => None } } - if (!isSwitchableTpe(scrut.tpe)) - None // TODO: emit a cast of the scrutinee and a switch on the cast scrutinee if patterns allow switch but the type of the scrutinee doesn't - else { - sequence(caseDefs) map { caseDefs => - import CODE._ - val caseDefsWithDefault = { - def isDefault(x: CaseDef): Boolean = x match { - case CaseDef(Ident(nme.WILDCARD), EmptyTree, _) => true - case _ => false - } - val hasDefault = caseDefs exists isDefault - if (hasDefault) caseDefs else { - val default = atPos(scrut.pos) { DEFAULT ==> MATCHERROR(REF(scrutSym)) } - caseDefs :+ default - } - } - val matcher = BLOCK( - if (scrut.tpe != IntClass.tpe) { - scrutSym setInfo IntClass.tpe - VAL(scrutSym) === (scrut DOT nme.toInt) - } else { - VAL(scrutSym) === scrut - }, - Match(REF(scrutSym), caseDefsWithDefault) // match on scrutSym, not scrut to avoid duplicating scrut - ) - // matcher filter (tree => tree.tpe == null) foreach println - // treeBrowser browse matcher - matcher // set type to avoid recursion in typedMatch + def isDefault(x: CaseDef): Boolean = x match { + case CaseDef(Ident(nme.WILDCARD), EmptyTree, _) => true + case _ => false + } + + def defaultSym: Symbol = scrutSym + def defaultBody: Tree = { import CODE._; MATCHERROR(REF(scrutSym)) } + def defaultCase(scrutSym: Symbol = defaultSym, body: Tree = defaultBody): CaseDef = { import CODE._; atPos(body.pos) { + DEFAULT ==> body + }} + } + + override def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Tree] = { import CODE._ + val regularSwitchMaker = new RegularSwitchMaker(scrutSym) + // 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)) { + val caseDefsWithDefault = regularSwitchMaker(cases map {c => (scrutSym, c)}, pt) + if (caseDefsWithDefault isEmpty) None + else { + // match on scrutSym -- converted to an int if necessary -- not on scrut directly (to avoid duplicating scrut) + val scrutToInt: Tree = + if(scrutSym.tpe =:= IntClass.tpe) REF(scrutSym) + else (REF(scrutSym) DOT (nme.toInt)) + Some(BLOCK( + VAL(scrutSym) === scrut, + Match(scrutToInt, caseDefsWithDefault) + )) + } + } else None + } + + // for the catch-cases in a try/catch + private object typeSwitchMaker extends SwitchMaker { + def switchableTpe(tp: Type) = true + val alternativesSupported = false // TODO: needs either back-end support of flattening of alternatives during typers + + // TODO: there are more treemaker-sequences that can be handled by type tests + // analyze the result of approximateTreeMaker rather than the TreeMaker itself + object SwitchableTreeMaker extends SwitchableTreeMakerExtractor { + def unapply(x: TreeMaker): Option[Tree] = x match { + case tm@TypeTestTreeMaker(_, _, _) => + Some(Bind(tm.nextBinder, Typed(Ident(nme.WILDCARD), TypeTree(tm.nextBinderTp)) /* not used by back-end */)) // -- TODO: use this if binder does not occur in the body + case tm@TypeAndEqualityTestTreeMaker(_, patBinder, pt, _) if tm.isStraightTypeTest => + Some(Bind(tm.nextBinder, Typed(Ident(nme.WILDCARD), TypeTree(tm.nextBinderTp)) /* not used by back-end */)) + case _ => + None } } + + def isDefault(x: CaseDef): Boolean = x match { + case CaseDef(Typed(Ident(nme.WILDCARD), tpt), EmptyTree, _) if (tpt.tpe =:= ThrowableClass.tpe) => true + case CaseDef(Bind(_, Typed(Ident(nme.WILDCARD), tpt)), EmptyTree, _) if (tpt.tpe =:= ThrowableClass.tpe) => true + case CaseDef(Ident(nme.WILDCARD), EmptyTree, _) => true + case _ => false + } + + lazy val defaultSym: Symbol = freshSym(NoPosition, ThrowableClass.tpe) + def defaultBody: Tree = Throw(CODE.REF(defaultSym)) + def defaultCase(scrutSym: Symbol = defaultSym, body: Tree = defaultBody): CaseDef = { import CODE._; atPos(body.pos) { + CASE (Bind(scrutSym, Typed(Ident(nme.WILDCARD), TypeTree(ThrowableClass.tpe)))) ==> body + }} + } + + // TODO: drop null checks + override def emitTypeSwitch(bindersAndCases: List[(Symbol, List[TreeMaker])], pt: Type): Option[List[CaseDef]] = { + val caseDefsWithDefault = typeSwitchMaker(bindersAndCases, pt) + if (caseDefsWithDefault isEmpty) None + else Some(caseDefsWithDefault) } } @@ -1551,33 +1661,31 @@ class Foo(x: Other) { x._1 } // no error in this order /** Inline runOrElse and get rid of Option allocations * - * runOrElse(scrut: scrutTp)(matcher): resTp = matcher(scrut) getOrElse (throw new MatchError(x)) + * runOrElse(scrut: scrutTp)(matcher): resTp = matcher(scrut) getOrElse ${catchAll(`scrut`)} * the matcher's optional result is encoded as a flag, keepGoing, where keepGoing == true encodes result.isEmpty, * if keepGoing is false, the result Some(x) of the naive translation is encoded as matchRes == x */ @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 - def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, hasDefault: Boolean) = { + def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, catchAll: Option[Tree => Tree]) = { 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(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(scrutSym) === scrut, VAL(matchRes) === mkZero(matchRes.info), // must cast to deal with GADT typing, hence the private mkZero above VAL(keepGoing) === TRUE, matcher, - if(hasDefault) REF(matchRes) - else (IF (REF(keepGoing)) THEN MATCHERROR(REF(scrutSym)) ELSE REF(matchRes)) + catchAll map { catchAllGen => (IF (REF(keepGoing)) THEN catchAllGen(REF(scrutSym)) ELSE REF(matchRes)) } getOrElse REF(matchRes) ) } // only used to wrap the RHS of a body def one(res: Tree, bodyPt: Type, matchPt: Type): Tree = { BLOCK( - if (dontStore(matchPt)) res // runOrElse hasn't been called yet, so matchRes.isMutable is irrelevant, also, tp may be a subtype of resTp used in runOrElse... - else (REF(matchRes) === res), // _asInstanceOf(res, tp.widen, force = true) - REF(keepGoing) === FALSE, + REF(keepGoing) === FALSE, // comes before assignment to matchRes, so the latter is in tail positions (can ignore the trailing zero -- will disappear when we flatten blocks, which is TODO) + if (dontStore(matchPt)) res else (REF(matchRes) === res), // runOrElse hasn't been called yet, so matchRes.isMutable is irrelevant, also, tp may be a subtype of resTp used in runOrElse... zero // to have a nice lub for lubs -- otherwise we'll get a boxed unit here -- TODO: get rid of all those dangling else zero's ) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index ef69e1525e..23fc30b9fb 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1987,7 +1987,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { treeCopy.CaseDef(cdef, pat1, guard1, body1) setType body1.tpe } - def typedCases(tree: Tree, cases: List[CaseDef], pattp: Type, pt: Type): List[CaseDef] = + def typedCases(cases: List[CaseDef], pattp: Type, pt: Type): List[CaseDef] = cases mapConserve { cdef => newTyper(context.makeNewScope(cdef, context.owner)).typedCase(cdef, pattp, pt) } @@ -3318,7 +3318,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { typed1(atPos(tree.pos) { Function(params, body) }, mode, pt) } else { val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType)) - var cases1 = typedCases(tree, cases, packCaptured(selector1.tpe.widen), pt) + var cases1 = typedCases(cases, packCaptured(selector1.tpe.widen), pt) if (isPastTyper || !opt.virtPatmat) { val (owntype, needAdapt) = ptOrLub(cases1 map (_.tpe)) @@ -3334,7 +3334,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { (MatchTranslator(this)).translateMatch(selector1, cases1, owntype) match { case Block(vd :: Nil, tree@Match(selector, cases)) => val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType)) - var cases1 = typedCases(tree, cases, packCaptured(selector1.tpe.widen), pt) + var cases1 = typedCases(cases, packCaptured(selector1.tpe.widen), pt) val (owntype, needAdapt) = ptOrLub(cases1 map (_.tpe)) if (needAdapt) cases1 = cases1 map (adaptCase(_, owntype)) @@ -4242,7 +4242,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case Try(block, catches, finalizer) => var block1 = typed(block, pt) - var catches1 = typedCases(tree, catches, ThrowableClass.tpe, pt) + var catches1 = typedCases(catches, ThrowableClass.tpe, pt) val finalizer1 = if (finalizer.isEmpty) finalizer else typed(finalizer, UnitClass.tpe) val (owntype, needAdapt) = ptOrLub(block1.tpe :: (catches1 map (_.tpe))) @@ -4250,6 +4250,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { block1 = adapt(block1, mode, owntype) catches1 = catches1 map (adaptCase(_, owntype)) } + + if(!isPastTyper && opt.virtPatmat) { + catches1 = (MatchTranslator(this)).translateTry(catches1, owntype, tree.pos) + } + treeCopy.Try(tree, block1, catches1, finalizer1) setType owntype case Throw(expr) => diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala index b2a1546b4e..a90dc36639 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala @@ -203,7 +203,7 @@ abstract class SelectiveCPSTransform extends PluginComponent with rhs.changeOwner(currentOwner -> fun.symbol) val exSym = currentOwner.newValueParameter(cpsNames.ex, pos).setInfo(ThrowableClass.tpe) - val catch2 = { localTyper.typedCases(tree, List( + val catch2 = { localTyper.typedCases(List( CaseDef(Bind(exSym, Typed(Ident("_"), TypeTree(ThrowableClass.tpe))), Apply(Select(Ident(funSym), nme.isDefinedAt), List(Ident(exSym))), Apply(Ident(funSym), List(Ident(exSym)))) diff --git a/src/library/scala/sys/process/BasicIO.scala b/src/library/scala/sys/process/BasicIO.scala index 5b7244e98e..edc60a1bb5 100644 --- a/src/library/scala/sys/process/BasicIO.scala +++ b/src/library/scala/sys/process/BasicIO.scala @@ -227,9 +227,10 @@ object BasicIO { out.write(buffer, 0, byteCount) // flush() will throw an exception once the process has terminated val available = try { out.flush(); true } catch { case _: IOException => false } - if (available) loop() else in.close() - } else in.close() + if (available) loop() + } } loop() + in.close() } } diff --git a/test/files/run/virtpatmat_partial.check b/test/files/run/virtpatmat_partial.check index 093020ce05..1555eca82b 100644 --- a/test/files/run/virtpatmat_partial.check +++ b/test/files/run/virtpatmat_partial.check @@ -1,2 +1,4 @@ Map(a -> Some(1), b -> None) -Map(a -> 1)
\ No newline at end of file +79 +undefined +Map(a -> 1) diff --git a/test/files/run/virtpatmat_partial.scala b/test/files/run/virtpatmat_partial.scala index c408b31983..6597f2f5ae 100644 --- a/test/files/run/virtpatmat_partial.scala +++ b/test/files/run/virtpatmat_partial.scala @@ -4,6 +4,29 @@ object Test extends App { val res = a collect {case (p, Some(a)) => (p, a)} + final val GT = 79 + final val GTGT = 93 + final val GTGTGT = 94 + final val GTEQ = 81 + final val GTGTEQ = 113 + final val GTGTGTEQ = 114 + final val ASSIGN = 75 + + def acceptClosingAngle(in: Int) { + val closers: PartialFunction[Int, Int] = { + case GTGTGTEQ => GTGTEQ + case GTGTGT => GTGT + case GTGTEQ => GTEQ + case GTGT => GT + case GTEQ => ASSIGN + } + if (closers isDefinedAt in) println(closers(in)) + else println("undefined") + } + + acceptClosingAngle(GTGT) + acceptClosingAngle(ASSIGN) + // should uncurry to: // val res: Map[String,Int] = a.collect[(String, Int), Map[String,Int]]( // new PartialFunction[(String, Option[Int]),(String, Int)] { diff --git a/test/files/run/virtpatmat_switch.scala b/test/files/run/virtpatmat_switch.scala index 2e2c31e8e5..1329c19d0f 100644 --- a/test/files/run/virtpatmat_switch.scala +++ b/test/files/run/virtpatmat_switch.scala @@ -14,9 +14,15 @@ object Test extends App { case 'b' => "got b" case _ => "got some letter" } + + def byteSwitch(x: Byte) = x match { + case 'a' => "got a" + case 'b' => "got b" + case _ => "got some letter" + } println(charSwitch('a')) - println(charSwitch('b')) + println(byteSwitch('b')) println(charSwitch('z')) def implicitDefault(x: Int) = x match { diff --git a/test/files/run/virtpatmat_try.check b/test/files/run/virtpatmat_try.check new file mode 100644 index 0000000000..80ebbf494a --- /dev/null +++ b/test/files/run/virtpatmat_try.check @@ -0,0 +1,2 @@ +meh +B diff --git a/test/files/run/virtpatmat_try.flags b/test/files/run/virtpatmat_try.flags new file mode 100644 index 0000000000..9769db9257 --- /dev/null +++ b/test/files/run/virtpatmat_try.flags @@ -0,0 +1 @@ + -Yvirtpatmat -Xexperimental diff --git a/test/files/run/virtpatmat_try.scala b/test/files/run/virtpatmat_try.scala new file mode 100644 index 0000000000..46e67cb72e --- /dev/null +++ b/test/files/run/virtpatmat_try.scala @@ -0,0 +1,47 @@ +object Test extends App { + case class A(val x: String) extends Throwable + class B extends Exception { override def toString = "B" } + def bla = 0 + + try { + throw new A("meh") + } catch { // this should emit a "catch-switch" + case y: A => println(y.x) + case (_ : A | _ : B) => println("B") + case _ => println("other") + } + + try { + throw new B() + } catch { // case classes and alternative flattening aren't supported yet, but could be in principle + // case A(x) => println(x) + case y: A => println(y.x) + case x@((_ : A) | (_ : B)) => println(x) + case _ => println("other") + } + + def simpleTry { + try { + bla + } catch { + case x: Exception if x.getMessage == "test" => println("first case " + x) + case x: Exception => println("second case " + x) + } + } + + def typedWildcardTry { + try { bla } catch { case _: ClassCastException => bla } + } + + def wildcardTry { + try { bla } catch { case _ => bla } + } + + def tryPlusFinally { + try { bla } finally { println("finally") } + } + + def catchAndPassToLambda { + try { bla } catch { case ex: Exception => val f = () => ex } + } +}
\ No newline at end of file diff --git a/test/files/specialized/SI-5005.scala b/test/files/specialized/SI-5005.scala index cc9d327b08..3d1ada49e2 100644 --- a/test/files/specialized/SI-5005.scala +++ b/test/files/specialized/SI-5005.scala @@ -17,7 +17,11 @@ object Test extends DirectTest { override def show(): Unit = { // redirect err to out, for inliner log - System.setErr(new PrintStream(System.out)); + val prevErr = System.err + System.setErr(System.out) compile() + System.setErr(prevErr) } + + override def isDebug = false // so we don't get the newSettings warning } |