diff options
author | Adriaan Moors <adriaan.moors@epfl.ch> | 2012-03-14 11:47:59 +0100 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@epfl.ch> | 2012-03-20 19:54:03 +0100 |
commit | b046a6e3316df8b27ac31e71da1a139c800ccce7 (patch) | |
tree | dfbed2ea5620d100a62bbdf39b93488628e8cbb3 /src/compiler/scala/tools/nsc/typechecker/Typers.scala | |
parent | 2d3b309a36757545901b99da1b77698749fcc0c5 (diff) | |
download | scala-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.scala | 210 |
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) { |