diff options
Diffstat (limited to 'src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala')
-rw-r--r-- | src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala | 520 |
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 - } - - - } - } -} |