summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@epfl.ch>2012-04-13 19:15:35 +0200
committerAdriaan Moors <adriaan.moors@epfl.ch>2012-04-14 11:50:28 +0200
commit391f92f4005700799ac2e775980f5c5a77fea203 (patch)
treea86fe28e319d66363518808eae126a0ca4cf69e3 /src/compiler
parente1c8e2da26831c9a2d123bed5cb0f53230a3f939 (diff)
downloadscala-391f92f4005700799ac2e775980f5c5a77fea203.tar.gz
scala-391f92f4005700799ac2e775980f5c5a77fea203.tar.bz2
scala-391f92f4005700799ac2e775980f5c5a77fea203.zip
virtpatmat: initial CPS support
typers&patmatvirtualizer have ad-hoc support for dropping annotations in a way that makes the CPS plugins happy... this is not ideal, but unless virtpatmat runs after the plugin phases, I don't see how to solve it running virtpatmat after the CPS plugin would mean the pattern matching evaluation cannot be captured by CPS, so it's not even desirable to move it to a later phase - typedIf must lub annotated types - drop selector.tpe's annotations - drop annots in matchEnd's argument type - deal with annots in casts synth by in virtpatmat (drop them from type arg to asInstanceof, recover them using type ascription) - workaround skolemize existential dropping annots CPS is the main reason why typedMatchAnonFun is not used anymore, and PartialFunction synthesis is moved back to uncurry (which is quite painful due to labeldefs being so broken) we can't synth partialfunction during typer since T @cps[U] does not conform to Any, so we can't pass it as a type arg to PartialFunction, so we can't type a cps-transformed PF after the CPS plugin, T @cps[U] becomes ControlContext[...], which is a type we can pass to PartialFunction virtpatmat is now also run until right before uncurry (so, can't use isPastTyper, although it means more or less the same thing -- we don't run after uncurry) the main functional improvements are in the selective ANF transform its treatment of labeldefs was broken: for example, LabelDef L1; LabelDef L2 --> DefDef L1; L1(); DefDef L2; L2() but this does not take into account L1 may jump over L2 to another label since methods always return (or fail), and the ANF transform generates ValDefs to store the result of those method calls, both L1 and L2 would always be executed (so you would run a match with N cases N times, with each partial run starting at a later case) also fixed a couple of weird bugs in selective anf that caused matches to be duplicated (with the duplicate being nested in the original) since label defs are turned into method defs, and later defs will be nested in the flatMap calls on the controlcontext yielded by earlier statements, we reverse the list of method definitions, so that earlier (in the control flow sense) methods are visible in later ones selective CPS now generates a catch that's directly digestible by backend
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala28
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala16
2 files changed, 21 insertions, 23 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala
index 9b50853c51..1fd9f6fc13 100644
--- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala
@@ -132,10 +132,10 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
* this could probably optimized... (but note that the matchStrategy must be solved for each nested patternmatch)
*/
def translateMatch(scrut: Tree, cases: List[CaseDef], pt: Type, scrutType: Type, matchFailGenOverride: Option[Tree => Tree] = None): Tree = {
- // we don't transform after typers
- // (that would require much more sophistication when generating trees,
+ // 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)
- assert(phase.id <= currentRun.typerPhase.id, phase)
+ assert(phase.id < currentRun.uncurryPhase.id, phase)
val scrutSym = freshSym(scrut.pos, pureType(scrutType)) setFlag SYNTH_CASE
// pt = Any* occurs when compiling test/files/pos/annotDepMethType.scala with -Xexperimental
@@ -1105,22 +1105,14 @@ class Foo(x: Other) { x._1 } // no error in this order
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
+ // drop annotations generated by CPS plugin etc, since its annotationchecker rejects T @cps[U] <: Any
+ // let's assume for now annotations don't affect casts, drop them there, and bring them back using the outer Typed tree
+ private def mkCast(t: Tree, tp: Type) = Typed(gen.mkAsInstanceOf(t, tp.withoutAnnotations, true, false), TypeTree() setType tp)
// 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, force: Boolean = false): Tree = { val tpX = /*repackExistential*/(tp)
- if (!force && (t.tpe ne NoType) && t.isTyped && typesConform(t.tpe, tpX)) t //{ println("warning: emitted redundant asInstanceOf: "+(t, t.tpe, tp)); t } //.setType(tpX)
- else gen.mkAsInstanceOf(t, tpX, true, false)
- }
-
- def _isInstanceOf(b: Symbol, tp: Type): Tree = gen.mkIsInstanceOf(REF(b), /*repackExistential*/(tp), true, false)
- // { val tpX = /*repackExistential*/(tp)
+ def _asInstanceOf(t: Tree, tp: Type, force: Boolean = false): Tree = if (!force && (t.tpe ne NoType) && t.isTyped && typesConform(t.tpe, tp)) t else mkCast(t, tp)
+ def _asInstanceOf(b: Symbol, tp: Type): Tree = if (typesConform(b.info, tp)) REF(b) else mkCast(REF(b), tp)
+ def _isInstanceOf(b: Symbol, tp: Type): Tree = gen.mkIsInstanceOf(REF(b), tp.withoutAnnotations, true, false)
// if (typesConform(b.info, tpX)) { println("warning: emitted spurious isInstanceOf: "+(b, tp)); TRUE }
- // else gen.mkIsInstanceOf(REF(b), tpX, true, false)
- // }
-
- def _asInstanceOf(b: Symbol, tp: Type): Tree = { val tpX = /*repackExistential*/(tp)
- if (typesConform(b.info, tpX)) REF(b) //{ println("warning: emitted redundant asInstanceOf: "+(b, b.info, tp)); REF(b) } //.setType(tpX)
- else gen.mkAsInstanceOf(REF(b), tpX, true, false)
- }
// duplicated out of frustration with cast generation
def mkZero(tp: Type): Tree = {
@@ -1658,7 +1650,7 @@ class Foo(x: Other) { x._1 } // no error in this order
*/
def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], matchFailGen: Option[Tree => Tree]): Tree = {
val matchEnd = NoSymbol.newLabel(freshName("matchEnd"), NoPosition) setFlag SYNTH_CASE
- val matchRes = NoSymbol.newValueParameter(newTermName("x"), NoPosition, SYNTHETIC) setInfo restpe
+ val matchRes = NoSymbol.newValueParameter(newTermName("x"), NoPosition, SYNTHETIC) setInfo restpe.withoutAnnotations //
matchEnd setInfo MethodType(List(matchRes), restpe)
def newCaseSym = NoSymbol.newLabel(freshName("case"), NoPosition) setInfo MethodType(Nil, restpe) setFlag SYNTH_CASE
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index c4eb43c427..dd8631ba21 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -2199,7 +2199,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
case s => (s, true)
}
val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType))
- val selectorTp = packCaptured(selector1.tpe.widen)
+ val selectorTp = packCaptured(selector1.tpe.widen.withoutAnnotations)
val casesTyped = typedCases(cases, selectorTp, resTp)
val caseTypes = casesTyped map (c => packedType(c, context.owner).deconst)
@@ -2223,7 +2223,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
// we've packed the type for each case in typedMatch so that if all cases have the same existential case, we get a clean lub
// here, we should open up the existential again
// relevant test cases: pos/existentials-harmful.scala, pos/gadt-gilles.scala, pos/t2683.scala, pos/virtpatmat_exist4.scala
- MatchTranslator(this).translateMatch(selector1, casesAdapted, repeatedToSeq(ownType.skolemizeExistential(context.owner, context.tree)), scrutType, matchFailGen)
+ // TODO: fix skolemizeExistential (it should preserve annotations, right?)
+ val ownTypeSkolemized = ownType.skolemizeExistential(context.owner, context.tree) withAnnotations ownType.annotations
+ MatchTranslator(this).translateMatch(selector1, casesAdapted, repeatedToSeq(ownTypeSkolemized), scrutType, matchFailGen)
}
}
@@ -3772,12 +3774,16 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
lazy val thenTp = packedType(thenp1, context.owner)
lazy val elseTp = packedType(elsep1, context.owner)
+ // println("typedIf: "+(thenp1.tpe, elsep1.tpe, ptOrLub(List(thenp1.tpe, elsep1.tpe)),"\n", thenTp, elseTp, thenTp =:= elseTp))
val (owntype, needAdapt) =
// in principle we should pack the types of each branch before lubbing, but lub doesn't really work for existentials anyway
// in the special (though common) case where the types are equal, it pays to pack before comparing
// especially virtpatmat needs more aggressive unification of skolemized types
// this breaks src/library/scala/collection/immutable/TrieIterator.scala
- if (opt.virtPatmat && !isPastTyper && thenTp =:= elseTp) (thenp1.tpe, false) // use unpacked type
+ if ( opt.virtPatmat && !isPastTyper
+ && thenp1.tpe.annotations.isEmpty && elsep1.tpe.annotations.isEmpty // annotated types need to be lubbed regardless (at least, continations break if you by pass them like this)
+ && thenTp =:= elseTp
+ ) (thenp1.tpe, false) // use unpacked type
// TODO: skolemize (lub of packed types) when that no longer crashes on files/pos/t4070b.scala
else ptOrLub(List(thenp1.tpe, elsep1.tpe))
@@ -3802,7 +3808,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
val selector1 = atPos(tree.pos.focusStart) { if (arity == 1) ids.head else gen.mkTuple(ids) }
val body = treeCopy.Match(tree, selector1, cases)
typed1(atPos(tree.pos) { Function(params, body) }, mode, pt)
- } else if (!opt.virtPatmat || isPastTyper) {
+ } else if (!((phase.id < currentRun.uncurryPhase.id) && opt.virtPatmat)) {
val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType))
var cases1 = typedCases(cases, packCaptured(selector1.tpe.widen), pt)
val (owntype, needAdapt) = ptOrLub(cases1 map (_.tpe))
@@ -4688,7 +4694,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
catches1 = catches1 map (adaptCase(_, mode, owntype))
}
- if(!isPastTyper && opt.virtPatmat) {
+ if((phase.id < currentRun.uncurryPhase.id) && opt.virtPatmat) {
catches1 = (MatchTranslator(this)).translateTry(catches1, owntype, tree.pos)
}