summaryrefslogtreecommitdiff
path: root/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala')
-rw-r--r--src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala520
1 files changed, 0 insertions, 520 deletions
diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala
deleted file mode 100644
index 3963447de3..0000000000
--- a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala
+++ /dev/null
@@ -1,520 +0,0 @@
-// $Id$
-
-package scala.tools.selectivecps
-
-import scala.tools.nsc.{ Global, Mode }
-import scala.tools.nsc.MissingRequirementError
-
-abstract class CPSAnnotationChecker extends CPSUtils {
- val global: Global
- import global._
- import analyzer.{AnalyzerPlugin, Typer}
- import definitions._
-
- //override val verbose = true
- @inline override final def vprintln(x: =>Any): Unit = if (verbose) println(x)
-
- /**
- * Checks whether @cps annotations conform
- */
- object checker extends AnnotationChecker {
- private[CPSAnnotationChecker] def addPlusMarker(tp: Type) = tp withAnnotation newPlusMarker()
- private[CPSAnnotationChecker] def addMinusMarker(tp: Type) = tp withAnnotation newMinusMarker()
-
- private[CPSAnnotationChecker] def cleanPlus(tp: Type) =
- removeAttribs(tp, MarkerCPSAdaptPlus, MarkerCPSTypes)
- private[CPSAnnotationChecker] 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
-
- vprintln("check annotations: " + tpe1 + " <:< " + tpe2)
-
- // Nothing is least element, but Any is not the greatest
- if (tpe1.typeSymbol eq NothingClass)
- return true
-
- val annots1 = cpsParamAnnotation(tpe1)
- val annots2 = cpsParamAnnotation(tpe2)
-
- // @plus and @minus should only occur at the left, and never together
- // TODO: insert check
-
- // @minus @cps is the same as no annotations
- if (hasMinusMarker(tpe1))
- return annots2.isEmpty
-
- // to handle answer type modification, we must make @plus <:< @cps
- 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
-
- // Need to handle uninstantiated type vars specially:
-
- // g map (x => x) with expected type List[Int] @cps
- // results in comparison ?That <:< List[Int] @cps
-
- // Instantiating ?That to an annotated type would fail during
- // transformation.
-
- // Instead we force-compare tpe1 <:< tpe2.withoutAnnotations
- // to trigger instantiation of the TypeVar to the base type
-
- // This is a bit unorthodox (we're only supposed to look at
- // annotations here) but seems to work.
-
- if (!annots2.isEmpty && !tpe1.isGround)
- return tpe1 <:< tpe2.withoutAnnotations
-
- false
- }
-
- /** 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 = cpsParamAnnotation(tpe)
- val annots2 = ts flatMap cpsParamAnnotation
-
- if (annots2.nonEmpty) {
- val cpsLub = newMarker(global.lub(annots1:::annots2 map (_.atp)))
- val tpe1 = if (annots1.nonEmpty) removeAttribs(tpe, MarkerCPSTypes) else tpe
- tpe1.withAnnotation(cpsLub)
- }
- 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 = newCpsParamsMarker(NothingTpe, AnyTpe)
- if (isFunctionType(tparams.head.owner.tpe_*) || isPartialFunctionType(tparams.head.owner.tpe_*)) {
- vprintln("function bound: " + tparams.head.owner.tpe + "/"+bounds+"/"+targs)
- if (hasCpsParamTypes(targs.last))
- bounds.reverse match {
- 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) {
- vprintln("byname bound: " + tparams.head.owner.tpe + "/"+bounds+"/"+targs)
- val TypeBounds(lo, hi) = bounds.head
- if (hasCpsParamTypes(targs.head) && !hasCpsParamTypes(hi))
- TypeBounds(lo, hi withAnnotation anyAtCPS) :: Nil
- else bounds
- } else
- bounds
- }
- }
-
- object plugin extends AnalyzerPlugin {
-
- import checker._
-
- override def canAdaptAnnotations(tree: Tree, typer: Typer, mode: Mode, pt: Type): Boolean = {
- if (!cpsEnabled) return false
- vprintln("can adapt annotations? " + tree + " / " + tree.tpe + " / " + mode + " / " + pt)
-
- val annots1 = cpsParamAnnotation(tree.tpe)
- val annots2 = cpsParamAnnotation(pt)
-
- if (mode.inPatternMode) {
- //println("can adapt pattern annotations? " + tree + " / " + tree.tpe + " / " + Integer.toHexString(mode) + " / " + pt)
- if (!annots1.isEmpty) {
- return true
- }
- }
-
-/*
- // not precise enough -- still relying on addAnnotations to remove things from ValDef symbols
- if (mode.inAllModes(TYPEmode | BYVALmode)) {
- if (!annots1.isEmpty) {
- return true
- }
- }
-*/
-
-/*
- this interferes with overloading resolution
- if (mode.inByValMode && tree.tpe <:< pt) {
- vprintln("already compatible, can't adapt further")
- return false
- }
-*/
- if (mode.inExprMode) {
- if ((annots1 corresponds annots2)(_.atp <:< _.atp)) {
- vprintln("already same, can't adapt further")
- false
- } else if (annots1.isEmpty && !annots2.isEmpty && !mode.inByValMode) {
- //println("can adapt annotations? " + tree + " / " + tree.tpe + " / " + Integer.toHexString(mode) + " / " + pt)
- if (!hasPlusMarker(tree.tpe)) {
- // val base = tree.tpe <:< removeAllCPSAnnotations(pt)
- // val known = global.analyzer.isFullyDefined(pt)
- // println(same + "/" + base + "/" + known)
- //val same = annots2 forall { case AnnotationInfo(atp: TypeRef, _, _) => atp.typeArgs(0) =:= atp.typeArgs(1) }
- // TBD: use same or not?
- //if (same) {
- vprintln("yes we can!! (unit)")
- true
- //}
- } else false
- } else if (!hasPlusMarker(tree.tpe) && annots1.isEmpty && !annots2.isEmpty && typer.context.inReturnExpr) {
- vprintln("checking enclosing method's result type without annotations")
- tree.tpe <:< pt.withoutAnnotations
- } else if (!hasMinusMarker(tree.tpe) && !annots1.isEmpty && mode.inByValMode) {
- val optCpsTypes: Option[(Type, Type)] = cpsParamTypes(tree.tpe)
- val optExpectedCpsTypes: Option[(Type, Type)] = cpsParamTypes(pt)
- if (optCpsTypes.isEmpty || optExpectedCpsTypes.isEmpty) {
- vprintln("yes we can!! (byval)")
- true
- } else { // check cps param types
- val cpsTpes = optCpsTypes.get
- val cpsPts = optExpectedCpsTypes.get
- // class cpsParam[-B,+C], therefore:
- cpsPts._1 <:< cpsTpes._1 && cpsTpes._2 <:< cpsPts._2
- }
- } else false
- } else false
- }
-
- override def adaptAnnotations(tree: Tree, typer: Typer, mode: Mode, pt: Type): Tree = {
- if (!cpsEnabled) return tree
-
- vprintln("adapt annotations " + tree + " / " + tree.tpe + " / " + mode + " / " + pt)
-
- val annotsTree = cpsParamAnnotation(tree.tpe)
- val annotsExpected = cpsParamAnnotation(pt)
- def isMissingExpectedAnnots = annotsTree.isEmpty && annotsExpected.nonEmpty
-
- // not sure I rephrased this comment correctly:
- // replacing `mode.inPatternMode` in the condition below by `mode.inPatternMode || mode.inAllModes(TYPEmode | BYVALmode)`
- // doesn't work correctly -- still relying on addAnnotations to remove things from ValDef symbols
- if (mode.inPatternMode && annotsTree.nonEmpty) tree modifyType removeAllCPSAnnotations
- else if (mode.typingExprNotValue && !hasPlusMarker(tree.tpe) && isMissingExpectedAnnots) { // shiftUnit
- // 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)
-
- // CAVEAT:
- // for monomorphic answer types we want to have @plus @cps (for better checking)
- // for answer type modification we want to have only @plus (because actual answer type may differ from pt)
-
- val res = tree modifyType (_ withAnnotations newPlusMarker() :: annotsExpected) // needed for #1807
- vprintln("adapted annotations (not by val) of " + tree + " to " + res.tpe)
- res
- } else if (mode.typingExprByValue && !hasMinusMarker(tree.tpe) && annotsTree.nonEmpty) { // dropping annotation
- // add a marker annotation that will make tree.tpe behave as pt, subtyping wise
- // tree will look like having no annotation
- val res = tree modifyType addMinusMarker
- vprintln("adapted annotations (by val) of " + tree + " to " + res.tpe)
- res
- } else if (typer.context.inReturnExpr && !hasPlusMarker(tree.tpe) && isMissingExpectedAnnots) {
- // add a marker annotation that will make tree.tpe behave as pt, subtyping wise
- // tree will look like having any possible annotation
-
- // note 1: we are only adding a plus marker if the method's result type is a cps type
- // (annotsExpected.nonEmpty == cpsParamAnnotation(pt).nonEmpty)
- // note 2: we are not adding the expected cps annotations, since they will be added
- // by adaptTypeOfReturn (see below).
- val res = tree modifyType (_ withAnnotation newPlusMarker())
- vprintln("adapted annotations (return) of " + tree + " to " + res.tpe)
- res
- } else tree
- }
-
- /** Returns an adapted type for a return expression if the method's result type (pt) is a CPS type.
- * Otherwise, it returns the `default` type (`typedReturn` passes `NothingTpe`).
- *
- * A return expression in a method that has a CPS result type is an error unless the return
- * is in tail position. Therefore, we are making sure that only the types of return expressions
- * are adapted which will either be removed, or lead to an error.
- */
- override def pluginsTypedReturn(default: Type, typer: Typer, tree: Return, pt: Type): Type = {
- val expr = tree.expr
- // only adapt if method's result type (pt) is cps type
- val annots = cpsParamAnnotation(pt)
- if (annots.nonEmpty) {
- // return type of `expr` without plus marker, but only if it doesn't have other cps annots
- if (hasPlusMarker(expr.tpe) && !hasCpsParamTypes(expr.tpe))
- expr.setType(removeAttribs(expr.tpe, MarkerCPSAdaptPlus))
- expr.tpe
- } else default
- }
-
- def updateAttributesFromChildren(tpe: Type, childAnnots: List[AnnotationInfo], byName: List[Tree]): Type = {
- tpe match {
- // Would need to push annots into each alternative of overloaded type
- // But we can't, since alternatives aren't types but symbols, which we
- // can't change (we'd be affecting symbols globally)
- /*
- case OverloadedType(pre, alts) =>
- OverloadedType(pre, alts.map((sym: Symbol) => updateAttributes(pre.memberType(sym), annots)))
- */
- case OverloadedType(pre, alts) => tpe //reconstruct correct annotations later
- case MethodType(params, restpe) => tpe
- case PolyType(params, restpe) => tpe
- case _ =>
- assert(childAnnots forall (_ matches MarkerCPSTypes), childAnnots)
- /*
- [] + [] = []
- plus + [] = plus
- cps + [] = cps
- plus cps + [] = plus cps
- minus cps + [] = minus cps
- synth cps + [] = synth cps // <- synth on left - does it happen?
-
- [] + cps = cps
- plus + cps = synth cps
- cps + cps = cps! <- lin
- plus cps + cps = synth cps! <- unify
- minus cps + cps = minus cps! <- lin
- synth cps + cps = synth cps! <- unify
- */
-
- 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) return {
- if (plus) { // @plus or @plus @cps
- byName foreach (_ modifyType cleanPlus)
- addPlusMarker(tpe)
- }
- else tpe
- }
-
- val annots1 = cpsParamAnnotation(tpe)
-
- if (annots1.isEmpty) { // nothing or @plus
- cleanPlusWith(tpe)(newSynthMarker(), linearize(childAnnots))
- }
- else {
- val annot1 = single(annots1)
- if (plus) { // @plus @cps
- val annot2 = linearize(childAnnots)
-
- if (annot2.atp <:< annot1.atp) {
- try cleanPlusWith(tpe)(newSynthMarker(), annot2)
- finally byName foreach (_ modifyType cleanPlus)
- }
- 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)
- cleanPlusWith(tpe)(annot2)
- else
- throw new TypeError(annot2 + " is not a subtype of " + annot1)
- }
- 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
-
- for ((a,tp) <- args.zip(formals ::: List.fill(overshoot)(NoType))) yield {
- tp match {
- case TypeRef(_, ByNameParamClass, List(elemtp)) =>
- Nil // TODO: check conformance??
- case _ =>
- List(a)
- }
- }
- }
-
-
- def transStms(stms: List[Tree]): List[Tree] = stms match {
- case ValDef(mods, name, tpt, rhs)::xs =>
- rhs::transStms(xs)
- case Assign(lhs, rhs)::xs =>
- rhs::transStms(xs)
- case x::xs =>
- x::transStms(xs)
- case Nil =>
- Nil
- }
-
- def single(xs: List[AnnotationInfo]) = xs match {
- case List(x) => x
- case _ =>
- global.globalError("not a single cps annotation: " + xs)
- xs(0)
- }
-
- def emptyOrSingleList(xs: List[AnnotationInfo]) = if (xs.isEmpty) Nil else List(single(xs))
-
- def transChildrenInOrder(tree: Tree, tpe: Type, childTrees: List[Tree], byName: List[Tree]) = {
- def inspect(t: Tree): List[AnnotationInfo] = {
- if (t.tpe eq null) Nil else {
- val extra: List[AnnotationInfo] = t.tpe match {
- case _: MethodType | _: PolyType | _: OverloadedType =>
- // method types, poly types and overloaded types do not obtain cps annotions by propagation
- // need to reconstruct transitively from their children.
- t match {
- case Select(qual, name) => inspect(qual)
- case Apply(fun, args) => (fun::(transArgList(fun,args).flatten)) flatMap inspect
- case TypeApply(fun, args) => (fun::(transArgList(fun,args).flatten)) flatMap inspect
- case _ => Nil
- }
- case _ => Nil
- }
-
- val types = cpsParamAnnotation(t.tpe)
- // TODO: check that it has been adapted and if so correctly
- extra ++ emptyOrSingleList(types)
- }
- }
- val children = childTrees flatMap inspect
-
- val newtpe = updateAttributesFromChildren(tpe, children, byName)
-
- if (!newtpe.annotations.isEmpty)
- vprintln("[checker] inferred " + tree + " / " + tpe + " ===> "+ newtpe)
-
- newtpe
- }
-
- /** Modify the type that has thus far been inferred
- * for a tree. All this should do is add annotations. */
-
- override def pluginsTyped(tpe: Type, typer: Typer, tree: Tree, mode: Mode, pt: Type): Type = {
- if (!cpsEnabled) {
- val report = try hasCpsParamTypes(tpe) catch { case _: MissingRequirementError => false }
- if (report)
- 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)
-
- tree match {
-
- case Apply(fun @ Select(qual, name), args) if fun.isTyped =>
-
- // HACK: With overloaded methods, fun will never get annotated. This is because
- // the 'overloaded' type gets annotated, but not the alternatives (among which
- // fun's type is chosen)
-
- vprintln("[checker] checking select apply " + tree + "/" + tpe)
-
- transChildrenInOrder(tree, tpe, qual::(transArgList(fun, args).flatten), Nil)
-
- case Apply(TypeApply(fun @ Select(qual, name), targs), args) if fun.isTyped => // not trigge
-
- vprintln("[checker] checking select apply type-apply " + tree + "/" + tpe)
-
- transChildrenInOrder(tree, tpe, qual::(transArgList(fun, args).flatten), Nil)
-
- case TypeApply(fun @ Select(qual, name), args) if fun.isTyped =>
- def stripNullaryMethodType(tp: Type) = tp match { case NullaryMethodType(restpe) => restpe case tp => tp }
- vprintln("[checker] checking select type-apply " + tree + "/" + tpe)
-
- transChildrenInOrder(tree, stripNullaryMethodType(tpe), List(qual, fun), Nil)
-
- case Apply(fun, args) if fun.isTyped =>
-
- vprintln("[checker] checking unknown apply " + tree + "/" + tpe)
-
- transChildrenInOrder(tree, tpe, fun::(transArgList(fun, args).flatten), Nil)
-
- case TypeApply(fun, args) =>
-
- vprintln("[checker] checking unknown type apply " + tree + "/" + tpe)
-
- transChildrenInOrder(tree, tpe, List(fun), Nil)
-
- case Select(qual, name) if qual.isTyped =>
-
- vprintln("[checker] checking select " + tree + "/" + tpe)
-
- // straightforward way is problematic (see select.scala and Test2.scala)
- // transChildrenInOrder(tree, tpe, List(qual), Nil)
-
- // the problem is that qual may be of type OverloadedType (or MethodType) and
- // we cannot safely annotate these. so we just ignore these cases and
- // clean up later in the Apply/TypeApply trees.
-
- 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
- // adapt and there is no Apply/TypeApply created)
- tpe match {
- case NullaryMethodType(restpe) =>
- //println("yep: " + restpe + "," + restpe.getClass)
- transChildrenInOrder(tree, restpe, List(qual), Nil)
- case _ : PolyType => tpe
- case _ : MethodType => tpe
- case _ : OverloadedType => tpe
- case _ =>
- transChildrenInOrder(tree, tpe, List(qual), Nil)
- }
- } else
- tpe
-
- case If(cond, thenp, elsep) =>
- transChildrenInOrder(tree, tpe, List(cond), List(thenp, elsep))
-
- case Match(select, cases) =>
- transChildrenInOrder(tree, tpe, List(select), cases:::(cases map { case CaseDef(_, _, body) => body }))
-
- case Try(block, catches, finalizer) =>
- val tpe1 = transChildrenInOrder(tree, tpe, Nil, block::catches:::(catches map { case CaseDef(_, _, body) => body }))
-
- val annots = cpsParamAnnotation(tpe1)
- if (annots.nonEmpty) {
- val ann = single(annots)
- 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
- reporter.error(tree.pos, "try/catch blocks that use continuations cannot have finalizers")
- }
- tpe1
-
- case Block(stms, expr) =>
- // if any stm has annotation, so does block
- transChildrenInOrder(tree, tpe, transStms(stms), List(expr))
-
- case ValDef(mods, name, tpt, rhs) =>
- vprintln("[checker] checking valdef " + name + "/"+tpe+"/"+tpt+"/"+tree.symbol.tpe)
- // ValDef symbols must *not* have annotations!
- // lazy vals are currently not supported
- // but if we erase here all annotations, compiler will complain only
- // when generating bytecode.
- // This way lazy vals will be reported as unsupported feature later rather than weird type error.
- if (hasAnswerTypeAnn(tree.symbol.info) && !mods.isLazy) { // is it okay to modify sym here?
- vprintln("removing annotation from sym " + tree.symbol + "/" + tree.symbol.tpe + "/" + tpt)
- tpt modifyType removeAllCPSAnnotations
- tree.symbol modifyInfo removeAllCPSAnnotations
- }
- tpe
-
- case _ =>
- tpe
- }
-
-
- }
- }
-}