Skip to content

Commit

Permalink
restore typedMatchAnonFun in all its glory
Browse files Browse the repository at this point in the history
detect partialfunction in cpsannotationchecker
emit apply/isDefinedAt if PF has @cps targs (applyOrElse can't be typed)
further hacky improvements to selective anf
better try/catch support in selective cps using freshly minted anonfun match
make virtpatmat resilient to scaladoc (after uncurry, don't translate matches
 TODO: factor out translation all together so presentation compiler/scaladoc can skip it)
  • Loading branch information
adriaanm committed Apr 14, 2012
1 parent 1a6e712 commit 2848373
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 63 deletions.
7 changes: 7 additions & 0 deletions src/compiler/scala/reflect/internal/Definitions.scala
Expand Up @@ -400,6 +400,8 @@ trait Definitions extends reflect.api.StandardDefinitions {
lazy val EqualsPatternClass = specialPolyClass(tpnme.EQUALS_PATTERN_NAME, 0L)(_ => AnyClass.tpe)
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
Expand Down Expand Up @@ -655,6 +657,11 @@ trait Definitions extends reflect.api.StandardDefinitions {
false
}

def isPartialFunctionType(tp: Type): Boolean = {
val sym = tp.typeSymbol
(sym eq PartialFunctionClass) || (sym eq AbstractPartialFunctionClass)
}

def isSeqType(tp: Type) = elementType(SeqClass, tp.normalize) != NoType

def elementType(container: Symbol, tp: Type): Type = tp match {
Expand Down
6 changes: 4 additions & 2 deletions src/compiler/scala/tools/nsc/transform/UnCurry.scala
Expand Up @@ -446,8 +446,10 @@ abstract class UnCurry extends InfoTransform
}

val members =
if (isPartial) List(applyOrElseMethodDef, isDefinedAtMethodDef)
else List(applyMethodDef)
if (isPartial) {
assert(!opt.virtPatmat, "PartialFunction should have been synthesized during typer "+ fun);
List(applyOrElseMethodDef, isDefinedAtMethodDef)
} else List(applyMethodDef)

// println("MEMBERS "+ members)
val res = localTyper.typedPos(fun.pos) {
Expand Down
Expand Up @@ -135,7 +135,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
// 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.uncurryPhase.id, phase)
if(phase.id >= currentRun.uncurryPhase.id) debugwarn("running translateMatch at "+ phase +" on "+ scrut +" match "+ cases)

val scrutSym = freshSym(scrut.pos, pureType(scrutType)) setFlag SYNTH_CASE
// pt = Any* occurs when compiling test/files/pos/annotDepMethType.scala with -Xexperimental
Expand Down Expand Up @@ -1107,7 +1107,9 @@ class Foo(x: Other) { x._1 } // no error in this order

// 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)
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 = 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)
Expand Down
73 changes: 46 additions & 27 deletions src/compiler/scala/tools/nsc/typechecker/Typers.scala
Expand Up @@ -2193,6 +2193,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {

def adaptCase(cdef: CaseDef, mode: Int, tpe: Type): CaseDef = deriveCaseDef(cdef)(adapt(_, mode, tpe))

// takes untyped sub-trees of a match and type checks them
def typedMatch(selector0: Tree, cases: List[CaseDef], mode: Int, resTp: Type) = {
val (selector, doTranslation) = selector0 match {
case Annotated(Ident(nme.synthSwitch), selector) => (selector, false)
Expand All @@ -2210,6 +2211,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
(selector1, selectorTp, casesAdapted, ownType, doTranslation)
}

// 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)
Expand Down Expand Up @@ -2271,11 +2273,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {

// need to duplicate the cases before typing them to generate the apply method, or the symbols will be all messed up
val casesTrue = if (isPartial) cases map (c => deriveCaseDef(c)(x => TRUE_typed).duplicate) else Nil
def parentsPartial(targs: List[Type]) = List(appliedType(AbstractPartialFunctionClass.typeConstructor, targs), SerializableClass.tpe)

def applyMethod = {
// rig the show so we can get started typing the method body -- later we'll correct the infos...
anonClass setInfo ClassInfoType(List(ObjectClass.tpe, pt, SerializableClass.tpe), newScope, anonClass)
val methodSym = anonClass.newMethod(nme.apply, tree.pos, FINAL)
val methodSym = anonClass.newMethod(nme.apply, tree.pos, if(isPartial) (FINAL | OVERRIDE) else FINAL)
val (paramSyms, selector) = mkParams(methodSym)

if (selector eq EmptyTree) EmptyTree
Expand All @@ -2288,7 +2291,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
val (selector1, selectorTp, casesAdapted, resTp, doTranslation) = methodBodyTyper.typedMatch(selector, cases, mode, ptRes)

val methFormals = paramSyms map (_.tpe)
val parents = List(abstractFunctionType(methFormals, resTp), SerializableClass.tpe)
val parents =
if (isPartial) parentsPartial(List(methFormals.head, resTp))
else List(abstractFunctionType(methFormals, resTp), SerializableClass.tpe)

anonClass setInfo ClassInfoType(parents, newScope, anonClass)
methodSym setInfoAndEnter MethodType(paramSyms, resTp)
Expand All @@ -2301,9 +2306,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
def applyOrElseMethodDef = {
// rig the show so we can get started typing the method body -- later we'll correct the infos...
// targs were type arguments for PartialFunction, so we know they will work for AbstractPartialFunction as well
def parents(targs: List[Type]) = List(appliedType(AbstractPartialFunctionClass.typeConstructor, targs), SerializableClass.tpe)

anonClass setInfo ClassInfoType(parents(targs), newScope, anonClass)
anonClass setInfo ClassInfoType(parentsPartial(targs), newScope, anonClass)
val methodSym = anonClass.newMethod(nme.applyOrElse, tree.pos, FINAL | OVERRIDE)

// create the parameter that corresponds to the function's parameter
Expand All @@ -2325,7 +2328,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {

val (selector1, selectorTp, casesAdapted, resTp, doTranslation) = methodBodyTyper.typedMatch(selector, cases, mode, ptRes)

anonClass setInfo ClassInfoType(parents(List(argTp, resTp)), newScope, anonClass)
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)

Expand Down Expand Up @@ -2354,7 +2357,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
}
}

val members = if (!isPartial) List(applyMethod) else List(applyOrElseMethodDef, isDefinedAtMethod)
val members = if (isPartial) {
if ((MarkerCPSTypes ne NoSymbol) && (targs exists (_ hasAnnotation MarkerCPSTypes))) List(applyMethod, isDefinedAtMethod)
else List(applyOrElseMethodDef, isDefinedAtMethod)
} else List(applyMethod)

if (members.head eq EmptyTree) setError(tree)
else typed(Block(List(ClassDef(anonClass, NoMods, List(List()), List(List()), members, tree.pos)), New(anonClass.tpe)), mode, pt)
}
Expand Down Expand Up @@ -2409,21 +2416,31 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
}
}

val vparamSyms = fun.vparams map { vparam =>
enterSym(context, vparam)
if (context.retyping) context.scope enter vparam.symbol
vparam.symbol
fun.body match {
// later phase indicates scaladoc is calling (where shit is messed up, I tell you)
// -- so fall back to old patmat, which is more forgiving
case Match(sel, cases) if opt.virtPatmat && (phase.id < currentRun.uncurryPhase.id) =>
// go to outer context -- must discard the context that was created for the Function since we're discarding the function
// thus, its symbol, which serves as the current context.owner, is not the right owner
// you won't know you're using the wrong owner until lambda lift crashes (unless you know better than to use the wrong owner)
newTyper(context.outer).typedMatchAnonFun(fun, cases, mode, pt, Some((fun.vparams, sel)))
case _ =>
val vparamSyms = fun.vparams map { vparam =>
enterSym(context, vparam)
if (context.retyping) context.scope enter vparam.symbol
vparam.symbol
}
val vparams = fun.vparams mapConserve (typedValDef)
// for (vparam <- vparams) {
// checkNoEscaping.locals(context.scope, WildcardType, vparam.tpt); ()
// }
val formals = vparamSyms map (_.tpe)
val body1 = typed(fun.body, respt)
val restpe = packedType(body1, fun.symbol).deconst.resultType
val funtpe = typeRef(clazz.tpe.prefix, clazz, formals :+ restpe)
// body = checkNoEscaping.locals(context.scope, restpe, body)
treeCopy.Function(fun, vparams, body1).setType(funtpe)
}
val vparams = fun.vparams mapConserve (typedValDef)
// for (vparam <- vparams) {
// checkNoEscaping.locals(context.scope, WildcardType, vparam.tpt); ()
// }
val formals = vparamSyms map (_.tpe)
val body1 = typed(fun.body, respt)
val restpe = packedType(body1, fun.symbol).deconst.resultType
val funtpe = typeRef(clazz.tpe.prefix, clazz, formals :+ restpe)
// body = checkNoEscaping.locals(context.scope, restpe, body)
treeCopy.Function(fun, vparams, body1).setType(funtpe)
}
}

Expand Down Expand Up @@ -3795,9 +3812,14 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
}
}

// translation only happens when (selector != EmptyTree) && !isPastTyper && opt.virtPatmat
def typedTranslatedMatch(tree: Tree, selector: Tree, cases: List[CaseDef]): Tree = {
if (selector == EmptyTree) {
if (opt.virtPatmat && (phase.id < currentRun.uncurryPhase.id)) {
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 typedMatchAnonFun(tree, cases, mode, pt)
} 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) {
Expand All @@ -3808,17 +3830,14 @@ 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 (!((phase.id < currentRun.uncurryPhase.id) && opt.virtPatmat)) {
} 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))
}
treeCopy.Match(tree, selector1, cases1) setType owntype
} else {
val (selector1, selectorTp, casesAdapted, ownType, doTranslation) = typedMatch(selector, cases, mode, pt)
typed(translatedMatch(selector1, selectorTp, casesAdapted, ownType, doTranslation), mode, pt)
}
}

Expand Down
Expand Up @@ -94,8 +94,7 @@ abstract class CPSAnnotationChecker extends CPSUtils {
if (!cpsEnabled) return bounds

val anyAtCPS = newCpsParamsMarker(NothingClass.tpe, AnyClass.tpe)

if (isFunctionType(tparams.head.owner.tpe) || tparams.head.owner == PartialFunctionClass) {
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 {
Expand Down
Expand Up @@ -457,11 +457,13 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with
val (anfStats, anfExpr) = rec(stms, cpsA, List())
// println("\nanf-block:\n"+ ((stms :+ expr) mkString ("{", "\n", "}")) +"\nBECAME\n"+ ((anfStats :+ anfExpr) mkString ("{", "\n", "}")))

if (anfStats.nonEmpty && (anfStats forall gen.hasSynthCaseSymbol)) {
// SUPER UGLY HACK: handle virtpatmat-style matches, whose labels have already been turned into DefDefs
if (anfStats.nonEmpty && (anfStats forall (t => !t.isDef || gen.hasSynthCaseSymbol(t)))) {
val (prologue, rest) = (anfStats :+ anfExpr) span (s => !s.isInstanceOf[DefDef]) // find first case
// val (defs, calls) = rest partition (_.isInstanceOf[DefDef])
if (rest nonEmpty){
val stats = prologue ++ rest.reverse // ++ calls
// the filter drops the ()'s emitted when transValue encountered a LabelDef
val stats = prologue ++ (rest filter (_.isInstanceOf[DefDef])).reverse // ++ calls
// println("REVERSED "+ (stats mkString ("{", "\n", "}")))
(stats, localTyper.typed{Apply(Ident(rest.head.symbol), List())}) // call first label to kick-start the match
} else (anfStats, anfExpr)
Expand Down
Expand Up @@ -190,32 +190,29 @@ abstract class SelectiveCPSTransform extends PluginComponent with

val targettp = transformCPSType(tree.tpe)

// val expr2 = if (catches.nonEmpty) {
val pos = catches.head.pos
val argSym = currentOwner.newValueParameter(cpsNames.ex, pos).setInfo(ThrowableClass.tpe)
val rhs = Match(Ident(argSym), catches1)
val fun = Function(List(ValDef(argSym)), rhs)
val funSym = currentOwner.newValueParameter(cpsNames.catches, pos).setInfo(appliedType(PartialFunctionClass.tpe, List(ThrowableClass.tpe, targettp)))
val funDef = localTyper.typed(atPos(pos) { ValDef(funSym, fun) })
val expr2 = localTyper.typed(atPos(pos) { Apply(Select(expr1, expr1.tpe.member(cpsNames.flatMapCatch)), List(Ident(funSym))) })

argSym.owner = fun.symbol
rhs.changeOwner(currentOwner -> fun.symbol)

val exSym = currentOwner.newValueParameter(cpsNames.ex, pos).setInfo(ThrowableClass.tpe)

import CODE._
// generate a case that is supported directly by the back-end
val catchIfDefined = CaseDef(
Bind(exSym, Ident(nme.WILDCARD)),
EmptyTree,
IF ((REF(funSym) DOT nme.isDefinedAt)(REF(exSym))) THEN (REF(funSym) APPLY (REF(exSym))) ELSE Throw(REF(exSym))
)

val catch2 = localTyper.typedCases(List(catchIfDefined), ThrowableClass.tpe, targettp)
//typedCases(tree, catches, ThrowableClass.tpe, pt)

localTyper.typed(Block(List(funDef), treeCopy.Try(tree, treeCopy.Block(block1, stms, expr2), catch2, finalizer1)))
val pos = catches.head.pos
val funSym = currentOwner.newValueParameter(cpsNames.catches, pos).setInfo(appliedType(PartialFunctionClass.tpe, List(ThrowableClass.tpe, targettp)))
val funDef = localTyper.typed(atPos(pos) {
ValDef(funSym, Match(EmptyTree, catches1))
})
val expr2 = localTyper.typed(atPos(pos) {
Apply(Select(expr1, expr1.tpe.member(cpsNames.flatMapCatch)), List(Ident(funSym)))
})

val exSym = currentOwner.newValueParameter(cpsNames.ex, pos).setInfo(ThrowableClass.tpe)

import CODE._
// generate a case that is supported directly by the back-end
val catchIfDefined = CaseDef(
Bind(exSym, Ident(nme.WILDCARD)),
EmptyTree,
IF ((REF(funSym) DOT nme.isDefinedAt)(REF(exSym))) THEN (REF(funSym) APPLY (REF(exSym))) ELSE Throw(REF(exSym))
)

val catch2 = localTyper.typedCases(List(catchIfDefined), ThrowableClass.tpe, targettp)
//typedCases(tree, catches, ThrowableClass.tpe, pt)

localTyper.typed(Block(List(funDef), treeCopy.Try(tree, treeCopy.Block(block1, stms, expr2), catch2, finalizer1)))


/*
Expand Down
4 changes: 2 additions & 2 deletions test/files/neg/t4515.check
@@ -1,6 +1,6 @@
t4515.scala:37: error: type mismatch;
found : _0(in value $anonfun) where type _0(in value $anonfun)
required: (some other)_0(in value $anonfun)
found : _0(in method apply) where type _0(in method apply)
required: (some other)_0(in method apply)
handler.onEvent(target, ctx.getEvent, node, ctx)
^
one error found

0 comments on commit 2848373

Please sign in to comment.