summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker/Typers.scala
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@epfl.ch>2012-03-14 11:47:59 +0100
committerAdriaan Moors <adriaan.moors@epfl.ch>2012-03-20 19:54:03 +0100
commitb046a6e3316df8b27ac31e71da1a139c800ccce7 (patch)
treedfbed2ea5620d100a62bbdf39b93488628e8cbb3 /src/compiler/scala/tools/nsc/typechecker/Typers.scala
parent2d3b309a36757545901b99da1b77698749fcc0c5 (diff)
downloadscala-b046a6e3316df8b27ac31e71da1a139c800ccce7.tar.gz
scala-b046a6e3316df8b27ac31e71da1a139c800ccce7.tar.bz2
scala-b046a6e3316df8b27ac31e71da1a139c800ccce7.zip
[vpm] typer synths Function{} for empty-sel match
typedMatchAnonFun is called from typedFunction when the function's body is a match this is work-in-progres: the compiler currently won't bootstrap under -Yvirtpatmat see also the pending test make sure to use the right context in typeFunction when the body is a Match when typer is set up for type checking a Function, the current owner is the symbol for the function, but we'll type check a Block(List(ClassDef(cd)), New(cd)) when the function is a match, and the function symbol is nowhere to be found, so go to outer context in patmatvirt: - simplified default case gen (no need for a Casegen instance) - using CASE | SYNTHETIC to detect generated matches (for switches) and avoid typing them endlessly more uniform, and necessary for new-style anon Function class instance gen for matches
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker/Typers.scala')
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala210
1 files changed, 165 insertions, 45 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 0dd4b37131..506e347828 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -2143,6 +2143,137 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
newTyper(context.makeNewScope(cdef, context.owner)).typedCase(cdef, pattp, pt)
}
+ def adaptCase(cdef: CaseDef, mode: Int, tpe: Type): CaseDef = deriveCaseDef(cdef)(adapt(_, mode, tpe))
+
+ def translateMatch(selector: Tree, cases: List[CaseDef], mode: Int, resTp: Type, scrutTp: Type = NoType, matchFailGen: Option[Tree => Tree] = None) = {
+ val selector1 = if(scrutTp eq NoType) checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType)) else selector
+ val selectorTp = if(scrutTp eq NoType) packCaptured(selector1.tpe.widen) else scrutTp
+ val casesTyped = typedCases(cases, selectorTp, resTp)
+ val (ownType, needAdapt) = if (isFullyDefined(resTp)) (resTp, false) else weakLub(casesTyped map (_.tpe.deconst))
+ val casesAdapted = if (!needAdapt) casesTyped else casesTyped map (adaptCase(_, mode, ownType))
+ // val (owntype0, needAdapt) = ptOrLub(casesTyped map (x => repackExistential(x.tpe)))
+ // val owntype = elimAnonymousClass(owntype0)
+
+ def repeatedToSeq(tp: Type): Type = (tp baseType RepeatedParamClass) match {
+ case TypeRef(_, RepeatedParamClass, args) => appliedType(SeqClass.typeConstructor, args)
+ case _ => tp
+ }
+
+ def isSynthSelector(selector: Tree): Boolean = selector match {
+ case Ident(_) if selector.symbol.hasFlag(SYNTHETIC | CASE) => true
+ case Select(sel, nme.toInt) => isSynthSelector(sel) // switch may need to convert to int first
+ case _ => false
+ }
+
+ if (isSynthSelector(selector1)) { // a switch
+ (Match(selector1, casesAdapted) setType ownType, ownType) // setType of the Match to avoid recursing endlessly
+ } else {
+ val scrutType = repeatedToSeq(elimAnonymousClass(selectorTp))
+ (MatchTranslator(this).translateMatch(selector1, casesAdapted, repeatedToSeq(ownType), scrutType, matchFailGen), ownType)
+ }
+ }
+
+ // TODO: use this to synthesize (partial)function implementation for matches from the get-go,
+ // instead of the dirty post-factum hacks in uncurry -- typedMatchAnonFun is currently not used due to mindboggling failures (see virtpatmat_anonfun_for.scala)
+ def typedMatchAnonFun(tree: Tree, cases: List[CaseDef], mode: Int, pt0: Type, selOverride: Option[(List[Symbol], Tree)] = None) = {
+ val pt = deskolemizeGADTSkolems(pt0)
+ val targs = pt.normalize.typeArgs
+ val arity = if (isFunctionType(pt)) targs.length - 1 else 1
+ val scrutTp0 = if (arity == 1) targs.head else /* arity > 1 */ tupleType(targs.init)
+ val scrutTp = packCaptured(scrutTp0)
+ val ptRes = targs.last // may not be fully defined
+ val isPartial = pt.typeSymbol == PartialFunctionClass
+ val cname = tpnme.ANON_FUN_NAME
+ val funThis = This(cname)
+ // used to create a new context for pattern matching translation so that
+ // we can easily rejig the owner structure when we have the actual symbols for these methods
+ // (after type checking them, but type checking requires translation -- this seems like the easiest way to end this vicious cycle)
+ val applySentinel = NoSymbol.newMethod(nme.apply)
+ val idaSentinel = NoSymbol.newMethod(nme._isDefinedAt)
+
+ def mkParams = {
+ val params =
+ for (i <- List.range(0, arity)) yield atPos(tree.pos.focusStart) {
+ ValDef(Modifiers(SYNTHETIC | PARAM), unit.freshTermName("x" + i + "$"), TypeTree(), EmptyTree)
+ }
+ val ids = params map (p => Ident(p.name))
+
+ val paramsRef = selOverride match {
+ case None => atPos(tree.pos.focusStart) { if (arity == 1) ids.head else gen.mkTuple(ids) }
+ case Some((_, sel)) => sel.duplicate // we'll replace the symbols that refer to the function's original syms by the ones introduced by the DefDef once the method's been type checked (until then, we don't know them)
+ }
+
+ (params, paramsRef) // paramsRef can't be typed until after match has been translated, thus supply explicit scrutTp to translate below
+ }
+
+ import CODE._
+
+ // 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).duplicate) else Nil
+
+ val (applyMethod, parents) = {
+ val (params, paramsRef) = mkParams
+ val (body, resTp) = newTyper(context.make(context.tree, applySentinel)).translateMatch(paramsRef, cases, mode, ptRes, scrutTp, if (isPartial) Some(scrut => (funThis DOT nme.missingCase) (scrut)) else None)
+
+ def abstractFunctionType = {
+ val sym = AbstractFunctionClass(arity)
+ typeRef(sym.typeConstructor.prefix, sym, targs.init :+ resTp)
+ }
+
+ val parents =
+ if (isFunctionType(pt)) List(abstractFunctionType, SerializableClass.tpe)
+ else if (isPartial) List(appliedType(AbstractPartialFunctionClass.typeConstructor, List(scrutTp, resTp)), SerializableClass.tpe)
+ else List(ObjectClass.tpe, pt, SerializableClass.tpe)
+
+ (atPos(tree.pos.focus)(DefDef(Modifiers(FINAL), nme.apply, Nil, List(params), TypeTree() setType resTp, body)), parents)
+ }
+
+ def isDefinedAtMethod = {
+ val (params, paramsRef) = mkParams
+ val (body, _) = newTyper(context.make(context.tree, idaSentinel)).translateMatch(paramsRef, casesTrue, mode, BooleanClass.tpe, scrutTp, Some(scrutinee => FALSE))
+ atPos(tree.pos.focus)(
+ DefDef(Modifiers(FINAL), nme._isDefinedAt, Nil, List(params), TypeTree() setType BooleanClass.tpe, body)
+ )
+ }
+
+ val members = if (!isPartial) List(applyMethod) else List(applyMethod, isDefinedAtMethod)
+
+ val cmods = Modifiers(FINAL | SYNTHETIC /*TODO: when do we need INCONSTRUCTOR ?*/) withAnnotations (
+ List(NEW(SerialVersionUIDAttr, LIT(0))))
+ val cdef =
+ ClassDef(cmods, cname, Nil,
+ Template(parents map (TypeTree() setType _), emptyValDef, Modifiers(0), Nil, List(Nil), members, tree.pos)
+ )
+ val funInst = (Block(List(cdef), Apply(Select(New(Ident(cname)), nme.CONSTRUCTOR), Nil)))
+
+ val res = typed(funInst, mode, pt)
+
+ // now that we have the symbols corresponding to the apply/isDefinedAt methods,
+ // we can fix up the result of fixerUpper... URGH
+ // fixerUpper nests the top-level definitions generated in the match under context.owner, but they should be owner by the apply/isDefinedAt method
+ res foreach {
+ case d: DefDef if (d.symbol.name == nme.apply) =>
+ d.rhs.changeOwner(applySentinel -> d.symbol)
+ case d: DefDef if (d.symbol.name == nme._isDefinedAt) =>
+ d.rhs.changeOwner(idaSentinel -> d.symbol)
+ case _ =>
+ }
+
+ selOverride match {
+ case None => res
+ case Some((paramSyms, sel)) =>
+ object substParamSyms extends Transformer {
+ override def transform(t: Tree): Tree = t match {
+ case d: DefDef if (d.symbol.name == nme.apply) || (d.symbol.name == nme._isDefinedAt) && (d.symbol.owner == res.tpe.typeSymbol) =>
+ deriveDefDef(d)(rhs => rhs.substTreeSyms(paramSyms, d.vparamss.head.map(_.symbol)))
+ case _ =>
+ super.transform(t)
+ }
+ }
+ substParamSyms.transform(res)
+ }
+ }
+
/**
* @param fun ...
* @param mode ...
@@ -2155,14 +2286,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
return MaxFunctionArityError(fun)
def decompose(pt: Type): (Symbol, List[Type], Type) =
- if ((isFunctionType(pt)
- ||
- pt.typeSymbol == PartialFunctionClass &&
- numVparams == 1 && fun.body.isInstanceOf[Match])
- && // see bug901 for a reason why next conditions are needed
- (pt.normalize.typeArgs.length - 1 == numVparams
- ||
- fun.vparams.exists(_.tpt.isEmpty)))
+ if ((isFunctionType(pt) || (pt.typeSymbol == PartialFunctionClass && numVparams == 1 && fun.body.isInstanceOf[Match])) && // see bug901 for a reason why next conditions are needed
+ ( pt.normalize.typeArgs.length - 1 == numVparams
+ || fun.vparams.exists(_.tpt.isEmpty)
+ ))
(pt.typeSymbol, pt.normalize.typeArgs.init, pt.normalize.typeArgs.last)
else
(FunctionClass(numVparams), fun.vparams map (x => NoType), WildcardType)
@@ -2204,13 +2331,27 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
// for (vparam <- vparams) {
// checkNoEscaping.locals(context.scope, WildcardType, vparam.tpt); ()
// }
- val body1 = typed(fun.body, respt)
- val formals = vparamSyms map (_.tpe)
- 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)
- }
+
+ def recompose(from: Type, to: Type) =
+ if(clazz == PartialFunctionClass) appliedType(PartialFunctionClass.typeConstructor, List(from, to))
+ else functionType(List(from), to)
+
+ fun.body match {
+ case Match(sel, cases) if opt.virtPatmat =>
+ val typedSel = typed(sel, EXPRmode | BYVALmode, WildcardType)
+ // 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, recompose(typedSel.tpe, respt), Some((vparamSyms, typedSel)))
+ case _ =>
+ val body1 = typed(fun.body, respt)
+ val formals = vparamSyms map (_.tpe)
+ 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)
+ }
+ }
}
def typedRefinement(stats: List[Tree]) {
@@ -3431,7 +3572,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
}
def typedMatch(tree: Tree, selector: Tree, cases: List[CaseDef]): Tree = {
- if (selector == EmptyTree) {
+ if (opt.virtPatmat && !isPastTyper) {
+ if (selector ne EmptyTree) typed(translateMatch(selector, cases, mode, pt)._1, mode, pt)
+ else typedMatchAnonFun(tree, cases, mode, pt)
+ } else if (selector == EmptyTree) {
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) {
@@ -3445,32 +3589,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
} else {
val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType))
var cases1 = typedCases(cases, packCaptured(selector1.tpe.widen), pt)
-
- if (isPastTyper || !opt.virtPatmat) {
- val (owntype, needAdapt) = ptOrLub(cases1 map (_.tpe))
- if (needAdapt) {
- cases1 = cases1 map (adaptCase(_, owntype))
- }
- treeCopy.Match(tree, selector1, cases1) setType owntype
- } else { // don't run translator after typers (see comments in PatMatVirtualiser)
- val (owntype0, needAdapt) = ptOrLub(cases1 map (x => repackExistential(x.tpe)))
- val owntype = elimAnonymousClass(owntype0)
- if (needAdapt) cases1 = cases1 map (adaptCase(_, owntype))
-
- (MatchTranslator(this)).translateMatch(selector1, cases1, owntype) match {
- case Block(vd :: Nil, tree@Match(selector, cases)) =>
- 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(_, owntype))
- typed(Block(vd :: Nil, treeCopy.Match(tree, selector1, cases1) setType owntype))
- case translated =>
- // TODO: get rid of setType owntype -- it should all typecheck
- // must call typed, not typed1, or we overflow the stack when emitting switches
- typed(translated, mode, WildcardType) setType owntype
- }
+ val (owntype, needAdapt) = ptOrLub(cases1 map (_.tpe))
+ if (needAdapt) {
+ cases1 = cases1 map (adaptCase(_, mode, owntype))
}
+ treeCopy.Match(tree, selector1, cases1) setType owntype
}
}
@@ -4240,9 +4363,6 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
}
}
- def adaptCase(cdef: CaseDef, tpe: Type): CaseDef =
- deriveCaseDef(cdef)(adapt(_, mode, tpe))
-
// begin typed1
val sym: Symbol = tree.symbol
if ((sym ne null) && (sym ne NoSymbol)) sym.initialize
@@ -4351,7 +4471,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
val (owntype, needAdapt) = ptOrLub(block1.tpe :: (catches1 map (_.tpe)))
if (needAdapt) {
block1 = adapt(block1, mode, owntype)
- catches1 = catches1 map (adaptCase(_, owntype))
+ catches1 = catches1 map (adaptCase(_, mode, owntype))
}
if(!isPastTyper && opt.virtPatmat) {