From 383d28daa161e46843fd99494f5c6fbc3e622fec Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Wed, 28 Mar 2012 15:17:59 +0200 Subject: old patmat support for the applyOrElse partial fun minimal fixes for typedMatchAnonFun so it compiles TODO: support for AbstractTotalFunction (when match is exhaustive) --- src/compiler/scala/reflect/internal/StdNames.scala | 3 +- .../scala/tools/nsc/transform/UnCurry.scala | 206 ++++++++++----------- .../scala/tools/nsc/typechecker/Typers.scala | 4 +- 3 files changed, 96 insertions(+), 117 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/reflect/internal/StdNames.scala b/src/compiler/scala/reflect/internal/StdNames.scala index 9fb7b5b747..e2c253628c 100644 --- a/src/compiler/scala/reflect/internal/StdNames.scala +++ b/src/compiler/scala/reflect/internal/StdNames.scala @@ -283,13 +283,13 @@ trait StdNames extends NameManglers { self: SymbolTable => val TYPE_ : NameType = "TYPE" val TypeTree: NameType = "TypeTree" val UNIT : NameType = "UNIT" - val _isDefinedAt: NameType = "_isDefinedAt" val add_ : NameType = "add" val annotation: NameType = "annotation" val anyValClass: NameType = "anyValClass" val append: NameType = "append" val apply: NameType = "apply" val applyDynamic: NameType = "applyDynamic" + val applyOrElse: NameType = "applyOrElse" val args : NameType = "args" val argv : NameType = "argv" val arrayValue: NameType = "arrayValue" @@ -357,7 +357,6 @@ trait StdNames extends NameManglers { self: SymbolTable => val main: NameType = "main" val map: NameType = "map" val mirror : NameType = "mirror" - val missingCase: NameType = "missingCase" val ne: NameType = "ne" val newArray: NameType = "newArray" val newScopeWith: NameType = "newScopeWith" diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 0d39c040f7..0efdd9ab9f 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -212,13 +212,13 @@ abstract class UnCurry extends InfoTransform * //TODO: correct code template below * class $anon() extends AbstractPartialFunction[T, R] with Serializable { - * def apply(x: T): R = (expr: @unchecked) match { + * def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = (expr: @unchecked) match { * case P_1 if G_1 => E_1 * ... - * case P_n if G_n => true - * case _ => this.missingCase(expr) + * case P_n if G_n => E_n + * case _ => default(expr) * } - * def _isDefinedAt(x: T): boolean = (x: @unchecked) match { + * def isDefinedAt(x: T): boolean = (x: @unchecked) match { * case P_1 if G_1 => true * ... * case P_n if G_n => true @@ -232,127 +232,107 @@ abstract class UnCurry extends InfoTransform * * def isDefinedAtCurrent(x: T): boolean = true */ - def transformFunction(fun: Function): Tree = { - val fun1 = deEta(fun) - def owner = fun.symbol.owner - def targs = fun.tpe.typeArgs - def isPartial = fun.tpe.typeSymbol == PartialFunctionClass - - // if the function was eta-expanded, it's not a match without a selector - if (fun1 ne fun) fun1 - else { - assert(!(opt.virtPatmat && isPartial)) // empty-selector matches have already been translated into instantiations of anonymous (partial) functions - val (formals, restpe) = (targs.init, targs.last) - val anonClass = owner.newAnonymousFunctionClass(fun.pos, inConstructorFlag) - def parents = - if (isFunctionType(fun.tpe)) List(abstractFunctionForFunctionType(fun.tpe), SerializableClass.tpe) - else if (isPartial) List(appliedType(AbstractPartialFunctionClass.typeConstructor, targs), SerializableClass.tpe) - else List(ObjectClass.tpe, fun.tpe, SerializableClass.tpe) - - anonClass setInfo ClassInfoType(parents, newScope, anonClass) - val applyMethod = anonClass.newMethod(nme.apply, fun.pos, FINAL) - applyMethod setInfoAndEnter MethodType(applyMethod newSyntheticValueParams formals, restpe) - anonClass addAnnotation serialVersionUIDAnnotation - - fun.vparams foreach (_.symbol.owner = applyMethod) - fun.body.changeOwner(fun.symbol -> applyMethod) - - def missingCaseCall(scrutinee: Tree): Tree = Apply(Select(This(anonClass), nme.missingCase), List(scrutinee)) - - def applyMethodDef() = { - val body = localTyper.typedPos(fun.pos) { - if (isPartial) gen.mkUncheckedMatch(gen.withDefaultCase(fun.body, missingCaseCall)) - else fun.body - } - // Have to repack the type to avoid mismatches when existentials - // appear in the result - see SI-4869. - val applyResultType = localTyper.packedType(body, applyMethod) - DefDef(Modifiers(FINAL), nme.apply, Nil, List(fun.vparams), TypeTree(applyResultType), body) setSymbol applyMethod - } - def isDefinedAtMethodDef() = { - val isDefinedAtName = { - if (anonClass.info.member(nme._isDefinedAt) != NoSymbol) nme._isDefinedAt - else nme.isDefinedAt - } - val m = anonClass.newMethod(isDefinedAtName, fun.pos, FINAL) - val params = m newSyntheticValueParams formals - m setInfoAndEnter MethodType(params, BooleanClass.tpe) + def transformFunction(fun: Function): Tree = + deEta(fun) match { + // nullary or parameterless + case fun1 if fun1 ne fun => fun1 + case _ => + def owner = fun.symbol.owner + def targs = fun.tpe.typeArgs + def isPartial = fun.tpe.typeSymbol == PartialFunctionClass + assert(!(opt.virtPatmat && isPartial)) // empty-selector matches have already been translated into instantiations of anonymous (partial) functions - val substParam = new TreeSymSubstituter(fun.vparams map (_.symbol), params) - def substTree[T <: Tree](t: T): T = substParam(resetLocalAttrs(t)) + def parents = + if (isFunctionType(fun.tpe)) List(abstractFunctionForFunctionType(fun.tpe), SerializableClass.tpe) + else if (isPartial) List(appliedType(AbstractPartialFunctionClass.typeConstructor, targs), SerializableClass.tpe) + else List(ObjectClass.tpe, fun.tpe, SerializableClass.tpe) - object isDefinedAtTransformer extends gen.MatchMatcher { - // TODO: optimize duplication, but make sure ValDef's introduced by wrap are treated correctly - override def caseMatch(orig: Tree, selector: Tree, cases: List[CaseDef], wrap: Tree => Tree): Tree = { - def transformCase(cdef: CaseDef): CaseDef = - CaseDef(cdef.pat, cdef.guard, Literal(Constant(true))) + val anonClass = owner newAnonymousFunctionClass(fun.pos, inConstructorFlag) addAnnotation serialVersionUIDAnnotation + anonClass setInfo ClassInfoType(parents, newScope, anonClass) - def defaultCase = CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false))) + val (formals, restpe) = (targs.init, targs.last) -// val casesNoSynthCatchAll = dropSyntheticCatchAll(cases) + def applyMethodDef = { + val methSym = anonClass.newMethod(nme.apply, fun.pos, FINAL) + methSym setInfoAndEnter MethodType(methSym newSyntheticValueParams formals, restpe) - gen.mkUncheckedMatch( - if (cases exists treeInfo.isDefaultCase) Literal(Constant(true)) - else substTree(wrap(Match(selector, (cases map transformCase) :+ defaultCase)).duplicate) - ) + fun.vparams foreach (_.symbol.owner = methSym) + fun.body changeOwner (fun.symbol -> methSym) + + val body = localTyper.typedPos(fun.pos)(fun.body) + val methDef = DefDef(methSym, List(fun.vparams), body) + + // Have to repack the type to avoid mismatches when existentials + // appear in the result - see SI-4869. + methDef.tpt setType localTyper.packedType(body, methSym) + methDef + } + + // def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = + def applyOrElseMethodDef = { + val methSym = anonClass.newMethod(fun.pos, nme.applyOrElse) setFlag (FINAL | OVERRIDE) + + val List(argtpe) = formals + val A1 = methSym newTypeParameter(newTypeName("A1")) setInfo TypeBounds.upper(argtpe) + val B1 = methSym newTypeParameter(newTypeName("B1")) setInfo TypeBounds.lower(restpe) + val methFormals = List(A1.tpe, functionType(List(A1.tpe), B1.tpe)) + val params@List(x, default) = methSym newSyntheticValueParams methFormals + methSym setInfoAndEnter polyType(List(A1, B1), MethodType(params, B1.tpe)) + + val substParam = new TreeSymSubstituter(fun.vparams map (_.symbol), List(x)) + val body = localTyper.typedPos(fun.pos) { import CODE._ + gen.mkUncheckedMatch(gen.withDefaultCase(substParam(fun.body), scrut => REF(default) APPLY (REF(x)))) } + body.changeOwner(fun.symbol -> methSym) + + val methDef = DefDef(methSym, body) - override def caseVirtualizedMatch(orig: Tree, _match: Tree, targs: List[Tree], scrut: Tree, matcher: Tree): Tree = {assert(false); orig} - // { - // object noOne extends Transformer { - // override val treeCopy = newStrictTreeCopier // must duplicate everything - // val one = _match.tpe member newTermName("one") - // override def transform(tree: Tree): Tree = tree match { - // case Apply(fun, List(a)) if fun.symbol == one => - // // blow one's argument away since all we want to know is whether the match succeeds or not - // // (the alternative, making `one` CBN, would entail moving away from Option) - // Apply(fun.duplicate, List(gen.mkZeroContravariantAfterTyper(a.tpe))) - // case _ => - // super.transform(tree) - // } - // } - // substTree(Apply(Apply(TypeApply(Select(_match.duplicate, _match.tpe.member(newTermName("isSuccess"))), targs map (_.duplicate)), List(scrut.duplicate)), List(noOne.transform(matcher)))) - // } - - override def caseVirtualizedMatchOpt(orig: Tree, zero: ValDef, x: ValDef, matchRes: ValDef, keepGoing: ValDef, stats: List[Tree], epilogue: Tree, wrap: Tree => Tree) = {assert(false); orig} - // { - // object dropMatchResAssign extends Transformer { - // // override val treeCopy = newStrictTreeCopier // will duplicate below - // override def transform(tree: Tree): Tree = tree match { - // // don't compute the result of the match -- remove the block for the RHS (emitted by pmgen.one), except for the assignment to keepGoing - // case gen.VirtualCaseDef(assignKeepGoing, matchRes, zero) if assignKeepGoing.lhs.symbol eq keepGoing.symbol => - // Block(List(assignKeepGoing), zero) - // case _ => - // super.transform(tree) - // } - // } - // val statsNoMatchRes: List[Tree] = stats map (dropMatchResAssign.transform) toList - // val idaBlock = wrap(Block( - // zero :: - // x :: - // /* drop matchRes def */ - // keepGoing :: - // statsNoMatchRes, - // NOT(REF(keepGoing.symbol)) // replace `if (keepGoing) throw new MatchError(...) else matchRes` epilogue by `!keepGoing` - // )) - // substTree(idaBlock.duplicate) // duplicate on block as a whole to ensure valdefs are properly cloned and substed - // } + // Have to repack the type to avoid mismatches when existentials + // appear in the result - see SI-4869. + methDef.tpt setType localTyper.packedType(body, methSym) + methDef } - DefDef(m, isDefinedAtTransformer(fun.body)) - } + // duplicate before applyOrElseMethodDef is run so we start with the same symbols as applyOrElseMethodDef + // otherwise `TreeSymSubstituter(fun.vparams map (_.symbol), params)` won't work as the subst has been run already + val bodyForIDA = fun.body.duplicate + def isDefinedAtMethodDef = { + val methSym = anonClass.newMethod(nme.isDefinedAt, fun.pos, FINAL) + val params = methSym newSyntheticValueParams formals + methSym setInfoAndEnter MethodType(params, BooleanClass.tpe) + + val substParam = new TreeSymSubstituter(fun.vparams map (_.symbol), params) + def doSubst(x: Tree) = substParam(resetLocalAttrs(x)) // see pos/t1761 for why `resetLocalAttrs` + object isDefinedAtTransformer extends gen.MatchMatcher { + // TODO: optimize duplication, but make sure ValDef's introduced by wrap are treated correctly + override def caseMatch(orig: Tree, selector: Tree, cases: List[CaseDef], wrap: Tree => Tree): Tree = { import CODE._ + gen.mkUncheckedMatch( + if (cases exists treeInfo.isDefaultCase) TRUE_typed + else + doSubst(wrap( + Match(selector, + (cases map (c => deriveCaseDef(c)(x => TRUE_typed))) :+ ( + DEFAULT ==> FALSE_typed) + ))) + ) + } + } + val body = isDefinedAtTransformer(bodyForIDA) + body.changeOwner(fun.symbol -> methSym) - val members = - if (isPartial) List(applyMethodDef, isDefinedAtMethodDef) - else List(applyMethodDef) + DefDef(methSym, body) + } + + val members = + if (isPartial) List(applyOrElseMethodDef, isDefinedAtMethodDef) + else List(applyMethodDef) - localTyper.typedPos(fun.pos) { - Block( - List(ClassDef(anonClass, NoMods, List(List()), List(List()), members, fun.pos)), - Typed(New(anonClass.tpe), TypeTree(fun.tpe))) + localTyper.typedPos(fun.pos) { + Block( + List(ClassDef(anonClass, NoMods, List(List()), List(List()), members, fun.pos)), + Typed(New(anonClass.tpe), TypeTree(fun.tpe))) + } } - } - } def transformArgs(pos: Position, fun: Symbol, args: List[Tree], formals: List[Type]) = { val isJava = fun.isJavaDefined diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 06a7311f79..aed29f27b5 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2233,7 +2233,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { methodSym setInfoAndEnter MethodType(paramSyms, resTp) // use apply's parameter since the scrut's type has been widened - def missingCase(scrut_ignored: Tree) = (funThis DOT nme.missingCase) (REF(paramSyms.head)) + def missingCase(scrut_ignored: Tree) = (funThis DOT newTermName("missingCase")) (REF(paramSyms.head)) val body = methodBodyTyper.translateMatch(selector1, selectorTp, casesAdapted, resTp, doTranslation, if (isPartial) Some(missingCase) else None) @@ -2242,7 +2242,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } def isDefinedAtMethod = { - val methodSym = anonClass.newMethod(nme._isDefinedAt, tree.pos, FINAL) + val methodSym = anonClass.newMethod(nme.isDefinedAt, tree.pos, FINAL) val (paramSyms, selector) = mkParams(methodSym) if (selector eq EmptyTree) EmptyTree else { -- cgit v1.2.3