diff --git a/src/compiler/scala/reflect/internal/AnnotationInfos.scala b/src/compiler/scala/reflect/internal/AnnotationInfos.scala index f2f4d334118..9d67040bf71 100644 --- a/src/compiler/scala/reflect/internal/AnnotationInfos.scala +++ b/src/compiler/scala/reflect/internal/AnnotationInfos.scala @@ -24,6 +24,7 @@ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable => def annotations: List[AnnotationInfo] // Annotations on this type. def setAnnotations(annots: List[AnnotationInfo]): Self // Replace annotations with argument list. def withAnnotations(annots: List[AnnotationInfo]): Self // Add annotations to this type. + def filterAnnotations(p: AnnotationInfo => Boolean): Self // Retain only annotations meeting the condition. def withoutAnnotations: Self // Remove all annotations from this type. /** Symbols of any @throws annotations on this symbol. @@ -35,9 +36,8 @@ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable => /** Test for, get, or remove an annotation */ def hasAnnotation(cls: Symbol) = annotations exists (_ matches cls) def getAnnotation(cls: Symbol) = annotations find (_ matches cls) - def removeAnnotation(cls: Symbol): Self = - if (hasAnnotation(cls)) setAnnotations(annotations filterNot (_ matches cls)) - else this + def removeAnnotation(cls: Symbol): Self = filterAnnotations(ann => !(ann matches cls)) + final def withAnnotation(annot: AnnotationInfo): Self = withAnnotations(List(annot)) } /** Arguments to classfile annotations (which are written to @@ -95,6 +95,9 @@ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable => object NestedAnnotArg extends NestedAnnotArgExtractor object AnnotationInfo extends AnnotationInfoExtractor { + def marker(atp: Type): AnnotationInfo = + apply(atp, Nil, Nil) + def lazily(lazyInfo: => AnnotationInfo) = new LazyAnnotationInfo(lazyInfo) @@ -233,6 +236,12 @@ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable => def argAtIndex(index: Int): Option[Tree] = if (index < args.size) Some(args(index)) else None + + override def hashCode = atp.## + args.## + assocs.## + override def equals(other: Any) = other match { + case x: AnnotationInfo => (atp == x.atp) && (args == x.args) && (assocs == x.assocs) + case _ => false + } } lazy val classfileAnnotArgManifest: ClassManifest[ClassfileAnnotArg] = diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index 0a9bb8c888f..8a11b731197 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -1199,6 +1199,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => def withoutAnnotations: this.type = setAnnotations(Nil) + + def filterAnnotations(p: AnnotationInfo => Boolean): this.type = + setAnnotations(annotations filter p) def addAnnotation(annot: AnnotationInfo): this.type = setAnnotations(annot :: annotations) diff --git a/src/compiler/scala/reflect/internal/Trees.scala b/src/compiler/scala/reflect/internal/Trees.scala index 9b8145413c5..4dfe0dee75f 100644 --- a/src/compiler/scala/reflect/internal/Trees.scala +++ b/src/compiler/scala/reflect/internal/Trees.scala @@ -101,6 +101,13 @@ trait Trees extends api.Trees { self: SymbolTable => class TreeOps(tree: Tree) { def isErroneous = (tree.tpe ne null) && tree.tpe.isErroneous def isTyped = (tree.tpe ne null) && !tree.tpe.isErroneous + + /** Sets the tree's type to the result of the given function. + * If the type is null, it remains null - the function is not called. + */ + def modifyType(f: Type => Type): Tree = + if (tree.tpe eq null) tree + else tree setType f(tree.tpe) /** If `pf` is defined for a given subtree, call super.traverse(pf(tree)), * otherwise super.traverse(tree). diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala index 45e58400fbe..8ad8f8ec61f 100644 --- a/src/compiler/scala/reflect/internal/Types.scala +++ b/src/compiler/scala/reflect/internal/Types.scala @@ -1022,11 +1022,10 @@ trait Types extends api.Types { self: SymbolTable => // overrides these. def annotations: List[AnnotationInfo] = Nil def withoutAnnotations: Type = this + def filterAnnotations(p: AnnotationInfo => Boolean): Type = this def setAnnotations(annots: List[AnnotationInfo]): Type = annotatedType(annots, this) def withAnnotations(annots: List[AnnotationInfo]): Type = annotatedType(annots, this) - final def withAnnotation(annot: AnnotationInfo): Type = withAnnotations(List(annot)) - /** Remove any annotations from this type and from any * types embedded in this type. */ def stripAnnotations = StripAnnotationsMap(this) @@ -2715,8 +2714,14 @@ A type's typeSymbol should never be inspected directly. override def safeToString = annotations.mkString(underlying + " @", " @", "") + override def filterAnnotations(p: AnnotationInfo => Boolean): Type = { + val (yes, no) = annotations partition p + if (yes.isEmpty) underlying + else if (no.isEmpty) this + else copy(annotations = yes) + } override def setAnnotations(annots: List[AnnotationInfo]): Type = - if (annots.isEmpty) withoutAnnotations + if (annots.isEmpty) underlying else copy(annotations = annots) /** Add a number of annotations to this type */ @@ -2724,7 +2729,11 @@ A type's typeSymbol should never be inspected directly. if (annots.isEmpty) this else copy(annots ::: this.annotations) - /** Remove any annotations from this type */ + /** Remove any annotations from this type. + * TODO - is it allowed to nest AnnotatedTypes? If not then let's enforce + * that at creation. At the moment if they do ever turn up nested this + * recursively calls withoutAnnotations. + */ override def withoutAnnotations = underlying.withoutAnnotations /** Set the self symbol */ diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala index c339d2bd750..4c6f0cd90b1 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala @@ -16,7 +16,14 @@ abstract class CPSAnnotationChecker extends CPSUtils { * Checks whether @cps annotations conform */ object checker extends AnnotationChecker { + private def addPlusMarker(tp: Type) = tp withAnnotation newPlusMarker() + private def addMinusMarker(tp: Type) = tp withAnnotation newMinusMarker() + private def cleanPlus(tp: Type) = + removeAttribs(tp, MarkerCPSAdaptPlus, MarkerCPSTypes) + private def cleanPlusWith(tp: Type)(newAnnots: AnnotationInfo*) = + cleanPlus(tp) withAnnotations newAnnots.toList + /** Check annotations to decide whether tpe1 <:< tpe2 */ def annotationsConform(tpe1: Type, tpe2: Type): Boolean = { if (!cpsEnabled) return true @@ -27,78 +34,74 @@ abstract class CPSAnnotationChecker extends CPSUtils { if (tpe1.typeSymbol eq NothingClass) return true - val annots1 = filterAttribs(tpe1,MarkerCPSTypes) - val annots2 = filterAttribs(tpe2,MarkerCPSTypes) - + val annots1 = cpsParamAnnotation(tpe1) + val annots2 = cpsParamAnnotation(tpe2) + // @plus and @minus should only occur at the left, and never together // TODO: insert check - val adaptPlusAnnots1 = filterAttribs(tpe1,MarkerCPSAdaptPlus) - val adaptMinusAnnots1 = filterAttribs(tpe1,MarkerCPSAdaptMinus) // @minus @cps is the same as no annotations - if (!adaptMinusAnnots1.isEmpty) + if (hasMinusMarker(tpe1)) return annots2.isEmpty // to handle answer type modification, we must make @plus <:< @cps - if (!adaptPlusAnnots1.isEmpty && annots1.isEmpty) + if (hasPlusMarker(tpe1) && annots1.isEmpty) return true // @plus @cps will fall through and compare the @cps type args - // @cps parameters must match exactly - if ((annots1 corresponds annots2) { _.atp <:< _.atp }) - return true - - false + (annots1 corresponds annots2)(_.atp <:< _.atp) } - /** Refine the computed least upper bound of a list of types. * All this should do is add annotations. */ override def annotationsLub(tpe: Type, ts: List[Type]): Type = { if (!cpsEnabled) return tpe - val annots1 = filterAttribs(tpe, MarkerCPSTypes) - val annots2 = ts flatMap (filterAttribs(_, MarkerCPSTypes)) + val annots1 = cpsParamAnnotation(tpe) + val annots2 = ts flatMap cpsParamAnnotation if (annots2.nonEmpty) { - val cpsLub = AnnotationInfo(global.lub(annots1:::annots2 map (_.atp)), Nil, Nil) + val cpsLub = newMarker(global.lub(annots1:::annots2 map (_.atp))) val tpe1 = if (annots1.nonEmpty) removeAttribs(tpe, MarkerCPSTypes) else tpe tpe1.withAnnotation(cpsLub) - } else tpe + } + else tpe } /** Refine the bounds on type parameters to the given type arguments. */ override def adaptBoundsToAnnotations(bounds: List[TypeBounds], tparams: List[Symbol], targs: List[Type]): List[TypeBounds] = { if (!cpsEnabled) return bounds - val anyAtCPS = AnnotationInfo(appliedType(MarkerCPSTypes.tpe, List(NothingClass.tpe, AnyClass.tpe)), Nil, Nil) + val anyAtCPS = newCpsParamsMarker(NothingClass.tpe, AnyClass.tpe) + if (isFunctionType(tparams.head.owner.tpe) || tparams.head.owner == PartialFunctionClass) { vprintln("function bound: " + tparams.head.owner.tpe + "/"+bounds+"/"+targs) - if (targs.last.hasAnnotation(MarkerCPSTypes)) + if (hasCpsParamTypes(targs.last)) bounds.reverse match { - case res::b if !res.hi.hasAnnotation(MarkerCPSTypes) => + case res::b if !hasCpsParamTypes(res.hi) => (TypeBounds(res.lo, res.hi.withAnnotation(anyAtCPS))::b).reverse case _ => bounds } else bounds - } else if (tparams.head.owner == ByNameParamClass) { + } + else if (tparams.head.owner == ByNameParamClass) { vprintln("byname bound: " + tparams.head.owner.tpe + "/"+bounds+"/"+targs) - if (targs.head.hasAnnotation(MarkerCPSTypes) && !bounds.head.hi.hasAnnotation(MarkerCPSTypes)) - TypeBounds(bounds.head.lo, bounds.head.hi.withAnnotation(anyAtCPS))::Nil + val TypeBounds(lo, hi) = bounds.head + if (hasCpsParamTypes(targs.head) && !hasCpsParamTypes(hi)) + TypeBounds(lo, hi withAnnotation anyAtCPS) :: Nil else bounds } else bounds } - override def canAdaptAnnotations(tree: Tree, mode: Int, pt: Type): Boolean = { if (!cpsEnabled) return false vprintln("can adapt annotations? " + tree + " / " + tree.tpe + " / " + Integer.toHexString(mode) + " / " + pt) - val annots1 = filterAttribs(tree.tpe,MarkerCPSTypes) - val annots2 = filterAttribs(pt,MarkerCPSTypes) + val annots1 = cpsParamAnnotation(tree.tpe) + val annots2 = cpsParamAnnotation(pt) if ((mode & global.analyzer.PATTERNmode) != 0) { //println("can adapt pattern annotations? " + tree + " / " + tree.tpe + " / " + Integer.toHexString(mode) + " / " + pt) @@ -124,15 +127,14 @@ abstract class CPSAnnotationChecker extends CPSUtils { } */ if ((mode & global.analyzer.EXPRmode) != 0) { - if ((annots1 corresponds annots2) { case (a1,a2) => a1.atp <:< a2.atp }) { + if ((annots1 corresponds annots2)(_.atp <:< _.atp)) { vprintln("already same, can't adapt further") return false } if (annots1.isEmpty && !annots2.isEmpty && ((mode & global.analyzer.BYVALmode) == 0)) { //println("can adapt annotations? " + tree + " / " + tree.tpe + " / " + Integer.toHexString(mode) + " / " + pt) - val adapt = AnnotationInfo(MarkerCPSAdaptPlus.tpe, Nil, Nil) - if (!tree.tpe.annotations.contains(adapt)) { + if (!hasPlusMarker(tree.tpe)) { // val base = tree.tpe <:< removeAllCPSAnnotations(pt) // val known = global.analyzer.isFullyDefined(pt) // println(same + "/" + base + "/" + known) @@ -144,7 +146,7 @@ abstract class CPSAnnotationChecker extends CPSUtils { //} } } else if (!annots1.isEmpty && ((mode & global.analyzer.BYVALmode) != 0)) { - if (!tree.tpe.hasAnnotation(MarkerCPSAdaptMinus)) { + if (!hasMinusMarker(tree.tpe)) { vprintln("yes we can!! (byval)") return true } @@ -152,19 +154,18 @@ abstract class CPSAnnotationChecker extends CPSUtils { } false } - - + override def adaptAnnotations(tree: Tree, mode: Int, pt: Type): Tree = { if (!cpsEnabled) return tree vprintln("adapt annotations " + tree + " / " + tree.tpe + " / " + Integer.toHexString(mode) + " / " + pt) - val annots1 = filterAttribs(tree.tpe,MarkerCPSTypes) - val annots2 = filterAttribs(pt,MarkerCPSTypes) + val annots1 = cpsParamAnnotation(tree.tpe) + val annots2 = cpsParamAnnotation(pt) if ((mode & global.analyzer.PATTERNmode) != 0) { if (!annots1.isEmpty) { - return tree.setType(removeAllCPSAnnotations(tree.tpe)) + return tree modifyType removeAllCPSAnnotations } } @@ -185,8 +186,6 @@ abstract class CPSAnnotationChecker extends CPSUtils { // add a marker annotation that will make tree.tpe behave as pt, subtyping wise // tree will look like having any possible annotation //println("adapt annotations " + tree + " / " + tree.tpe + " / " + Integer.toHexString(mode) + " / " + pt) - - val adapt = AnnotationInfo(MarkerCPSAdaptPlus.tpe, Nil, Nil) //val same = annots2 forall { case AnnotationInfo(atp: TypeRef, _, _) => atp.typeArgs(0) =:= atp.typeArgs(1) } // TBD: use same or not? see infer0.scala/infer1.scala @@ -196,9 +195,9 @@ abstract class CPSAnnotationChecker extends CPSUtils { //val known = global.analyzer.isFullyDefined(pt) - if (/*same &&*/ !tree.tpe.annotations.contains(adapt)) { + if (/*same &&*/ !hasPlusMarker(tree.tpe)) { //if (known) - return tree.setType(tree.tpe.withAnnotations(adapt::annots2)) // needed for #1807 + return tree modifyType (_ withAnnotations newPlusMarker() :: annots2) // needed for #1807 //else // return tree.setType(tree.tpe.withAnnotations(adapt::Nil)) } @@ -206,16 +205,14 @@ abstract class CPSAnnotationChecker extends CPSUtils { } else if (!annots1.isEmpty && ((mode & global.analyzer.BYVALmode) != 0)) { // dropping annotation // add a marker annotation that will make tree.tpe behave as pt, subtyping wise // tree will look like having no annotation - if (!tree.tpe.hasAnnotation(MarkerCPSAdaptMinus)) { - val adapt = AnnotationInfo(MarkerCPSAdaptMinus.tpe, Nil, Nil) - return tree.setType(tree.tpe.withAnnotations(adapt::Nil)) + if (!hasMinusMarker(tree.tpe)) { + return tree modifyType addMinusMarker } } } tree } - def updateAttributesFromChildren(tpe: Type, childAnnots: List[AnnotationInfo], byName: List[Tree]): Type = { tpe match { // Would need to push annots into each alternative of overloaded type @@ -226,7 +223,7 @@ abstract class CPSAnnotationChecker extends CPSUtils { OverloadedType(pre, alts.map((sym: Symbol) => updateAttributes(pre.memberType(sym), annots))) */ case _ => - assert(childAnnots forall (_.atp.typeSymbol == MarkerCPSTypes), childAnnots) + assert(childAnnots forall (_ matches MarkerCPSTypes), childAnnots) /* [] + [] = [] plus + [] = plus @@ -243,56 +240,50 @@ abstract class CPSAnnotationChecker extends CPSUtils { synth cps + cps = synth cps! <- unify */ - val plus = tpe.hasAnnotation(MarkerCPSAdaptPlus) || (tpe.hasAnnotation(MarkerCPSTypes) && - byName.nonEmpty && byName.forall(_.tpe.hasAnnotation(MarkerCPSAdaptPlus))) - + val plus = hasPlusMarker(tpe) || ( + hasCpsParamTypes(tpe) + && byName.nonEmpty + && (byName forall (t => hasPlusMarker(t.tpe))) + ) + // move @plus annotations outward from by-name children - if (childAnnots.isEmpty) { + if (childAnnots.isEmpty) return { if (plus) { // @plus or @plus @cps - for (t <- byName) { - //println("removeAnnotation " + t + " / " + t.tpe) - t.setType(removeAttribs(t.tpe, MarkerCPSAdaptPlus, MarkerCPSTypes)) - } - return tpe.withAnnotation(AnnotationInfo(MarkerCPSAdaptPlus.tpe, Nil, Nil)) - } else - return tpe + byName foreach (_ modifyType cleanPlus) + addPlusMarker(tpe) + } + else tpe } - val annots1 = filterAttribs(tpe, MarkerCPSTypes) + val annots1 = cpsParamAnnotation(tpe) if (annots1.isEmpty) { // nothing or @plus - val synth = MarkerCPSSynth.tpe - val annots2 = List(linearize(childAnnots)) - removeAttribs(tpe,MarkerCPSAdaptPlus).withAnnotations(AnnotationInfo(synth, Nil, Nil)::annots2) - } else { + cleanPlusWith(tpe)(newSynthMarker(), linearize(childAnnots)) + } + else { val annot1 = single(annots1) if (plus) { // @plus @cps - val synth = AnnotationInfo(MarkerCPSSynth.tpe, Nil, Nil) val annot2 = linearize(childAnnots) - if (!(annot2.atp <:< annot1.atp)) - throw new TypeError(annot2 + " is not a subtype of " + annot1) - val res = removeAttribs(tpe, MarkerCPSAdaptPlus, MarkerCPSTypes).withAnnotations(List(synth, annot2)) - for (t <- byName) { - //println("removeAnnotation " + t + " / " + t.tpe) - t.setType(removeAttribs(t.tpe, MarkerCPSAdaptPlus, MarkerCPSTypes)) + + if (annot2.atp <:< annot1.atp) { + try cleanPlusWith(tpe)(newSynthMarker(), annot2) + finally byName foreach (_ modifyType cleanPlus) } - res - } else if (tpe.hasAnnotation(MarkerCPSSynth)) { // @synth @cps + else throw new TypeError(annot2 + " is not a subtype of " + annot1) + } + else if (hasSynthMarker(tpe)) { // @synth @cps val annot2 = linearize(childAnnots) - if (!(annot2.atp <:< annot1.atp)) + if (annot2.atp <:< annot1.atp) + cleanPlusWith(tpe)(annot2) + else throw new TypeError(annot2 + " is not a subtype of " + annot1) - removeAttribs(tpe, MarkerCPSTypes).withAnnotation(annot2) - } else { // @cps - removeAttribs(tpe, MarkerCPSTypes).withAnnotation(linearize(childAnnots:::annots1)) } + else // @cps + cleanPlusWith(tpe)(linearize(childAnnots:::annots1)) } } } - - - - def transArgList(fun: Tree, args: List[Tree]): List[List[Tree]] = { val formals = fun.tpe.paramTypes val overshoot = args.length - formals.length @@ -329,7 +320,7 @@ abstract class CPSAnnotationChecker extends CPSUtils { def transChildrenInOrder(tree: Tree, tpe: Type, childTrees: List[Tree], byName: List[Tree]) = { val children = childTrees.flatMap { t => if (t.tpe eq null) Nil else { - val types = filterAttribs(t.tpe, MarkerCPSTypes) + val types = cpsParamAnnotation(t.tpe) // TODO: check that it has been adapted and if so correctly if (types.isEmpty) Nil else List(single(types)) } @@ -348,10 +339,10 @@ abstract class CPSAnnotationChecker extends CPSUtils { override def addAnnotations(tree: Tree, tpe: Type): Type = { if (!cpsEnabled) { - if (tpe.annotations.nonEmpty && tpe.hasAnnotation(MarkerCPSTypes)) + if (hasCpsParamTypes(tpe)) global.reporter.error(tree.pos, "this code must be compiled with the Scala continuations plugin enabled") return tpe - } + } // if (tree.tpe.hasAnnotation(MarkerCPSAdaptPlus)) // println("addAnnotation " + tree + "/" + tpe) @@ -397,7 +388,7 @@ abstract class CPSAnnotationChecker extends CPSUtils { // we cannot safely annotate these. so we just ignore these cases and // clean up later in the Apply/TypeApply trees. - if (qual.tpe.hasAnnotation(MarkerCPSTypes)) { + if (hasCpsParamTypes(qual.tpe)) { // however there is one special case: // if it's a method without parameters, just apply it. normally done in adapt, but // we have to do it here so we don't lose the cps information (wouldn't trigger our @@ -425,10 +416,10 @@ abstract class CPSAnnotationChecker extends CPSUtils { case Try(block, catches, finalizer) => val tpe1 = transChildrenInOrder(tree, tpe, Nil, block::catches:::(catches collect { case CaseDef(_, _, body) => body })) - val annots = filterAttribs(tpe1, MarkerCPSTypes) + val annots = cpsParamAnnotation(tpe1) if (annots.nonEmpty) { val ann = single(annots) - val atp0::atp1::Nil = ann.atp.normalize.typeArgs + val (atp0, atp1) = annTypes(ann) if (!(atp0 =:= atp1)) throw new TypeError("only simple cps types allowed in try/catch blocks (found: " + tpe1 + ")") if (!finalizer.isEmpty) // no finalizers allowed. see explanation in SelectiveCPSTransform @@ -445,8 +436,8 @@ abstract class CPSAnnotationChecker extends CPSUtils { // ValDef symbols must *not* have annotations! if (hasAnswerTypeAnn(tree.symbol.info)) { // is it okay to modify sym here? vprintln("removing annotation from sym " + tree.symbol + "/" + tree.symbol.tpe + "/" + tpt) - tpt.setType(removeAllCPSAnnotations(tpt.tpe)) - tree.symbol.setInfo(removeAllCPSAnnotations(tree.symbol.info)) + tpt modifyType removeAllCPSAnnotations + tree.symbol modifyInfo removeAllCPSAnnotations } tpe diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala index f7879bb1c23..dcb1c593164 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala @@ -13,100 +13,94 @@ trait CPSUtils { val verbose: Boolean = System.getProperty("cpsVerbose", "false") == "true" def vprintln(x: =>Any): Unit = if (verbose) println(x) - lazy val MarkerCPSSym = definitions.getClass("scala.util.continuations.cpsSym") - lazy val MarkerCPSTypes = definitions.getClass("scala.util.continuations.cpsParam") - lazy val MarkerCPSSynth = definitions.getClass("scala.util.continuations.cpsSynth") - - lazy val MarkerCPSAdaptPlus = definitions.getClass("scala.util.continuations.cpsPlus") + lazy val MarkerCPSSym = definitions.getClass("scala.util.continuations.cpsSym") + lazy val MarkerCPSTypes = definitions.getClass("scala.util.continuations.cpsParam") + lazy val MarkerCPSSynth = definitions.getClass("scala.util.continuations.cpsSynth") + lazy val MarkerCPSAdaptPlus = definitions.getClass("scala.util.continuations.cpsPlus") lazy val MarkerCPSAdaptMinus = definitions.getClass("scala.util.continuations.cpsMinus") - lazy val Context = definitions.getClass("scala.util.continuations.ControlContext") - lazy val ModCPS = definitions.getModule("scala.util.continuations") - lazy val MethShiftUnit = definitions.getMember(ModCPS, "shiftUnit") - lazy val MethShiftUnitR = definitions.getMember(ModCPS, "shiftUnitR") - lazy val MethShift = definitions.getMember(ModCPS, "shift") - lazy val MethShiftR = definitions.getMember(ModCPS, "shiftR") - lazy val MethReify = definitions.getMember(ModCPS, "reify") - lazy val MethReifyR = definitions.getMember(ModCPS, "reifyR") + lazy val MethShiftUnit = definitions.getMember(ModCPS, "shiftUnit") + lazy val MethShiftUnitR = definitions.getMember(ModCPS, "shiftUnitR") + lazy val MethShift = definitions.getMember(ModCPS, "shift") + lazy val MethShiftR = definitions.getMember(ModCPS, "shiftR") + lazy val MethReify = definitions.getMember(ModCPS, "reify") + lazy val MethReifyR = definitions.getMember(ModCPS, "reifyR") lazy val allCPSAnnotations = List(MarkerCPSSym, MarkerCPSTypes, MarkerCPSSynth, MarkerCPSAdaptPlus, MarkerCPSAdaptMinus) + + // TODO - needed? Can these all use the same annotation info? + protected def newSynthMarker() = newMarker(MarkerCPSSynth) + protected def newPlusMarker() = newMarker(MarkerCPSAdaptPlus) + protected def newMinusMarker() = newMarker(MarkerCPSAdaptMinus) + protected def newMarker(tpe: Type): AnnotationInfo = AnnotationInfo marker tpe + protected def newMarker(sym: Symbol): AnnotationInfo = AnnotationInfo marker sym.tpe + + protected def newCpsParamsMarker(tp1: Type, tp2: Type) = + newMarker(appliedType(MarkerCPSTypes.tpe, List(tp1, tp2))) // annotation checker - def filterAttribs(tpe:Type, cls:Symbol) = - tpe.annotations.filter(_.atp.typeSymbol == cls) + protected def annTypes(ann: AnnotationInfo): (Type, Type) = { + val tp0 :: tp1 :: Nil = ann.atp.normalize.typeArgs + ((tp0, tp1)) + } + protected def hasMinusMarker(tpe: Type) = tpe hasAnnotation MarkerCPSAdaptMinus + protected def hasPlusMarker(tpe: Type) = tpe hasAnnotation MarkerCPSAdaptPlus + protected def hasSynthMarker(tpe: Type) = tpe hasAnnotation MarkerCPSSynth + protected def hasCpsParamTypes(tpe: Type) = tpe hasAnnotation MarkerCPSTypes + protected def cpsParamTypes(tpe: Type) = tpe getAnnotation MarkerCPSTypes map annTypes - def removeAttribs(tpe:Type, cls:Symbol*) = - tpe.withoutAnnotations.withAnnotations(tpe.annotations.filterNot(cls contains _.atp.typeSymbol)) + def filterAttribs(tpe:Type, cls:Symbol) = + tpe.annotations filter (_ matches cls) + + def removeAttribs(tpe: Type, classes: Symbol*) = + tpe filterAnnotations (ann => !(classes exists (ann matches _))) def removeAllCPSAnnotations(tpe: Type) = removeAttribs(tpe, allCPSAnnotations:_*) + def cpsParamAnnotation(tpe: Type) = filterAttribs(tpe, MarkerCPSTypes) + def linearize(ann: List[AnnotationInfo]): AnnotationInfo = { - ann.reduceLeft { (a, b) => - val atp0::atp1::Nil = a.atp.normalize.typeArgs - val btp0::btp1::Nil = b.atp.normalize.typeArgs - val (u0,v0) = (atp0, atp1) - val (u1,v1) = (btp0, btp1) -/* - val (u0,v0) = (a.atp.typeArgs(0), a.atp.typeArgs(1)) - val (u1,v1) = (b.atp.typeArgs(0), b.atp.typeArgs(1)) - vprintln("check lin " + a + " andThen " + b) -*/ - vprintln("check lin " + a + " andThen " + b) - if (!(v1 <:< u0)) + ann reduceLeft { (a, b) => + val (u0,v0) = annTypes(a) + val (u1,v1) = annTypes(b) + // vprintln("check lin " + a + " andThen " + b) + + if (v1 <:< u0) + newCpsParamsMarker(u1, v0) + else throw new TypeError("illegal answer type modification: " + a + " andThen " + b) - // TODO: improve error message (but it is not very common) - AnnotationInfo(appliedType(MarkerCPSTypes.tpe, List(u1,v0)),Nil,Nil) } } - + // anf transform - + def getExternalAnswerTypeAnn(tp: Type) = { - tp.annotations.find(a => a.atp.typeSymbol == MarkerCPSTypes) match { - case Some(AnnotationInfo(atp, _, _)) => - val atp0::atp1::Nil = atp.normalize.typeArgs - Some((atp0, atp1)) - case None => - if (tp.hasAnnotation(MarkerCPSAdaptPlus)) - global.warning("trying to instantiate type " + tp + " to unknown cps type") - None - } - } - - def getAnswerTypeAnn(tp: Type) = { - tp.annotations.find(a => a.atp.typeSymbol == MarkerCPSTypes) match { - case Some(AnnotationInfo(atp, _, _)) => - if (!tp.hasAnnotation(MarkerCPSAdaptPlus)) {//&& !tp.hasAnnotation(MarkerCPSAdaptMinus)) - val atp0::atp1::Nil = atp.normalize.typeArgs - Some((atp0, atp1)) - } else - None - case None => None + cpsParamTypes(tp) orElse { + if (hasPlusMarker(tp)) + global.warning("trying to instantiate type " + tp + " to unknown cps type") + None } } + + def getAnswerTypeAnn(tp: Type): Option[(Type, Type)] = + cpsParamTypes(tp) filterNot (_ => hasPlusMarker(tp)) - def hasAnswerTypeAnn(tp: Type) = { - tp.hasAnnotation(MarkerCPSTypes) && !tp.hasAnnotation(MarkerCPSAdaptPlus) /*&& - !tp.hasAnnotation(MarkerCPSAdaptMinus)*/ - } - - def hasSynthAnn(tp: Type) = { - tp.annotations.exists(a => a.atp.typeSymbol == MarkerCPSSynth) - } - + def hasAnswerTypeAnn(tp: Type) = + hasCpsParamTypes(tp) && !hasPlusMarker(tp) + def updateSynthFlag(tree: Tree) = { // remove annotations if *we* added them (@synth present) - if (hasSynthAnn(tree.tpe)) { + if (hasSynthMarker(tree.tpe)) { log("removing annotation from " + tree) - tree.setType(removeAllCPSAnnotations(tree.tpe)) + tree modifyType removeAllCPSAnnotations } else tree } - + type CPSInfo = Option[(Type,Type)] def linearize(a: CPSInfo, b: CPSInfo)(implicit unit: CompilationUnit, pos: Position): CPSInfo = { @@ -124,7 +118,4 @@ trait CPSUtils { case _ => None } } - - // cps transform - -} \ No newline at end of file +} diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala index 77e7ef71d33..817bde860d6 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala @@ -301,7 +301,7 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with log("cps type conversion (has: " + cpsA + "/" + spc + "/" + expr.tpe + ")") log("cps type conversion (expected: " + cpsR.get + "): " + expr) - if (!expr.tpe.hasAnnotation(MarkerCPSAdaptPlus)) + if (!hasPlusMarker(expr.tpe)) unit.warning(tree.pos, "expression " + tree + " is cps-transformed unexpectedly") try { @@ -331,9 +331,9 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with } else { // all is well - if (expr.tpe.hasAnnotation(MarkerCPSAdaptPlus)) { + if (hasPlusMarker(expr.tpe)) { unit.warning(tree.pos, "expression " + expr + " of type " + expr.tpe + " is not expected to have a cps type") - expr.setType(removeAllCPSAnnotations(expr.tpe)) + expr modifyType removeAllCPSAnnotations } // TODO: sanity check that types agree