diff options
author | Adriaan Moors <adriaan.moors@epfl.ch> | 2012-04-27 17:12:03 +0200 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@epfl.ch> | 2012-05-02 12:30:29 +0200 |
commit | bc860f3a31db8b6f37c9931f2bf4712fed06d486 (patch) | |
tree | 8de88e3e6391729d1afebf733fa05acb2c618adb /src/compiler | |
parent | f004e99b2fb7450fbfd0c5d96a4b2406cb8fc142 (diff) | |
download | scala-bc860f3a31db8b6f37c9931f2bf4712fed06d486.tar.gz scala-bc860f3a31db8b6f37c9931f2bf4712fed06d486.tar.bz2 scala-bc860f3a31db8b6f37c9931f2bf4712fed06d486.zip |
move more of match translation out of typers
reduce duplication in [typed/translated]Match & co
in preparation of moving match translation out of the type checker,
setting everything up so that we can simply type Match nodes first,
then translate them separately
using DefaultOverrideMatchAttachment to remember the default override for a match
that defines a PartialFunction
only strip annotations when translating match
or cps in matches fails
widen selector type when translating
match-derived partialfunction slightly less cps-specific
Diffstat (limited to 'src/compiler')
3 files changed, 78 insertions, 79 deletions
diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala index 23d517eba9..7347249b76 100644 --- a/src/compiler/scala/reflect/internal/Definitions.scala +++ b/src/compiler/scala/reflect/internal/Definitions.scala @@ -412,8 +412,6 @@ trait Definitions extends reflect.api.StandardDefinitions { lazy val JavaRepeatedParamClass = specialPolyClass(tpnme.JAVA_REPEATED_PARAM_CLASS_NAME, COVARIANT)(tparam => arrayType(tparam.tpe)) lazy val RepeatedParamClass = specialPolyClass(tpnme.REPEATED_PARAM_CLASS_NAME, COVARIANT)(tparam => seqType(tparam.tpe)) - lazy val MarkerCPSTypes = getClassIfDefined("scala.util.continuations.cpsParam") - def isByNameParamType(tp: Type) = tp.typeSymbol == ByNameParamClass def isScalaRepeatedParamType(tp: Type) = tp.typeSymbol == RepeatedParamClass def isJavaRepeatedParamType(tp: Type) = tp.typeSymbol == JavaRepeatedParamClass diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala index e5b5746e8d..b3f4b10865 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala @@ -35,6 +35,9 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => val SYNTH_CASE = Flags.CASE | SYNTHETIC + object TranslatedMatchAttachment + case class DefaultOverrideMatchAttachment(default: Tree) + object vpmName { val one = newTermName("one") val drop = newTermName("drop") @@ -136,15 +139,36 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => * thus, you must typecheck the result (and that will in turn translate nested matches) * 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 = { + def translateMatch(match_ : Match): Tree = { + val Match(selector, cases) = match_ + // 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 "+ scrut +" match "+ cases) + if(phase.id >= currentRun.uncurryPhase.id) debugwarn("running translateMatch at "+ phase +" on "+ selector +" match "+ cases) // println("translating "+ cases.mkString("{", "\n", "}")) - val scrutSym = freshSym(scrut.pos, pureType(scrutType)) setFlag SYNTH_CASE + + def repeatedToSeq(tp: Type): Type = (tp baseType RepeatedParamClass) match { + case TypeRef(_, RepeatedParamClass, arg :: Nil) => seqType(arg) + case _ => tp + } + + val selectorTp = repeatedToSeq(elimAnonymousClass(selector.tpe.widen.withoutAnnotations)) + val pt0 = match_.tpe + + // 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 + // TODO: fix skolemizeExistential (it should preserve annotations, right?) + val pt = repeatedToSeq(pt0.skolemizeExistential(context.owner, context.tree) withAnnotations pt0.annotations) + + // the alternative to attaching the default case override would be to simply + // append the default to the list of cases and suppress the unreachable case error that may arise (once we detect that...) + val matchFailGenOverride = match_ firstAttachment {case DefaultOverrideMatchAttachment(default) => ((scrut: Tree) => default)} + + val selectorSym = freshSym(selector.pos, pureType(selectorTp)) setFlag SYNTH_CASE // pt = Any* occurs when compiling test/files/pos/annotDepMethType.scala with -Xexperimental - combineCases(scrut, scrutSym, cases map translateCase(scrutSym, pt), pt, matchOwner, matchFailGenOverride) + combineCases(selector, selectorSym, cases map translateCase(selectorSym, pt), pt, matchOwner, matchFailGenOverride) } // return list of typed CaseDefs that are supported by the backend (typed/bind/wildcard) @@ -1586,7 +1610,7 @@ class Foo(x: Other) { x._1 } // no error in this order else (REF(scrutSym) DOT (nme.toInt)) Some(BLOCK( VAL(scrutSym) === scrut, - Match(gen.mkSynthSwitchSelector(scrutToInt), caseDefsWithDefault) // add switch annotation + Match(scrutToInt, caseDefsWithDefault) withAttachment TranslatedMatchAttachment // add switch annotation )) } } else None diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 2410117af7..553cafe966 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2208,44 +2208,26 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser def adaptCase(cdef: CaseDef, mode: Int, tpe: Type): CaseDef = deriveCaseDef(cdef)(adapt(_, mode, tpe)) + def ptOrLub(tps: List[Type], pt: Type ) = if (isFullyDefined(pt)) (pt, false) else weakLub(tps map (_.deconst)) + def ptOrLubPacked(trees: List[Tree], pt: Type) = if (isFullyDefined(pt)) (pt, false) else weakLub(trees map (c => packedType(c, context.owner).deconst)) + // takes untyped sub-trees of a match and type checks them - def typedMatch(selector0: Tree, cases: List[CaseDef], mode: Int, resTp: Type): Match = { - // strip off the annotation as it won't type check - val (selector, doTranslation) = selector0 match { - case Annotated(Ident(nme.synthSwitch), selector) => (selector, false) - case s => (s, true) - } - val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType)) - val selectorTp = packCaptured(selector1.tpe.widen.withoutAnnotations) + def typedMatch(selector: Tree, cases: List[CaseDef], mode: Int, pt: Type, tree: Tree = EmptyTree): Match = { + val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType)) + val selectorTp = packCaptured(selector1.tpe.widen) + val casesTyped = typedCases(cases, selectorTp, pt) - val casesTyped = typedCases(cases, selectorTp, resTp) - val caseTypes = casesTyped map (c => packedType(c, context.owner).deconst) - val (ownType, needAdapt) = if (isFullyDefined(resTp)) (resTp, false) else weakLub(caseTypes) + val (resTp, needAdapt) = + if (opt.virtPatmat) ptOrLubPacked(casesTyped, pt) + else ptOrLub(casesTyped map (_.tpe), pt) - val casesAdapted = if (!needAdapt) casesTyped else casesTyped map (adaptCase(_, mode, ownType)) + val casesAdapted = if (!needAdapt) casesTyped else casesTyped map (adaptCase(_, mode, resTp)) - (selector1, selectorTp, casesAdapted, ownType, doTranslation) + treeCopy.Match(tree, selector1, casesAdapted) setType resTp } // match has been typed, now translate it - def translatedMatch(selector1: Tree, selectorTp: Type, casesAdapted: List[CaseDef], ownType: Type, doTranslation: Boolean, matchFailGen: Option[Tree => Tree] = None) = { - def repeatedToSeq(tp: Type): Type = (tp baseType RepeatedParamClass) match { - case TypeRef(_, RepeatedParamClass, arg :: Nil) => seqType(arg) - case _ => tp - } - - if (!doTranslation) { // a switch - Match(selector1, casesAdapted) setType ownType // setType of the Match to avoid recursing endlessly - } else { - val scrutType = repeatedToSeq(elimAnonymousClass(selectorTp)) - // 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 - // 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) - } - } + def translatedMatch(match_ : Match) = MatchTranslator(this).translateMatch(match_) // synthesize and type check a (Partial)Function implementation based on a match specified by `cases` // Match(EmptyTree, cases) ==> new <Partial>Function { def apply<OrElse>(params) = `translateMatch('`(param1,...,paramN)` match { cases }')` } @@ -2298,7 +2280,8 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser val methodBodyTyper = newTyper(context.makeNewScope(context.tree, methodSym)) // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it) paramSyms foreach (methodBodyTyper.context.scope enter _) - val (selector1, selectorTp, casesAdapted, resTp, doTranslation) = methodBodyTyper.typedMatch(selector, cases, mode, ptRes) + val match_ = methodBodyTyper.typedMatch(selector, cases, mode, ptRes) + val resTp = match_.tpe val methFormals = paramSyms map (_.tpe) val parents = @@ -2308,7 +2291,7 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser anonClass setInfo ClassInfoType(parents, newScope, anonClass) methodSym setInfoAndEnter MethodType(paramSyms, resTp) - DefDef(methodSym, methodBodyTyper.translatedMatch(selector1, selectorTp, casesAdapted, resTp, doTranslation)) + DefDef(methodSym, methodBodyTyper.translatedMatch(match_)) } } @@ -2337,16 +2320,17 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser val methodBodyTyper = newTyper(context.makeNewScope(context.tree, methodSym)) // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it) paramSyms foreach (methodBodyTyper.context.scope enter _) - val (selector1, selectorTp, casesAdapted, resTp, doTranslation) = methodBodyTyper.typedMatch(selector, cases, mode, ptRes) + val match_ = methodBodyTyper.typedMatch(selector, cases, mode, ptRes) + val resTp = match_.tpe anonClass setInfo ClassInfoType(parentsPartial(List(argTp, resTp)), newScope, anonClass) B1 setInfo TypeBounds.lower(resTp) anonClass.info.decls enter methodSym // methodSym's info need not change (B1's bound has been updated instead) - // use applyOrElse's first parameter since the scrut's type has been widened - def doDefault(scrut_ignored: Tree) = REF(default) APPLY (REF(x)) + match_ setType B1.tpe - val body = methodBodyTyper.translatedMatch(selector1, selectorTp, casesAdapted, B1.tpe, doTranslation, Some(doDefault)) + // the default uses applyOrElse's first parameter since the scrut's type has been widened + val body = methodBodyTyper.translatedMatch(match_ withAttachment DefaultOverrideMatchAttachment(REF(default) APPLY (REF(x)))) DefDef(methodSym, body) } @@ -2363,17 +2347,18 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser paramSyms foreach (methodBodyTyper.context.scope enter _) methodSym setInfoAndEnter MethodType(paramSyms, BooleanClass.tpe) - val (selector1, selectorTp, casesAdapted, resTp, doTranslation) = methodBodyTyper.typedMatch(selector, casesTrue, mode, BooleanClass.tpe) - val body = methodBodyTyper.translatedMatch(selector1, selectorTp, casesAdapted, resTp, doTranslation, Some(scrutinee => FALSE_typed)) + val match_ = methodBodyTyper.typedMatch(selector, casesTrue, mode, BooleanClass.tpe) + val body = methodBodyTyper.translatedMatch(match_ withAttachment DefaultOverrideMatchAttachment(FALSE_typed)) DefDef(methodSym, body) } } val members = if (isPartial) { - // TODO: don't check for MarkerCPSTypes -- check whether all targs are subtype of any (which they are not under CPS) - if ((MarkerCPSTypes ne NoSymbol) && (targs exists (_ hasAnnotation MarkerCPSTypes))) List(applyMethod, isDefinedAtMethod) - else List(applyOrElseMethodDef, isDefinedAtMethod) + // somehow @cps annotations upset the typer when looking at applyOrElse's signature, but not apply's + // TODO: figure out the details (T @cps[U] is not a subtype of Any, but then why does it work for the apply method?) + if (targs forall (_ <:< AnyClass.tpe)) List(applyOrElseMethodDef, isDefinedAtMethod) + else List(applyMethod, isDefinedAtMethod) } else List(applyMethod) def translated = @@ -3639,8 +3624,6 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser def isPatternMode = inPatternMode(mode) //Console.println("typed1("+tree.getClass()+","+Integer.toHexString(mode)+","+pt+")") - def ptOrLub(tps: List[Type]) = if (isFullyDefined(pt)) (pt, false) else weakLub(tps map (_.deconst)) - //@M! get the type of the qualifier in a Select tree, otherwise: NoType def prefixType(fun: Tree): Type = fun match { case Select(qualifier, _) => qualifier.tpe @@ -3830,7 +3813,7 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser && 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)) + else ptOrLub(List(thenp1.tpe, elsep1.tpe), pt) if (needAdapt) { //isNumericValueType(owntype)) { thenp1 = adapt(thenp1, mode, owntype) @@ -3840,34 +3823,28 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser } } - def typedTranslatedMatch(tree: Tree, selector: Tree, cases: List[CaseDef]): Tree = { - if (doMatchTranslation) { - if (selector ne EmptyTree) { - val (selector1, selectorTp, casesAdapted, ownType, doTranslation) = typedMatch(selector, cases, mode, pt) - typed(translatedMatch(selector1, selectorTp, casesAdapted, ownType, doTranslation), mode, pt) - } else (new MatchFunTyper(tree, cases, mode, pt)).translated - } else if (selector == EmptyTree) { - if (opt.virtPatmat) debugwarn("virtpatmat should not encounter empty-selector matches "+ tree) - val arity = if (isFunctionType(pt)) pt.normalize.typeArgs.length - 1 else 1 - val params = for (i <- List.range(0, arity)) yield - atPos(tree.pos.focusStart) { - ValDef(Modifiers(PARAM | SYNTHETIC), - unit.freshTermName("x" + i + "$"), TypeTree(), EmptyTree) - } - val ids = for (p <- params) yield Ident(p.name) - 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 { - val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType)) - var cases1 = typedCases(cases, packCaptured(selector1.tpe.widen), pt) - val (owntype, needAdapt) = ptOrLub(cases1 map (_.tpe)) - if (needAdapt) { - cases1 = cases1 map (adaptCase(_, mode, owntype)) + def typedTranslatedMatch(tree: Tree, selector: Tree, cases: List[CaseDef]): Tree = + if (selector == EmptyTree) { + if (doMatchTranslation) (new MatchFunTyper(tree, cases, mode, pt)).translated + else { + if (opt.virtPatmat) debugwarn("virtpatmat should not encounter empty-selector matches "+ tree) + val arity = if (isFunctionType(pt)) pt.normalize.typeArgs.length - 1 else 1 + val params = for (i <- List.range(0, arity)) yield + atPos(tree.pos.focusStart) { + ValDef(Modifiers(PARAM | SYNTHETIC), + unit.freshTermName("x" + i + "$"), TypeTree(), EmptyTree) + } + val ids = for (p <- params) yield Ident(p.name) + 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) } - treeCopy.Match(tree, selector1, cases1) setType owntype + } else { + if (!doMatchTranslation || (tree firstAttachment {case TranslatedMatchAttachment => } nonEmpty)) + typedMatch(selector, cases, mode, pt, tree) + else + typed(translatedMatch(typedMatch(selector, cases, mode, pt, tree)), mode, pt) } - } def typedReturn(expr: Tree) = { val enclMethod = context.enclMethod @@ -4719,7 +4696,7 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser 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))) + val (owntype, needAdapt) = ptOrLub(block1.tpe :: (catches1 map (_.tpe)), pt) if (needAdapt) { block1 = adapt(block1, mode, owntype) catches1 = catches1 map (adaptCase(_, mode, owntype)) |