diff options
author | Paul Phillips <paulp@improving.org> | 2012-03-30 20:54:05 -0700 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2012-03-30 20:54:05 -0700 |
commit | 03aca2c5bc293f8fd79e99b2aaac79cdc043c1da (patch) | |
tree | ea832b582e355f1a6687b286266b59b8108188c6 /src | |
parent | dc8b431f1c38ff805d660c44b19ee5ecc91bddb2 (diff) | |
parent | 1890a4175dc6c436b37ddc8c6b74e036b9c6328c (diff) | |
download | scala-03aca2c5bc293f8fd79e99b2aaac79cdc043c1da.tar.gz scala-03aca2c5bc293f8fd79e99b2aaac79cdc043c1da.tar.bz2 scala-03aca2c5bc293f8fd79e99b2aaac79cdc043c1da.zip |
Merge remote-tracking branch 'adriaanm/topic/partialfun' into develop
Diffstat (limited to 'src')
-rw-r--r-- | src/actors/scala/actors/Actor.scala | 8 | ||||
-rw-r--r-- | src/actors/scala/actors/Future.scala | 4 | ||||
-rw-r--r-- | src/actors/scala/actors/Reactor.scala | 4 | ||||
-rw-r--r-- | src/compiler/scala/reflect/internal/StdNames.scala | 3 | ||||
-rw-r--r-- | src/compiler/scala/tools/cmd/FromString.scala | 12 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/interpreter/AbstractOrMissingHandler.scala | 4 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/matching/ParallelMatching.scala | 4 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/UnCurry.scala | 207 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Duplicators.scala | 16 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 71 | ||||
-rw-r--r-- | src/library/scala/Function.scala | 7 | ||||
-rw-r--r-- | src/library/scala/PartialFunction.scala | 134 | ||||
-rw-r--r-- | src/library/scala/runtime/AbstractPartialFunction.scala | 84 | ||||
-rw-r--r-- | src/library/scala/util/control/Exception.scala | 5 |
14 files changed, 339 insertions, 224 deletions
diff --git a/src/actors/scala/actors/Actor.scala b/src/actors/scala/actors/Actor.scala index aab533ae8d..bc9bbc6ef0 100644 --- a/src/actors/scala/actors/Actor.scala +++ b/src/actors/scala/actors/Actor.scala @@ -1,3 +1,5 @@ + + /* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2005-2011, LAMP/EPFL ** @@ -246,8 +248,8 @@ object Actor extends Combinators { rawSelf.react(new RecursiveProxyHandler(rawSelf, f)) private class RecursiveProxyHandler(a: InternalReplyReactor, f: PartialFunction[Any, Unit]) - extends scala.runtime.AbstractPartialFunction[Any, Unit] { - def _isDefinedAt(m: Any): Boolean = + extends PartialFunction[Any, Unit] { + def isDefinedAt(m: Any): Boolean = true // events are immediately removed from the mailbox def apply(m: Any) { if (f.isDefinedAt(m)) f(m) @@ -404,5 +406,5 @@ trait Actor extends InternalActor with ReplyReactor { this } -} + } diff --git a/src/actors/scala/actors/Future.scala b/src/actors/scala/actors/Future.scala index eec43013d3..735c13190b 100644 --- a/src/actors/scala/actors/Future.scala +++ b/src/actors/scala/actors/Future.scala @@ -200,8 +200,8 @@ object Futures { Actor.timer.schedule(timerTask, timeout) def awaitWith(partFuns: Seq[PartialFunction[Any, Pair[Int, Any]]]) { - val reaction: PartialFunction[Any, Unit] = new scala.runtime.AbstractPartialFunction[Any, Unit] { - def _isDefinedAt(msg: Any) = msg match { + val reaction: PartialFunction[Any, Unit] = new PartialFunction[Any, Unit] { + def isDefinedAt(msg: Any) = msg match { case TIMEOUT => true case _ => partFuns exists (_ isDefinedAt msg) } diff --git a/src/actors/scala/actors/Reactor.scala b/src/actors/scala/actors/Reactor.scala index 8fc7578344..206a97d97c 100644 --- a/src/actors/scala/actors/Reactor.scala +++ b/src/actors/scala/actors/Reactor.scala @@ -38,8 +38,8 @@ private[actors] object Reactor { } } - val waitingForNone: PartialFunction[Any, Unit] = new scala.runtime.AbstractPartialFunction[Any, Unit] { - def _isDefinedAt(x: Any) = false + val waitingForNone: PartialFunction[Any, Unit] = new PartialFunction[Any, Unit] { + def isDefinedAt(x: Any) = false def apply(x: Any) {} } } 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/cmd/FromString.scala b/src/compiler/scala/tools/cmd/FromString.scala index e4504702d4..3792c26c34 100644 --- a/src/compiler/scala/tools/cmd/FromString.scala +++ b/src/compiler/scala/tools/cmd/FromString.scala @@ -14,9 +14,9 @@ import scala.reflect.OptManifest * example instances are in the companion object, but in general * either IntFromString will suffice or you'll want custom transformers. */ -abstract class FromString[+T](implicit m: OptManifest[T]) extends scala.runtime.AbstractPartialFunction[String, T] { +abstract class FromString[+T](implicit m: OptManifest[T]) extends PartialFunction[String, T] { def apply(s: String): T - def _isDefinedAt(s: String): Boolean = true + def isDefinedAt(s: String): Boolean = true def zero: T = apply("") def targetString: String = m.toString @@ -30,20 +30,20 @@ object FromString { /** Path related stringifiers. */ val ExistingFile: FromString[File] = new FromString[File] { - override def _isDefinedAt(s: String) = toFile(s).isFile + override def isDefinedAt(s: String) = toFile(s).isFile def apply(s: String): File = if (isDefinedAt(s)) toFile(s) else cmd.runAndExit(println("'%s' is not an existing file." format s)) } val ExistingDir: FromString[Directory] = new FromString[Directory] { - override def _isDefinedAt(s: String) = toDir(s).isDirectory + override def isDefinedAt(s: String) = toDir(s).isDirectory def apply(s: String): Directory = if (isDefinedAt(s)) toDir(s) else cmd.runAndExit(println("'%s' is not an existing directory." format s)) } def ExistingDirRelativeTo(root: Directory) = new FromString[Directory] { private def resolve(s: String) = toDir(s) toAbsoluteWithRoot root toDirectory - override def _isDefinedAt(s: String) = resolve(s).isDirectory + override def isDefinedAt(s: String) = resolve(s).isDirectory def apply(s: String): Directory = if (isDefinedAt(s)) resolve(s) else cmd.runAndExit(println("'%s' is not an existing directory." format resolve(s))) @@ -65,7 +65,7 @@ object FromString { /** Implicit as the most likely to be useful as-is. */ implicit val IntFromString: FromString[Int] = new FromString[Int] { - override def _isDefinedAt(s: String) = safeToInt(s).isDefined + override def isDefinedAt(s: String) = safeToInt(s).isDefined def apply(s: String) = safeToInt(s).get def safeToInt(s: String): Option[Int] = try Some(java.lang.Integer.parseInt(s)) catch { case _: NumberFormatException => None } } diff --git a/src/compiler/scala/tools/nsc/interpreter/AbstractOrMissingHandler.scala b/src/compiler/scala/tools/nsc/interpreter/AbstractOrMissingHandler.scala index 33ef4a432d..2f47685757 100644 --- a/src/compiler/scala/tools/nsc/interpreter/AbstractOrMissingHandler.scala +++ b/src/compiler/scala/tools/nsc/interpreter/AbstractOrMissingHandler.scala @@ -6,8 +6,8 @@ package scala.tools.nsc package interpreter -class AbstractOrMissingHandler[T](onError: String => Unit, value: T) extends scala.runtime.AbstractPartialFunction[Throwable, T] { - def _isDefinedAt(t: Throwable) = t match { +class AbstractOrMissingHandler[T](onError: String => Unit, value: T) extends PartialFunction[Throwable, T] { + def isDefinedAt(t: Throwable) = t match { case _: AbstractMethodError => true case _: NoSuchMethodError => true case _: MissingRequirementError => true diff --git a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala b/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala index be5a9907b8..43aad9f591 100644 --- a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala +++ b/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala @@ -425,7 +425,7 @@ trait ParallelMatching extends ast.TreeDSL // Should the given pattern join the expanded pivot in the success matrix? If so, // this partial function will be defined for the pattern, and the result of the apply // is the expanded sequence of new patterns. - lazy val successMatrixFn = new scala.runtime.AbstractPartialFunction[Pattern, List[Pattern]] { + lazy val successMatrixFn = new PartialFunction[Pattern, List[Pattern]] { private def seqIsDefinedAt(x: SequenceLikePattern) = (hasStar, x.hasStar) match { case (true, true) => true case (true, false) => pivotLen <= x.nonStarLength @@ -433,7 +433,7 @@ trait ParallelMatching extends ast.TreeDSL case (false, false) => pivotLen == x.nonStarLength } - def _isDefinedAt(pat: Pattern) = pat match { + def isDefinedAt(pat: Pattern) = pat match { case x: SequenceLikePattern => seqIsDefinedAt(x) case WildcardPattern() => true case _ => false diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index e54e0289bb..0efdd9ab9f 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -210,14 +210,15 @@ abstract class UnCurry extends InfoTransform * body = expr match { case P_i if G_i => E_i }_i=1..n * to: * + //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 @@ -231,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/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala index eb0d489901..f6d1e42c32 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala @@ -79,7 +79,17 @@ abstract class Duplicators extends Analyzer { override def mapOver(tpe: Type): Type = tpe match { case TypeRef(NoPrefix, sym, args) if sym.isTypeParameterOrSkolem => - val sym1 = context.scope.lookup(sym.name) + var sym1 = context.scope.lookup(sym.name) + if (sym1 eq NoSymbol) { + // try harder (look in outer scopes) + // with virtpatmat, this can happen when the sym is referenced in the scope of a LabelDef but is defined in the scope of an outer DefDef (e.g., in AbstractPartialFunction's andThen) + BodyDuplicator.super.silent(_.typedType(Ident(sym.name))) match { + case SilentResultValue(t) => + sym1 = t.symbol + debuglog("fixed by trying harder: "+(sym, sym1, context)) + case _ => + } + } // assert(sym1 ne NoSymbol, tpe) if ((sym1 ne NoSymbol) && (sym1 ne sym)) { debuglog("fixing " + sym + " -> " + sym1) @@ -255,7 +265,10 @@ abstract class Duplicators extends Analyzer { case ldef @ LabelDef(name, params, rhs) => // log("label def: " + ldef) + // in case the rhs contains any definitions -- TODO: is this necessary? + invalidate(rhs) ldef.tpe = null + // since typer does not create the symbols for a LabelDef's params, // we do that manually here -- we should really refactor LabelDef to be a subclass of DefDef def newParam(p: Tree): Ident = { @@ -265,6 +278,7 @@ abstract class Duplicators extends Analyzer { val params1 = params map newParam val rhs1 = (new TreeSubstituter(params map (_.symbol), params1) transform rhs) // TODO: duplicate? rhs1.tpe = null + super.typed(treeCopy.LabelDef(tree, name, params1, rhs1), mode, pt) case Bind(name, _) => diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 06a7311f79..7d2e587b3f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2186,17 +2186,24 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { anonClass addAnnotation serialVersionUIDAnnotation - def mkParams(methodSym: Symbol) = { + def deriveFormals = + selOverride match { + case None if targs.isEmpty => Nil + case None => targs.init // is there anything we can do if targs.isEmpty?? + case Some((vparams, _)) => + vparams map {p => if(p.tpt.tpe == null) typedType(p.tpt).tpe else p.tpt.tpe} + } + + def mkParams(methodSym: Symbol, formals: List[Type] = deriveFormals) = { selOverride match { case None if targs.isEmpty => MissingParameterTypeAnonMatchError(tree, pt); (Nil, EmptyTree) case None => - val ps = methodSym newSyntheticValueParams targs.init // is there anything we can do if targs.isEmpty?? + val ps = methodSym newSyntheticValueParams formals // is there anything we can do if targs.isEmpty?? val ids = ps map (p => Ident(p.name)) val sel = atPos(tree.pos.focusStart) { if (arity == 1) ids.head else gen.mkTuple(ids) } (ps, sel) case Some((vparams, sel)) => - val newParamSyms = vparams map {p => - val tp = if(p.tpt.tpe == null) typedType(p.tpt).tpe else p.tpt.tpe + val newParamSyms = (vparams, formals).zipped map {(p, tp) => methodSym.newValueParameter(p.name, focusPos(p.pos), SYNTHETIC) setInfo tp } @@ -2209,7 +2216,7 @@ 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 - val applyMethod = { + 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) @@ -2224,25 +2231,59 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val (selector1, selectorTp, casesAdapted, resTp, doTranslation) = methodBodyTyper.prepareTranslateMatch(selector, cases, mode, ptRes) - val formalTypes = paramSyms map (_.tpe) - val parents = - if (isPartial) List(appliedType(AbstractPartialFunctionClass.typeConstructor, List(formalTypes.head, resTp)), SerializableClass.tpe) - else List(appliedType(AbstractFunctionClass(arity).typeConstructor, formalTypes :+ resTp), SerializableClass.tpe) + val methFormals = paramSyms map (_.tpe) + val parents = List(appliedType(AbstractFunctionClass(arity).typeConstructor, methFormals :+ resTp), SerializableClass.tpe) anonClass setInfo ClassInfoType(parents, newScope, anonClass) 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)) + DefDef(methodSym, methodBodyTyper.translateMatch(selector1, selectorTp, casesAdapted, resTp, doTranslation)) + } + } + + // def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = + 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) + val methodSym = anonClass.newMethod(nme.applyOrElse, tree.pos, FINAL | OVERRIDE) + + // create the parameter that corresponds to the function's parameter + val List(argTp) = deriveFormals + val A1 = methodSym newTypeParameter(newTypeName("A1")) setInfo TypeBounds.upper(argTp) + val (List(x), selector) = mkParams(methodSym, List(A1.tpe)) + + if (selector eq EmptyTree) EmptyTree + else { + // applyOrElse's default parameter: + val B1 = methodSym newTypeParameter(newTypeName("B1")) setInfo TypeBounds.empty //lower(resTp) + val default = methodSym newValueParameter(newTermName("default"), focusPos(tree.pos), SYNTHETIC) setInfo functionType(List(A1.tpe), B1.tpe) + + val paramSyms = List(x, default) + methodSym setInfoAndEnter polyType(List(A1, B1), MethodType(paramSyms, B1.tpe)) + + val methodBodyTyper = newTyper(context.makeNewScope(context.tree, methodSym)) // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it) + paramSyms foreach (methodBodyTyper.context.scope enter _) + + val (selector1, selectorTp, casesAdapted, resTp, doTranslation) = methodBodyTyper.prepareTranslateMatch(selector, cases, mode, ptRes) + + anonClass setInfo ClassInfoType(parents(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) + + // use applyOrElse's first parameter since the scrut's type has been widened + def doDefault(scrut_ignored: Tree) = REF(default) APPLY (REF(x)) - val body = methodBodyTyper.translateMatch(selector1, selectorTp, casesAdapted, resTp, doTranslation, if (isPartial) Some(missingCase) else None) + val body = methodBodyTyper.translateMatch(selector1, selectorTp, casesAdapted, B1.tpe, doTranslation, Some(doDefault)) DefDef(methodSym, body) } } 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 { @@ -2257,7 +2298,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } } - val members = if (!isPartial) List(applyMethod) else List(applyMethod, isDefinedAtMethod) + val members = if (!isPartial) List(applyMethod) else List(applyOrElseMethodDef, isDefinedAtMethod) 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) } @@ -4142,7 +4183,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { var defEntry: ScopeEntry = null // the scope entry of defSym, if defined in a local scope var cx = startingIdentContext - while (defSym == NoSymbol && cx != NoContext) { + while (defSym == NoSymbol && cx != NoContext && (cx.scope ne null)) { // cx.scope eq null arises during FixInvalidSyms in Duplicators // !!! Shouldn't the argument to compileSourceFor be cx, not context? // I can't tell because those methods do nothing in the standard compiler, // presumably they are overridden in the IDE. diff --git a/src/library/scala/Function.scala b/src/library/scala/Function.scala index 4a10b65735..9fa56a332f 100644 --- a/src/library/scala/Function.scala +++ b/src/library/scala/Function.scala @@ -29,6 +29,7 @@ object Function { /** Turns a function `A => Option[B]` into a `PartialFunction[A, B]`. * + * TODO: check if the paragraph below is still correct * '''Important note''': this transformation implies the original function * will be called 2 or more times on each logical invocation, because the * only way to supply an implementation of `isDefinedAt` is to call the @@ -39,11 +40,7 @@ object Function { * f returns `Some(_)` and undefined where `f` returns `None`. * @see [[scala.PartialFunction#lift]] */ - def unlift[T, R](f: T => Option[R]): PartialFunction[T, R] = new runtime.AbstractPartialFunction[T, R] { - def apply(x: T): R = f(x).get - def _isDefinedAt(x: T): Boolean = f(x).isDefined - override def lift: T => Option[R] = f - } + def unlift[T, R](f: T => Option[R]): PartialFunction[T, R] = PartialFunction.unlifted(f) /** Uncurrying for functions of arity 2. This transforms a unary function * returning another unary function into a function of arity 2. diff --git a/src/library/scala/PartialFunction.scala b/src/library/scala/PartialFunction.scala index 3c5d6d0d23..7154b8da34 100644 --- a/src/library/scala/PartialFunction.scala +++ b/src/library/scala/PartialFunction.scala @@ -8,6 +8,7 @@ package scala + /** A partial function of type `PartialFunction[A, B]` is a unary function * where the domain does not necessarily include all values of type `A`. * The function `isDefinedAt` allows to test dynamically if a value is in @@ -43,10 +44,11 @@ package scala * }}} * * - * @author Martin Odersky + * @author Martin Odersky, Pavel Pavlov, Adriaan Moors * @version 1.0, 16/07/2003 */ -trait PartialFunction[-A, +B] extends (A => B) { +trait PartialFunction[-A, +B] extends (A => B) { self => + import PartialFunction._ /** Checks if a value is contained in the function's domain. * @@ -55,10 +57,6 @@ trait PartialFunction[-A, +B] extends (A => B) { */ def isDefinedAt(x: A): Boolean - //protected def missingCase[A1 <: A, B1 >: B]: PartialFunction[A1, B1] = PartialFunction.empty - - protected def missingCase(x: A): B = throw new MatchError(x) - /** Composes this partial function with a fallback partial function which * gets applied where this partial function is not defined. * @@ -70,16 +68,8 @@ trait PartialFunction[-A, +B] extends (A => B) { * takes `x` to `this(x)` where `this` is defined, and to `that(x)` where it is not. */ def orElse[A1 <: A, B1 >: B](that: PartialFunction[A1, B1]) : PartialFunction[A1, B1] = - new runtime.AbstractPartialFunction[A1, B1] { - def _isDefinedAt(x: A1): Boolean = - PartialFunction.this.isDefinedAt(x) || that.isDefinedAt(x) - def apply(x: A1): B1 = - if (PartialFunction.this.isDefinedAt(x)) PartialFunction.this.apply(x) - else that.apply(x) - } - - def orElseFast[A1 <: A, B1 >: B](that: PartialFunction[A1, B1]) : PartialFunction[A1, B1] = - orElse(that) + new OrElse[A1, B1] (this, that) + //TODO: why not overload it with orElse(that: F1): F1? /** Composes this partial function with a transformation function that * gets applied to results of this partial function. @@ -88,9 +78,9 @@ trait PartialFunction[-A, +B] extends (A => B) { * @return a partial function with the same domain as this partial function, which maps * arguments `x` to `k(this(x))`. */ - override def andThen[C](k: B => C) : PartialFunction[A, C] = new runtime.AbstractPartialFunction[A, C] { - def _isDefinedAt(x: A): Boolean = PartialFunction.this.isDefinedAt(x) - def apply(x: A): C = k(PartialFunction.this.apply(x)) + override def andThen[C](k: B => C) : PartialFunction[A, C] = new PartialFunction[A, C] { + def isDefinedAt(x: A): Boolean = self isDefinedAt x + def apply(x: A): C = k(self(x)) } /** Turns this partial function into an plain function returning an `Option` result. @@ -98,9 +88,30 @@ trait PartialFunction[-A, +B] extends (A => B) { * @return a function that takes an argument `x` to `Some(this(x))` if `this` * is defined for `x`, and to `None` otherwise. */ - def lift: A => Option[B] = new (A => Option[B]) { - def apply(x: A): Option[B] = if (isDefinedAt(x)) Some(PartialFunction.this.apply(x)) else None - } + def lift: A => Option[B] = new Lifted(this) + + /** + * TODO: comment + * @since 2.10 + */ + def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = + if (isDefinedAt(x)) apply(x) else default(x) + + /** + * TODO: comment + * @since 2.10 + */ + def run[U](x: A)(action: B => U): Boolean = + applyOrElse(x, fallbackToken) match { + case FallbackToken => false + case z => action(z); true + } + + /** + * TODO: comment + * @since 2.10 + */ + def runWith[U](action: B => U): A => Boolean = { x => run(x)(action) } } /** A few handy operations which leverage the extra bit of information @@ -119,14 +130,73 @@ trait PartialFunction[-A, +B] extends (A => B) { * @since 2.8 */ object PartialFunction { - private[this] final val empty_pf: PartialFunction[Any, Nothing] = new runtime.AbstractPartialFunction[Any, Nothing] { - def _isDefinedAt(x: Any) = false - override def isDefinedAt(x: Any) = false - def apply(x: Any): Nothing = throw new MatchError(x) - override def orElse[A1, B1](that: PartialFunction[A1, B1]): PartialFunction[A1, B1] = that - override def orElseFast[A1, B1](that: PartialFunction[A1, B1]): PartialFunction[A1, B1] = that - override def lift = (x: Any) => None + /** Composite function produced by `PartialFunction#orElse` method + */ + private final class OrElse[-A, +B] (f1: PartialFunction[A, B], f2: PartialFunction[A, B]) extends PartialFunction[A, B] { + def isDefinedAt(x: A) = f1.isDefinedAt(x) || f2.isDefinedAt(x) + + def apply(x: A): B = f1.applyOrElse(x, f2) + + override def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = + f1.applyOrElse(x, fallbackToken) match { + case FallbackToken => f2.applyOrElse(x, default) + case z => z + } + + override def orElse[A1 <: A, B1 >: B](that: PartialFunction[A1, B1]) = + new OrElse[A1, B1] (f1, f2 orElse that) + + override def andThen[C](k: B => C) = + new OrElse[A, C] (f1 andThen k, f2 andThen k) } + + private[scala] lazy val FallbackToken: PartialFunction[Any, PartialFunction[Any, Nothing]] = { case _ => FallbackToken.asInstanceOf[PartialFunction[Any, Nothing]] } + private[scala] final def fallbackToken[B] = FallbackToken.asInstanceOf[PartialFunction[Any, B]] + //TODO: check generated code for PF literal here + + private[scala] final class Lifted[-A, +B] (val pf: PartialFunction[A, B]) + extends runtime.AbstractFunction1[A, Option[B]] { + + def apply(x: A): Option[B] = pf.applyOrElse(x, fallbackToken) match { + case FallbackToken => None + case z => Some(z) + } + } + + private final class Unlifted[A, B] (f: A => Option[B]) extends runtime.AbstractPartialFunction[A, B] { + def isDefinedAt(x: A): Boolean = f(x).isDefined + override def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = + f(x) getOrElse default(x) //TODO: check generated code and inline getOrElse if needed + override def lift = f + } + + private[scala] def unlifted[A, B](f: A => Option[B]): PartialFunction[A, B] = f match { + case lf: Lifted[A, B] => lf.pf + case ff => new Unlifted(ff) + } + + /** Converts ordinary function to partial one + * @since 2.10 + */ + //TODO: check generated code for PF literal here + def apply[A, B](f: A => B): PartialFunction[A, B] = { case x => f(x) } + + private[this] final val constFalse: Any => Boolean = { _ => false} + + private[this] final val empty_pf: PartialFunction[Any, Nothing] = new PartialFunction[Any, Nothing] { + def isDefinedAt(x: Any) = false + def apply(x: Any) = throw new MatchError(x) + override def orElse[A1, B1](that: PartialFunction[A1, B1]) = that + override def andThen[C](k: Nothing => C) = this + override val lift = (x: Any) => None + override def run[U](x: Any)(action: Nothing => U) = false + override def runWith[U](action: Nothing => U) = constFalse + } + + /** + * TODO: comment + * @since 2.10 + */ def empty[A, B] : PartialFunction[A, B] = empty_pf /** Creates a Boolean test based on a value and a partial function. @@ -137,8 +207,7 @@ object PartialFunction { * @param pf the partial function * @return true, iff `x` is in the domain of `pf` and `pf(x) == true`. */ - def cond[T](x: T)(pf: PartialFunction[T, Boolean]): Boolean = - (pf isDefinedAt x) && pf(x) + def cond[T](x: T)(pf: PartialFunction[T, Boolean]): Boolean = pf.applyOrElse(x, constFalse) /** Transforms a PartialFunction[T, U] `pf` into Function1[T, Option[U]] `f` * whose result is `Some(x)` if the argument is in `pf`'s domain and `None` @@ -150,6 +219,5 @@ object PartialFunction { * @param pf the PartialFunction[T, U] * @return `Some(pf(x))` if `pf isDefinedAt x`, `None` otherwise. */ - def condOpt[T,U](x: T)(pf: PartialFunction[T, U]): Option[U] = - if (pf isDefinedAt x) Some(pf(x)) else None + def condOpt[T,U](x: T)(pf: PartialFunction[T, U]): Option[U] = pf.lift(x) } diff --git a/src/library/scala/runtime/AbstractPartialFunction.scala b/src/library/scala/runtime/AbstractPartialFunction.scala index cbe778f09b..2e435d8a7e 100644 --- a/src/library/scala/runtime/AbstractPartialFunction.scala +++ b/src/library/scala/runtime/AbstractPartialFunction.scala @@ -8,45 +8,61 @@ package scala.runtime -import scala.annotation.unchecked.uncheckedVariance - -/** This class provides a default implementation of partial functions - * that is used for all partial function literals. - * It contains an optimized `orElse` method which supports - * chained `orElse` in linear time, and with no slow-down - * if the `orElse` part is not needed. - * The implementation of `orElse` works by cloning the abstract function object - * and modifying a private `fallBack` variable that encodes the `getorElse` part. +/** `AbstractPartialFunction` reformulates all operations of its supertrait `PartialFunction` in terms of `isDefinedAt` and `applyOrElse`. + * + * This allows more efficient implementations in many cases: + * - optimized `orElse` method supports chained `orElse` in linear time, + * and with no slow-down if the `orElse` part is not needed. + * - optimized `lift` method helps to avoid double evaluation of pattern matchers & guards + * of partial function literals. + * + * This trait is used as a basis for implementation of all partial function literals + * with non-exhaustive matchers. + * + * Use of `AbstractPartialFunction` instead of `PartialFunction` as a base trait for + * user-defined partial functions may result in better performance + * and more predictable behavior w.r.t. side effects. + * + * @author Pavel Pavlov + * @since 2.10 */ -abstract class AbstractPartialFunction[-T1, +R] - extends AbstractFunction1[T1, R] - with PartialFunction[T1, R] - with Cloneable { - - private var fallBackField: PartialFunction[T1 @uncheckedVariance, R @uncheckedVariance] = _ +abstract class AbstractPartialFunction[@specialized(scala.Int, scala.Long, scala.Float, scala.Double, scala.AnyRef) -T1, @specialized(scala.Unit, scala.Boolean, scala.Int, scala.Float, scala.Long, scala.Double, scala.AnyRef) +R] extends Function1[T1, R] with PartialFunction[T1, R] { self => + // this method must be overridden for better performance, + // for backwards compatibility, fall back to the one inherited from PartialFunction + // this assumes the old-school partial functions override the apply method, though + // override def applyOrElse[A1 <: T1, B1 >: R](x: A1, default: A1 => B1): B1 = ??? - def fallBack: PartialFunction[T1, R] = synchronized { - if (fallBackField eq null) fallBackField = PartialFunction.empty - fallBackField - } + // probably okay to make final since classes compiled before have overridden against the old version of AbstractPartialFunction + // let's not make it final so as not to confuse anyone + /*final*/ def apply(x: T1): R = applyOrElse(x, PartialFunction.empty) - override protected def missingCase(x: T1): R = fallBack(x) - - // Question: Need to ensure that fallBack is overwritten before any access - // Is the `synchronized` here the right thing to achieve this? - // Is there a cheaper way? - override def orElse[A1 <: T1, B1 >: R](that: PartialFunction[A1, B1]) : PartialFunction[A1, B1] = { - val result = this.clone.asInstanceOf[AbstractPartialFunction[A1, B1]] - result.synchronized { - result.fallBackField = if (this.fallBackField eq null) that else this.fallBackField orElse that - result + override final def andThen[C](k: R => C) : PartialFunction[T1, C] = + new AbstractPartialFunction[T1, C] { + def isDefinedAt(x: T1): Boolean = self.isDefinedAt(x) + override def applyOrElse[A1 <: T1, C1 >: C](x: A1, default: A1 => C1): C1 = + self.applyOrElse(x, PartialFunction.fallbackToken) match { + case PartialFunction.FallbackToken => default(x) + case z => k(z) + } } - } - - def isDefinedAt(x: T1): scala.Boolean = _isDefinedAt(x) || fallBack.isDefinedAt(x) - def _isDefinedAt(x: T1): scala.Boolean + // TODO: remove + protected def missingCase(x: T1): R = throw new MatchError(x) } - +/** `AbstractTotalFunction` is a partial function whose `isDefinedAt` method always returns `true`. + * + * This class is used as base class for partial function literals with + * certainly exhaustive pattern matchers. + * + * @author Pavel Pavlov + * @since 2.10 + */ +abstract class AbstractTotalFunction[@specialized(scala.Int, scala.Long, scala.Float, scala.Double, scala.AnyRef) -T1, @specialized(scala.Unit, scala.Boolean, scala.Int, scala.Float, scala.Long, scala.Double, scala.AnyRef) +R] extends Function1[T1, R] with PartialFunction[T1, R] { + final def isDefinedAt(x: T1): Boolean = true + override final def applyOrElse[A1 <: T1, B1 >: R](x: A1, default: A1 => B1): B1 = apply(x) + override final def orElse[A1 <: T1, B1 >: R](that: PartialFunction[A1, B1]): PartialFunction[A1, B1] = this + //TODO: check generated code for PF literal here + override final def andThen[C](k: R => C): PartialFunction[T1, C] = { case x => k(apply(x)) } +} diff --git a/src/library/scala/util/control/Exception.scala b/src/library/scala/util/control/Exception.scala index 5e3f8b6451..20a179a884 100644 --- a/src/library/scala/util/control/Exception.scala +++ b/src/library/scala/util/control/Exception.scala @@ -230,8 +230,5 @@ object Exception { classes exists (_ isAssignableFrom x.getClass) private def pfFromExceptions(exceptions: Class[_]*): PartialFunction[Throwable, Nothing] = - new scala.runtime.AbstractPartialFunction[Throwable, Nothing] { - def apply(x: Throwable) = throw x - def _isDefinedAt(x: Throwable) = wouldMatch(x, exceptions) - } + { case x if wouldMatch(x, exceptions) => throw x } } |