diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/actors-migration/scala/actors/MigrationSystem.scala | 36 | ||||
-rw-r--r-- | src/actors-migration/scala/actors/Pattern.scala | 25 | ||||
-rw-r--r-- | src/actors-migration/scala/actors/Props.scala | 13 | ||||
-rw-r--r-- | src/actors-migration/scala/actors/StashingActor.scala | 255 | ||||
-rw-r--r-- | src/actors-migration/scala/actors/Timeout.scala | 39 | ||||
-rw-r--r-- | src/actors/scala/actors/ActorRef.scala | 119 | ||||
-rw-r--r-- | src/actors/scala/actors/ActorTask.scala | 1 | ||||
-rw-r--r-- | src/actors/scala/actors/InternalActor.scala | 53 | ||||
-rw-r--r-- | src/actors/scala/actors/MQueue.scala | 14 | ||||
-rw-r--r-- | src/actors/scala/actors/Reactor.scala | 10 | ||||
-rw-r--r-- | src/actors/scala/actors/ReplyReactor.scala | 7 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/AnnotationVisitor.java (renamed from src/compiler/scala/tools/asm/AnnotationVisitor.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/AnnotationWriter.java (renamed from src/compiler/scala/tools/asm/AnnotationWriter.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/Attribute.java (renamed from src/compiler/scala/tools/asm/Attribute.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/ByteVector.java (renamed from src/compiler/scala/tools/asm/ByteVector.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/ClassReader.java (renamed from src/compiler/scala/tools/asm/ClassReader.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/ClassVisitor.java (renamed from src/compiler/scala/tools/asm/ClassVisitor.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/ClassWriter.java (renamed from src/compiler/scala/tools/asm/ClassWriter.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/CustomAttr.java (renamed from src/compiler/scala/tools/asm/CustomAttr.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/Edge.java (renamed from src/compiler/scala/tools/asm/Edge.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/FieldVisitor.java (renamed from src/compiler/scala/tools/asm/FieldVisitor.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/FieldWriter.java (renamed from src/compiler/scala/tools/asm/FieldWriter.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/Frame.java (renamed from src/compiler/scala/tools/asm/Frame.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/Handle.java (renamed from src/compiler/scala/tools/asm/Handle.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/Handler.java (renamed from src/compiler/scala/tools/asm/Handler.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/Item.java (renamed from src/compiler/scala/tools/asm/Item.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/Label.java (renamed from src/compiler/scala/tools/asm/Label.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/MethodVisitor.java (renamed from src/compiler/scala/tools/asm/MethodVisitor.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/MethodWriter.java (renamed from src/compiler/scala/tools/asm/MethodWriter.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/Opcodes.java (renamed from src/compiler/scala/tools/asm/Opcodes.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/Type.java (renamed from src/compiler/scala/tools/asm/Type.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/signature/SignatureReader.java (renamed from src/compiler/scala/tools/asm/signature/SignatureReader.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/signature/SignatureVisitor.java (renamed from src/compiler/scala/tools/asm/signature/SignatureVisitor.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/signature/SignatureWriter.java (renamed from src/compiler/scala/tools/asm/signature/SignatureWriter.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/AbstractInsnNode.java (renamed from src/compiler/scala/tools/asm/tree/AbstractInsnNode.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/AnnotationNode.java (renamed from src/compiler/scala/tools/asm/tree/AnnotationNode.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/ClassNode.java (renamed from src/compiler/scala/tools/asm/tree/ClassNode.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/FieldInsnNode.java (renamed from src/compiler/scala/tools/asm/tree/FieldInsnNode.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/FieldNode.java (renamed from src/compiler/scala/tools/asm/tree/FieldNode.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/FrameNode.java (renamed from src/compiler/scala/tools/asm/tree/FrameNode.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/IincInsnNode.java (renamed from src/compiler/scala/tools/asm/tree/IincInsnNode.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/InnerClassNode.java (renamed from src/compiler/scala/tools/asm/tree/InnerClassNode.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/InsnList.java (renamed from src/compiler/scala/tools/asm/tree/InsnList.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/InsnNode.java (renamed from src/compiler/scala/tools/asm/tree/InsnNode.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/IntInsnNode.java (renamed from src/compiler/scala/tools/asm/tree/IntInsnNode.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/InvokeDynamicInsnNode.java (renamed from src/compiler/scala/tools/asm/tree/InvokeDynamicInsnNode.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/JumpInsnNode.java (renamed from src/compiler/scala/tools/asm/tree/JumpInsnNode.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/LabelNode.java (renamed from src/compiler/scala/tools/asm/tree/LabelNode.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/LdcInsnNode.java (renamed from src/compiler/scala/tools/asm/tree/LdcInsnNode.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/LineNumberNode.java (renamed from src/compiler/scala/tools/asm/tree/LineNumberNode.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/LocalVariableNode.java (renamed from src/compiler/scala/tools/asm/tree/LocalVariableNode.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/LookupSwitchInsnNode.java (renamed from src/compiler/scala/tools/asm/tree/LookupSwitchInsnNode.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/MethodInsnNode.java (renamed from src/compiler/scala/tools/asm/tree/MethodInsnNode.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/MethodNode.java (renamed from src/compiler/scala/tools/asm/tree/MethodNode.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/MultiANewArrayInsnNode.java (renamed from src/compiler/scala/tools/asm/tree/MultiANewArrayInsnNode.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/TableSwitchInsnNode.java (renamed from src/compiler/scala/tools/asm/tree/TableSwitchInsnNode.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/TryCatchBlockNode.java (renamed from src/compiler/scala/tools/asm/tree/TryCatchBlockNode.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/TypeInsnNode.java (renamed from src/compiler/scala/tools/asm/tree/TypeInsnNode.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/VarInsnNode.java (renamed from src/compiler/scala/tools/asm/tree/VarInsnNode.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/analysis/Analyzer.java (renamed from src/compiler/scala/tools/asm/tree/analysis/Analyzer.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/analysis/AnalyzerException.java (renamed from src/compiler/scala/tools/asm/tree/analysis/AnalyzerException.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/analysis/BasicInterpreter.java (renamed from src/compiler/scala/tools/asm/tree/analysis/BasicInterpreter.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/analysis/BasicValue.java (renamed from src/compiler/scala/tools/asm/tree/analysis/BasicValue.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/analysis/BasicVerifier.java (renamed from src/compiler/scala/tools/asm/tree/analysis/BasicVerifier.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/analysis/Frame.java (renamed from src/compiler/scala/tools/asm/tree/analysis/Frame.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/analysis/Interpreter.java (renamed from src/compiler/scala/tools/asm/tree/analysis/Interpreter.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/analysis/SimpleVerifier.java (renamed from src/compiler/scala/tools/asm/tree/analysis/SimpleVerifier.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/analysis/SmallSet.java (renamed from src/compiler/scala/tools/asm/tree/analysis/SmallSet.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/analysis/SourceInterpreter.java (renamed from src/compiler/scala/tools/asm/tree/analysis/SourceInterpreter.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/analysis/SourceValue.java (renamed from src/compiler/scala/tools/asm/tree/analysis/SourceValue.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/analysis/Subroutine.java (renamed from src/compiler/scala/tools/asm/tree/analysis/Subroutine.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/tree/analysis/Value.java (renamed from src/compiler/scala/tools/asm/tree/analysis/Value.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/util/ASMifiable.java (renamed from src/compiler/scala/tools/asm/util/ASMifiable.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/util/ASMifier.java (renamed from src/compiler/scala/tools/asm/util/ASMifier.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/util/CheckAnnotationAdapter.java (renamed from src/compiler/scala/tools/asm/util/CheckAnnotationAdapter.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/util/CheckClassAdapter.java (renamed from src/compiler/scala/tools/asm/util/CheckClassAdapter.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/util/CheckFieldAdapter.java (renamed from src/compiler/scala/tools/asm/util/CheckFieldAdapter.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/util/CheckMethodAdapter.java (renamed from src/compiler/scala/tools/asm/util/CheckMethodAdapter.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/util/CheckSignatureAdapter.java (renamed from src/compiler/scala/tools/asm/util/CheckSignatureAdapter.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/util/Printer.java (renamed from src/compiler/scala/tools/asm/util/Printer.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/util/SignatureChecker.java (renamed from src/compiler/scala/tools/asm/util/SignatureChecker.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/util/Textifiable.java (renamed from src/compiler/scala/tools/asm/util/Textifiable.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/util/Textifier.java (renamed from src/compiler/scala/tools/asm/util/Textifier.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/util/TraceAnnotationVisitor.java (renamed from src/compiler/scala/tools/asm/util/TraceAnnotationVisitor.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/util/TraceClassVisitor.java (renamed from src/compiler/scala/tools/asm/util/TraceClassVisitor.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/util/TraceFieldVisitor.java (renamed from src/compiler/scala/tools/asm/util/TraceFieldVisitor.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/util/TraceMethodVisitor.java (renamed from src/compiler/scala/tools/asm/util/TraceMethodVisitor.java) | 0 | ||||
-rw-r--r-- | src/asm/scala/tools/asm/util/TraceSignatureVisitor.java (renamed from src/compiler/scala/tools/asm/util/TraceSignatureVisitor.java) | 0 | ||||
-rw-r--r-- | src/build/maven/maven-deploy.xml | 3 | ||||
-rw-r--r-- | src/build/maven/scala-actors-migration-pom.xml | 66 | ||||
-rw-r--r-- | src/build/pack.xml | 6 | ||||
-rw-r--r-- | src/compiler/scala/reflect/internal/Constants.scala | 1 | ||||
-rw-r--r-- | src/compiler/scala/reflect/internal/Definitions.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/reflect/internal/InfoTransformers.scala | 4 | ||||
-rw-r--r-- | src/compiler/scala/reflect/internal/Phase.scala | 10 | ||||
-rw-r--r-- | src/compiler/scala/reflect/internal/util/Origins.scala | 86 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/Global.scala | 14 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/TreeGen.scala | 1 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/Trees.scala | 4 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/parser/Parsers.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/icode/Members.scala | 20 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala | 8 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala | 9 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/doc/html/page/Index.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/doc/html/page/Template.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/doc/html/resource/lib/index.css | 298 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js | 89 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js | 51 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala | 9 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/AddInterfaces.scala | 3 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/InfoTransform.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala | 17 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/UnCurry.scala | 28 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala | 44 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Contexts.scala | 12 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Duplicators.scala | 28 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Implicits.scala | 8 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Infer.scala | 58 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Namers.scala | 28 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala | 29 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala | 1336 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/RefChecks.scala | 14 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala | 67 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 21 | ||||
-rw-r--r-- | src/library/scala/collection/mutable/FlatHashTable.scala | 8 | ||||
-rw-r--r-- | src/library/scala/collection/mutable/HashTable.scala | 20 | ||||
-rw-r--r-- | src/library/scala/collection/mutable/PriorityQueue.scala | 26 | ||||
-rw-r--r-- | src/library/scala/collection/mutable/StackProxy.scala | 5 | ||||
-rw-r--r-- | src/library/scala/collection/package.scala | 4 | ||||
-rw-r--r-- | src/library/scala/concurrent/Future.scala | 102 | ||||
-rw-r--r-- | src/library/scala/concurrent/Promise.scala | 14 | ||||
-rw-r--r-- | src/library/scala/concurrent/impl/Future.scala | 2 | ||||
-rw-r--r-- | src/library/scala/concurrent/impl/Promise.scala | 21 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/PartestTask.scala | 11 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/AntRunner.scala | 1 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/ConsoleFileManager.scala | 9 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/DirectRunner.scala | 4 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/FileManager.scala | 1 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/ReflectiveRunner.scala | 4 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/SBTRunner.scala | 2 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/Worker.scala | 1 |
141 files changed, 2476 insertions, 713 deletions
diff --git a/src/actors-migration/scala/actors/MigrationSystem.scala b/src/actors-migration/scala/actors/MigrationSystem.scala new file mode 100644 index 0000000000..ffc93d9c6f --- /dev/null +++ b/src/actors-migration/scala/actors/MigrationSystem.scala @@ -0,0 +1,36 @@ +package scala.actors + +import scala.collection._ + +object MigrationSystem { + + private[actors] val contextStack = new ThreadLocal[immutable.Stack[Boolean]] { + override def initialValue() = immutable.Stack[Boolean]() + } + + private[this] def withCleanContext(block: => ActorRef): ActorRef = { + // push clean marker + val old = contextStack.get + contextStack.set(old.push(true)) + try { + val instance = block + + if (instance eq null) + throw new Exception("Actor instance passed to actorOf can't be 'null'") + + instance + } finally { + val stackAfter = contextStack.get + if (stackAfter.nonEmpty) + contextStack.set(if (!stackAfter.head) stackAfter.pop.pop else stackAfter.pop) + } + } + + def actorOf(props: Props): ActorRef = withCleanContext { + val creator = props.creator() + val r = new InternalActorRef(creator) + creator.start() + r + } + +}
\ No newline at end of file diff --git a/src/actors-migration/scala/actors/Pattern.scala b/src/actors-migration/scala/actors/Pattern.scala new file mode 100644 index 0000000000..97dbd2cccd --- /dev/null +++ b/src/actors-migration/scala/actors/Pattern.scala @@ -0,0 +1,25 @@ +package scala.actors + +import scala.concurrent.util.Duration + +object pattern { + + implicit def askSupport(ar: ActorRef): AskableActorRef = + new AskableActorRef(ar) +} + +/** + * ActorRef with support for ask(?) operation. + */ +class AskableActorRef(val ar: ActorRef) extends ActorRef { + + def !(message: Any)(implicit sender: ActorRef = null): Unit = ar.!(message)(sender) + + def ?(message: Any)(timeout: Timeout): Future[Any] = ar.?(message, timeout.duration) + + private[actors] def ?(message: Any, timeout: Duration): Future[Any] = ar.?(message, timeout) + + def forward(message: Any) = ar.forward(message) + + private[actors] def localActor: AbstractActor = ar.localActor +}
\ No newline at end of file diff --git a/src/actors-migration/scala/actors/Props.scala b/src/actors-migration/scala/actors/Props.scala new file mode 100644 index 0000000000..891e23213a --- /dev/null +++ b/src/actors-migration/scala/actors/Props.scala @@ -0,0 +1,13 @@ +package scala.actors + +/** + * ActorRef configuration object. It represents the minimal subset of Akka Props class. + */ +case class Props(creator: () ⇒ InternalActor, dispatcher: String) { + + /** + * Returns a new Props with the specified creator set + */ + def withCreator(c: ⇒ InternalActor) = copy(creator = () ⇒ c) + +} diff --git a/src/actors-migration/scala/actors/StashingActor.scala b/src/actors-migration/scala/actors/StashingActor.scala new file mode 100644 index 0000000000..37300f9d63 --- /dev/null +++ b/src/actors-migration/scala/actors/StashingActor.scala @@ -0,0 +1,255 @@ +package scala.actors + +import scala.collection._ +import scala.concurrent.util.Duration +import java.util.concurrent.TimeUnit + +object StashingActor extends Combinators { + implicit def mkBody[A](body: => A) = new InternalActor.Body[A] { + def andThen[B](other: => B): Unit = Actor.rawSelf.seq(body, other) + } +} + +@deprecated("Scala Actors are being removed from the standard library. Please refer to the migration guide.", "2.10") +trait StashingActor extends InternalActor { + type Receive = PartialFunction[Any, Unit] + + // checks if StashingActor is created within the actorOf block + creationCheck; + + private[actors] val ref = new InternalActorRef(this) + + val self: ActorRef = ref + + protected[this] val context: ActorContext = new ActorContext(this) + + @volatile + private var myTimeout: Option[Long] = None + + private val stash = new MQueue[Any]("Stash") + + /** + * Migration notes: + * this method replaces receiveWithin, receive and react methods from Scala Actors. + */ + def receive: Receive + + /** + * User overridable callback. + * <p/> + * Is called when an Actor is started by invoking 'actor'. + */ + def preStart() {} + + /** + * User overridable callback. + * <p/> + * Is called when 'actor.stop()' is invoked. + */ + def postStop() {} + + /** + * User overridable callback. + * <p/> + * Is called on a crashed Actor right BEFORE it is restarted to allow clean + * up of resources before Actor is terminated. + * By default it calls postStop() + */ + def preRestart(reason: Throwable, message: Option[Any]) { postStop() } + + /** + * Changes the Actor's behavior to become the new 'Receive' (PartialFunction[Any, Unit]) handler. + * Puts the behavior on top of the hotswap stack. + * If "discardOld" is true, an unbecome will be issued prior to pushing the new behavior to the stack + */ + private def become(behavior: Receive, discardOld: Boolean = true) { + if (discardOld) unbecome() + behaviorStack = behaviorStack.push(wrapWithSystemMessageHandling(behavior)) + } + + /** + * Reverts the Actor behavior to the previous one in the hotswap stack. + */ + private def unbecome() { + // never unbecome the initial behavior + if (behaviorStack.size > 1) + behaviorStack = behaviorStack.pop + } + + /** + * User overridable callback. + * <p/> + * Is called when a message isn't handled by the current behavior of the actor + * by default it does: EventHandler.warning(self, message) + */ + def unhandled(message: Any) { + message match { + case Terminated(dead) ⇒ throw new DeathPactException(dead) + case _ ⇒ System.err.println("Unhandeled message " + message) + } + } + + protected def sender: ActorRef = new OutputChannelRef(internalSender) + + override def act(): Unit = internalAct() + + override def start(): StashingActor = { + super.start() + this + } + + override def receive[R](f: PartialFunction[Any, R]): R + + /* + * Internal implementation. + */ + + private[actors] var behaviorStack = immutable.Stack[PartialFunction[Any, Unit]]() + + /* + * Checks that StashingActor can be created only by MigrationSystem.actorOf method. + */ + private[this] def creationCheck = { + + // creation check (see ActorRef) + val context = MigrationSystem.contextStack.get + if (context.isEmpty) + throw new RuntimeException("In order to create StashingActor one must use actorOf.") + else { + if (!context.head) + throw new RuntimeException("Only one actor can be created per actorOf call.") + else + MigrationSystem.contextStack.set(context.push(false)) + } + + } + + private[actors] override def preAct() { + preStart() + } + + /** + * Adds message to a stash, to be processed later. Stashed messages can be fed back into the $actor's + * mailbox using <code>unstashAll()</code>. + * + * Temporarily stashing away messages that the $actor does not (yet) handle simplifies implementing + * certain messaging protocols. + */ + final def stash(msg: Any): Unit = { + stash.append(msg, null) + } + + final def unstashAll(): Unit = { + mailbox.prepend(stash) + stash.clear() + } + + /** + * Wraps any partial function with Exit message handling. + */ + private[actors] def wrapWithSystemMessageHandling(pf: PartialFunction[Any, Unit]): PartialFunction[Any, Unit] = { + + def swapExitHandler(pf: PartialFunction[Any, Unit]) = new PartialFunction[Any, Unit] { + def swapExit(v: Any) = v match { + case Exit(from, reason) => + Terminated(new InternalActorRef(from.asInstanceOf[InternalActor])) + case v => v + } + + def isDefinedAt(v: Any) = pf.isDefinedAt(swapExit(v)) + def apply(v: Any) = pf(swapExit(v)) + } + + swapExitHandler(pf orElse { + case m => unhandled(m) + }) + } + + /** + * Method that models the behavior of Akka actors. + */ + private[actors] def internalAct() { + trapExit = true + behaviorStack = behaviorStack.push(wrapWithSystemMessageHandling(receive)) + loop { + if (myTimeout.isDefined) + reactWithin(myTimeout.get)(behaviorStack.top) + else + react(behaviorStack.top) + } + } + + private[actors] override def internalPostStop() = postStop() + + // Used for pattern matching statement similar to Akka + lazy val ReceiveTimeout = TIMEOUT + + /** + * Used to simulate Akka context behavior. Should be used only for migration purposes. + */ + protected[actors] class ActorContext(val actr: StashingActor) { + + /** + * Changes the Actor's behavior to become the new 'Receive' (PartialFunction[Any, Unit]) handler. + * Puts the behavior on top of the hotswap stack. + * If "discardOld" is true, an unbecome will be issued prior to pushing the new behavior to the stack + */ + def become(behavior: Receive, discardOld: Boolean = true) = actr.become(behavior, discardOld) + + /** + * Reverts the Actor behavior to the previous one in the hotswap stack. + */ + def unbecome() = actr.unbecome() + + /** + * Shuts down the actor its dispatcher and message queue. + */ + def stop(subject: ActorRef): Nothing = if (subject != ref) + throw new RuntimeException("Only stoping of self is allowed during migration.") + else + actr.exit() + + /** + * Registers this actor as a Monitor for the provided ActorRef. + * @return the provided ActorRef + */ + def watch(subject: ActorRef): ActorRef = { + actr.watch(subject) + subject + } + + /** + * Unregisters this actor as Monitor for the provided ActorRef. + * @return the provided ActorRef + */ + def unwatch(subject: ActorRef): ActorRef = { + actr unwatch subject + subject + } + + /** + * Defines the receiver timeout value. + */ + final def setReceiveTimeout(timeout: Duration): Unit = + actr.myTimeout = Some(timeout.toMillis) + + /** + * Gets the current receiveTimeout + */ + final def receiveTimeout: Option[Duration] = + actr.myTimeout.map(Duration(_, TimeUnit.MILLISECONDS)) + + } +} + +/** + * This message is thrown by default when an Actor does not handle termination. + */ +class DeathPactException(ref: ActorRef = null) extends Exception { + override def fillInStackTrace() = this //Don't waste cycles generating stack trace +} + +/** + * Message that is sent to a watching actor when the watched actor terminates. + */ +case class Terminated(actor: ActorRef) diff --git a/src/actors-migration/scala/actors/Timeout.scala b/src/actors-migration/scala/actors/Timeout.scala new file mode 100644 index 0000000000..0d9532a14b --- /dev/null +++ b/src/actors-migration/scala/actors/Timeout.scala @@ -0,0 +1,39 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2005-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.actors + +import scala.concurrent.util.Duration +import java.util.concurrent.TimeUnit + +case class Timeout(duration: Duration) { + def this(timeout: Long) = this(Duration(timeout, TimeUnit.MILLISECONDS)) + def this(length: Long, unit: TimeUnit) = this(Duration(length, unit)) +} + +object Timeout { + + /** + * A timeout with zero duration, will cause most requests to always timeout. + */ + val zero = new Timeout(Duration.Zero) + + /** + * A Timeout with infinite duration. Will never timeout. Use extreme caution with this + * as it may cause memory leaks, blocked threads, or may not even be supported by + * the receiver, which would result in an exception. + */ + val never = new Timeout(Duration.Inf) + + def apply(timeout: Long) = new Timeout(timeout) + def apply(length: Long, unit: TimeUnit) = new Timeout(length, unit) + + implicit def durationToTimeout(duration: Duration) = new Timeout(duration) + implicit def intToTimeout(timeout: Int) = new Timeout(timeout) + implicit def longToTimeout(timeout: Long) = new Timeout(timeout) +} diff --git a/src/actors/scala/actors/ActorRef.scala b/src/actors/scala/actors/ActorRef.scala new file mode 100644 index 0000000000..8f70b13e01 --- /dev/null +++ b/src/actors/scala/actors/ActorRef.scala @@ -0,0 +1,119 @@ +package scala.actors + +import java.util.concurrent.TimeoutException +import scala.concurrent.util.Duration + +/** + * Trait used for migration of Scala actors to Akka. + */ +@deprecated("ActorRef ought to be used only with the Actor Migration Kit.") +trait ActorRef { + + /** + * Sends a one-way asynchronous message. E.g. fire-and-forget semantics. + * <p/> + * + * If invoked from within an actor then the actor reference is implicitly passed on as the implicit 'sender' argument. + * <p/> + * + * This actor 'sender' reference is then available in the receiving actor in the 'sender' member variable, + * if invoked from within an Actor. If not then no sender is available. + * <pre> + * actor ! message + * </pre> + * <p/> + */ + def !(message: Any)(implicit sender: ActorRef = null): Unit + + /** + * Sends a message asynchronously, returning a future which may eventually hold the reply. + */ + private[actors] def ?(message: Any, timeout: Duration): Future[Any] + + /** + * Forwards the message and passes the original sender actor as the sender. + * <p/> + * Works with '!' and '?'. + */ + def forward(message: Any) + + private[actors] def localActor: AbstractActor + +} + +private[actors] class OutputChannelRef(val actor: OutputChannel[Any]) extends ActorRef { + + override private[actors] def ?(message: Any, timeout: Duration): Future[Any] = + throw new UnsupportedOperationException("Output channel does not support ?") + + /** + * Sends a one-way asynchronous message. E.g. fire-and-forget semantics. + * <p/> + * + * <p/> + * <pre> + * actor ! message + * </pre> + * <p/> + */ + def !(message: Any)(implicit sender: ActorRef = null): Unit = + if (sender != null) + actor.send(message, sender.localActor) + else + actor ! message + + override def equals(that: Any) = + that.isInstanceOf[OutputChannelRef] && that.asInstanceOf[OutputChannelRef].actor == this.actor + + private[actors] override def localActor: AbstractActor = + throw new UnsupportedOperationException("Output channel does not have an instance of the actor") + + def forward(message: Any): Unit = throw new UnsupportedOperationException("OutputChannel does not support forward.") + +} + +private[actors] class ReactorRef(override val actor: Reactor[Any]) extends OutputChannelRef(actor) { + + /** + * Forwards the message and passes the original sender actor as the sender. + * <p/> + * Works with '!' and '?'. + */ + override def forward(message: Any) = actor.forward(message) + +} + +private[actors] final class InternalActorRef(override val actor: InternalActor) extends ReactorRef(actor) { + + /** + * Sends a message asynchronously, returning a future which may eventually hold the reply. + */ + override private[actors] def ?(message: Any, timeout: Duration): Future[Any] = + Futures.future { + val dur = if (timeout.isFinite()) timeout.toMillis else (java.lang.Long.MAX_VALUE >> 2) + actor !? (dur, message) match { + case Some(x) => x + case None => new AskTimeoutException("? operation timed out.") + } + } + + override def !(message: Any)(implicit sender: ActorRef = null): Unit = + if (message == PoisonPill) + actor.stop('normal) + else if (sender != null) + actor.send(message, sender.localActor) + else + actor ! message + + private[actors] override def localActor: InternalActor = this.actor +} + +/** + * This is what is used to complete a Future that is returned from an ask/? call, + * when it times out. + */ +class AskTimeoutException(message: String, cause: Throwable) extends TimeoutException { + def this(message: String) = this(message, null: Throwable) +} + +object PoisonPill diff --git a/src/actors/scala/actors/ActorTask.scala b/src/actors/scala/actors/ActorTask.scala index bb04302238..045b00f5f2 100644 --- a/src/actors/scala/actors/ActorTask.scala +++ b/src/actors/scala/actors/ActorTask.scala @@ -51,7 +51,6 @@ private[actors] class ActorTask(actor: InternalActor, super.terminateExecution(e) () => {} } - actor.internalPostStop res } diff --git a/src/actors/scala/actors/InternalActor.scala b/src/actors/scala/actors/InternalActor.scala index c94da5b9fd..cb66021d1c 100644 --- a/src/actors/scala/actors/InternalActor.scala +++ b/src/actors/scala/actors/InternalActor.scala @@ -153,7 +153,7 @@ private[actors] trait InternalActor extends AbstractActor with InternalReplyReac val matches = f.isDefinedAt(m) senders = senders.tail matches - }) + }) if (null eq qel) { val todo = synchronized { // in mean time new stuff might have arrived @@ -317,6 +317,35 @@ private[actors] trait InternalActor extends AbstractActor with InternalReplyReac } /** + * Links <code>self</code> to actor <code>to</code>. + * + * @param to the actor to link to + * @return the parameter actor + */ + def link(to: ActorRef): ActorRef = { + this.link(to.localActor) + to + } + + /** + * Unidirectional linking. For migration purposes only + */ + private[actors] def watch(subject: ActorRef): ActorRef = { + assert(Actor.self(scheduler) == this, "link called on actor different from self") + subject.localActor linkTo this + subject + } + + /** + * Unidirectional linking. For migration purposes only + */ + private[actors] def unwatch(subject: ActorRef): ActorRef = { + assert(Actor.self(scheduler) == this, "link called on actor different from self") + subject.localActor unlinkFrom this + subject + } + + /** * Links <code>self</code> to the actor defined by <code>body</code>. * * @param body the body of the actor to link to @@ -346,17 +375,24 @@ private[actors] trait InternalActor extends AbstractActor with InternalReplyReac from unlinkFrom this } + /** + * Unlinks <code>self</code> from actor <code>from</code>. + */ + def unlink(from: ActorRef) { + unlink(from.localActor) + } + private[actors] def unlinkFrom(from: AbstractActor) = synchronized { links = links.filterNot(from.==) } - @volatile + @volatile private[actors] var _trapExit = false - + def trapExit = _trapExit - + def trapExit_=(value: Boolean) = _trapExit = value - + // guarded by this private var exitReason: AnyRef = 'normal // guarded by this @@ -445,12 +481,11 @@ private[actors] trait InternalActor extends AbstractActor with InternalReplyReac scheduler.onTerminate(this) { f } } - private[actors] def internalPostStop() = {} - private[actors] def stop(reason: AnyRef): Unit = { + private[actors] def stop(reason: AnyRef): Unit = { synchronized { shouldExit = true - exitReason = reason + exitReason = reason // resume this Actor in a way that // causes it to exit // (because shouldExit == true) @@ -464,7 +499,7 @@ private[actors] trait InternalActor extends AbstractActor with InternalReplyReac /* Here we should not throw a SuspendActorControl, since the current method is called from an actor that is in the process of exiting. - + Therefore, the contract for scheduleActor is that it never throws a SuspendActorControl. */ diff --git a/src/actors/scala/actors/MQueue.scala b/src/actors/scala/actors/MQueue.scala index 65427d68c5..4a148d2cb3 100644 --- a/src/actors/scala/actors/MQueue.scala +++ b/src/actors/scala/actors/MQueue.scala @@ -25,6 +25,20 @@ private[actors] class MQueue[Msg >: Null](protected val label: String) { _size += diff } + def prepend(other: MQueue[Msg]) { + if (!other.isEmpty) { + other.last.next = first + first = other.first + } + } + + def clear() { + first = null + last = null + _size = 0 + } + + def append(msg: Msg, session: OutputChannel[Any]) { changeSize(1) // size always increases by 1 val el = new MQueueElement(msg, session) diff --git a/src/actors/scala/actors/Reactor.scala b/src/actors/scala/actors/Reactor.scala index 206a97d97c..7a8d738758 100644 --- a/src/actors/scala/actors/Reactor.scala +++ b/src/actors/scala/actors/Reactor.scala @@ -214,11 +214,16 @@ trait Reactor[Msg >: Null] extends OutputChannel[Msg] with Combinators { scheduler executeFromActor makeReaction(null, handler, msg) } + private[actors] def preAct() = {} + // guarded by this private[actors] def dostart() { _state = Actor.State.Runnable scheduler newActor this - scheduler execute makeReaction(() => act(), null, null) + scheduler execute makeReaction(() => { + preAct() + act() + }, null, null) } /** @@ -285,12 +290,15 @@ trait Reactor[Msg >: Null] extends OutputChannel[Msg] with Combinators { throw Actor.suspendException } + private[actors] def internalPostStop() = {} + private[actors] def terminated() { synchronized { _state = Actor.State.Terminated // reset waitingFor, otherwise getState returns Suspended waitingFor = Reactor.waitingForNone } + internalPostStop() scheduler.terminated(this) } diff --git a/src/actors/scala/actors/ReplyReactor.scala b/src/actors/scala/actors/ReplyReactor.scala index 0ffbbd3cce..83d7ba0f7f 100644 --- a/src/actors/scala/actors/ReplyReactor.scala +++ b/src/actors/scala/actors/ReplyReactor.scala @@ -7,10 +7,7 @@ \* */ package scala.actors -@deprecated("Scala Actors are beeing removed from the standard library. Please refer to the migration guide.", "2.10") +@deprecated("Scala Actors are being removed from the standard library. Please refer to the migration guide.", "2.10") trait ReplyReactor extends InternalReplyReactor { - - protected[actors] def sender: OutputChannel[Any] = super.internalSender - + protected[actors] def sender: OutputChannel[Any] = super.internalSender } - diff --git a/src/compiler/scala/tools/asm/AnnotationVisitor.java b/src/asm/scala/tools/asm/AnnotationVisitor.java index b96e730a73..b96e730a73 100644 --- a/src/compiler/scala/tools/asm/AnnotationVisitor.java +++ b/src/asm/scala/tools/asm/AnnotationVisitor.java diff --git a/src/compiler/scala/tools/asm/AnnotationWriter.java b/src/asm/scala/tools/asm/AnnotationWriter.java index e530780249..e530780249 100644 --- a/src/compiler/scala/tools/asm/AnnotationWriter.java +++ b/src/asm/scala/tools/asm/AnnotationWriter.java diff --git a/src/compiler/scala/tools/asm/Attribute.java b/src/asm/scala/tools/asm/Attribute.java index 408f21ce1e..408f21ce1e 100644 --- a/src/compiler/scala/tools/asm/Attribute.java +++ b/src/asm/scala/tools/asm/Attribute.java diff --git a/src/compiler/scala/tools/asm/ByteVector.java b/src/asm/scala/tools/asm/ByteVector.java index 5081f0184b..5081f0184b 100644 --- a/src/compiler/scala/tools/asm/ByteVector.java +++ b/src/asm/scala/tools/asm/ByteVector.java diff --git a/src/compiler/scala/tools/asm/ClassReader.java b/src/asm/scala/tools/asm/ClassReader.java index f3287d41ae..f3287d41ae 100644 --- a/src/compiler/scala/tools/asm/ClassReader.java +++ b/src/asm/scala/tools/asm/ClassReader.java diff --git a/src/compiler/scala/tools/asm/ClassVisitor.java b/src/asm/scala/tools/asm/ClassVisitor.java index ae38ae0ab9..ae38ae0ab9 100644 --- a/src/compiler/scala/tools/asm/ClassVisitor.java +++ b/src/asm/scala/tools/asm/ClassVisitor.java diff --git a/src/compiler/scala/tools/asm/ClassWriter.java b/src/asm/scala/tools/asm/ClassWriter.java index c7a0736b51..c7a0736b51 100644 --- a/src/compiler/scala/tools/asm/ClassWriter.java +++ b/src/asm/scala/tools/asm/ClassWriter.java diff --git a/src/compiler/scala/tools/asm/CustomAttr.java b/src/asm/scala/tools/asm/CustomAttr.java index 5ecfd283d0..5ecfd283d0 100644 --- a/src/compiler/scala/tools/asm/CustomAttr.java +++ b/src/asm/scala/tools/asm/CustomAttr.java diff --git a/src/compiler/scala/tools/asm/Edge.java b/src/asm/scala/tools/asm/Edge.java index daac1f7bb0..daac1f7bb0 100644 --- a/src/compiler/scala/tools/asm/Edge.java +++ b/src/asm/scala/tools/asm/Edge.java diff --git a/src/compiler/scala/tools/asm/FieldVisitor.java b/src/asm/scala/tools/asm/FieldVisitor.java index 9ac0f6236f..9ac0f6236f 100644 --- a/src/compiler/scala/tools/asm/FieldVisitor.java +++ b/src/asm/scala/tools/asm/FieldVisitor.java diff --git a/src/compiler/scala/tools/asm/FieldWriter.java b/src/asm/scala/tools/asm/FieldWriter.java index 45ef6d0df3..45ef6d0df3 100644 --- a/src/compiler/scala/tools/asm/FieldWriter.java +++ b/src/asm/scala/tools/asm/FieldWriter.java diff --git a/src/compiler/scala/tools/asm/Frame.java b/src/asm/scala/tools/asm/Frame.java index 387b56796d..387b56796d 100644 --- a/src/compiler/scala/tools/asm/Frame.java +++ b/src/asm/scala/tools/asm/Frame.java diff --git a/src/compiler/scala/tools/asm/Handle.java b/src/asm/scala/tools/asm/Handle.java index be8f334192..be8f334192 100644 --- a/src/compiler/scala/tools/asm/Handle.java +++ b/src/asm/scala/tools/asm/Handle.java diff --git a/src/compiler/scala/tools/asm/Handler.java b/src/asm/scala/tools/asm/Handler.java index 9e92bb98be..9e92bb98be 100644 --- a/src/compiler/scala/tools/asm/Handler.java +++ b/src/asm/scala/tools/asm/Handler.java diff --git a/src/compiler/scala/tools/asm/Item.java b/src/asm/scala/tools/asm/Item.java index 021a0b11d3..021a0b11d3 100644 --- a/src/compiler/scala/tools/asm/Item.java +++ b/src/asm/scala/tools/asm/Item.java diff --git a/src/compiler/scala/tools/asm/Label.java b/src/asm/scala/tools/asm/Label.java index 712c7f251f..712c7f251f 100644 --- a/src/compiler/scala/tools/asm/Label.java +++ b/src/asm/scala/tools/asm/Label.java diff --git a/src/compiler/scala/tools/asm/MethodVisitor.java b/src/asm/scala/tools/asm/MethodVisitor.java index a8a859a6a9..a8a859a6a9 100644 --- a/src/compiler/scala/tools/asm/MethodVisitor.java +++ b/src/asm/scala/tools/asm/MethodVisitor.java diff --git a/src/compiler/scala/tools/asm/MethodWriter.java b/src/asm/scala/tools/asm/MethodWriter.java index 321bacb6fc..321bacb6fc 100644 --- a/src/compiler/scala/tools/asm/MethodWriter.java +++ b/src/asm/scala/tools/asm/MethodWriter.java diff --git a/src/compiler/scala/tools/asm/Opcodes.java b/src/asm/scala/tools/asm/Opcodes.java index 809e5ae590..809e5ae590 100644 --- a/src/compiler/scala/tools/asm/Opcodes.java +++ b/src/asm/scala/tools/asm/Opcodes.java diff --git a/src/compiler/scala/tools/asm/Type.java b/src/asm/scala/tools/asm/Type.java index bf1107182a..bf1107182a 100644 --- a/src/compiler/scala/tools/asm/Type.java +++ b/src/asm/scala/tools/asm/Type.java diff --git a/src/compiler/scala/tools/asm/signature/SignatureReader.java b/src/asm/scala/tools/asm/signature/SignatureReader.java index 22e6427e63..22e6427e63 100644 --- a/src/compiler/scala/tools/asm/signature/SignatureReader.java +++ b/src/asm/scala/tools/asm/signature/SignatureReader.java diff --git a/src/compiler/scala/tools/asm/signature/SignatureVisitor.java b/src/asm/scala/tools/asm/signature/SignatureVisitor.java index 2fc364e374..2fc364e374 100644 --- a/src/compiler/scala/tools/asm/signature/SignatureVisitor.java +++ b/src/asm/scala/tools/asm/signature/SignatureVisitor.java diff --git a/src/compiler/scala/tools/asm/signature/SignatureWriter.java b/src/asm/scala/tools/asm/signature/SignatureWriter.java index a59fdfde2b..a59fdfde2b 100644 --- a/src/compiler/scala/tools/asm/signature/SignatureWriter.java +++ b/src/asm/scala/tools/asm/signature/SignatureWriter.java diff --git a/src/compiler/scala/tools/asm/tree/AbstractInsnNode.java b/src/asm/scala/tools/asm/tree/AbstractInsnNode.java index 471f842ffc..471f842ffc 100644 --- a/src/compiler/scala/tools/asm/tree/AbstractInsnNode.java +++ b/src/asm/scala/tools/asm/tree/AbstractInsnNode.java diff --git a/src/compiler/scala/tools/asm/tree/AnnotationNode.java b/src/asm/scala/tools/asm/tree/AnnotationNode.java index 9f132550e6..9f132550e6 100644 --- a/src/compiler/scala/tools/asm/tree/AnnotationNode.java +++ b/src/asm/scala/tools/asm/tree/AnnotationNode.java diff --git a/src/compiler/scala/tools/asm/tree/ClassNode.java b/src/asm/scala/tools/asm/tree/ClassNode.java index 64effae698..64effae698 100644 --- a/src/compiler/scala/tools/asm/tree/ClassNode.java +++ b/src/asm/scala/tools/asm/tree/ClassNode.java diff --git a/src/compiler/scala/tools/asm/tree/FieldInsnNode.java b/src/asm/scala/tools/asm/tree/FieldInsnNode.java index 6b7a6a142a..6b7a6a142a 100644 --- a/src/compiler/scala/tools/asm/tree/FieldInsnNode.java +++ b/src/asm/scala/tools/asm/tree/FieldInsnNode.java diff --git a/src/compiler/scala/tools/asm/tree/FieldNode.java b/src/asm/scala/tools/asm/tree/FieldNode.java index 9a1e17033c..9a1e17033c 100644 --- a/src/compiler/scala/tools/asm/tree/FieldNode.java +++ b/src/asm/scala/tools/asm/tree/FieldNode.java diff --git a/src/compiler/scala/tools/asm/tree/FrameNode.java b/src/asm/scala/tools/asm/tree/FrameNode.java index 66825de0ac..66825de0ac 100644 --- a/src/compiler/scala/tools/asm/tree/FrameNode.java +++ b/src/asm/scala/tools/asm/tree/FrameNode.java diff --git a/src/compiler/scala/tools/asm/tree/IincInsnNode.java b/src/asm/scala/tools/asm/tree/IincInsnNode.java index 75ac40884d..75ac40884d 100644 --- a/src/compiler/scala/tools/asm/tree/IincInsnNode.java +++ b/src/asm/scala/tools/asm/tree/IincInsnNode.java diff --git a/src/compiler/scala/tools/asm/tree/InnerClassNode.java b/src/asm/scala/tools/asm/tree/InnerClassNode.java index 4579488921..4579488921 100644 --- a/src/compiler/scala/tools/asm/tree/InnerClassNode.java +++ b/src/asm/scala/tools/asm/tree/InnerClassNode.java diff --git a/src/compiler/scala/tools/asm/tree/InsnList.java b/src/asm/scala/tools/asm/tree/InsnList.java index dedd3bba73..dedd3bba73 100644 --- a/src/compiler/scala/tools/asm/tree/InsnList.java +++ b/src/asm/scala/tools/asm/tree/InsnList.java diff --git a/src/compiler/scala/tools/asm/tree/InsnNode.java b/src/asm/scala/tools/asm/tree/InsnNode.java index d4664d23c2..d4664d23c2 100644 --- a/src/compiler/scala/tools/asm/tree/InsnNode.java +++ b/src/asm/scala/tools/asm/tree/InsnNode.java diff --git a/src/compiler/scala/tools/asm/tree/IntInsnNode.java b/src/asm/scala/tools/asm/tree/IntInsnNode.java index b61270c786..b61270c786 100644 --- a/src/compiler/scala/tools/asm/tree/IntInsnNode.java +++ b/src/asm/scala/tools/asm/tree/IntInsnNode.java diff --git a/src/compiler/scala/tools/asm/tree/InvokeDynamicInsnNode.java b/src/asm/scala/tools/asm/tree/InvokeDynamicInsnNode.java index d993b5a054..d993b5a054 100644 --- a/src/compiler/scala/tools/asm/tree/InvokeDynamicInsnNode.java +++ b/src/asm/scala/tools/asm/tree/InvokeDynamicInsnNode.java diff --git a/src/compiler/scala/tools/asm/tree/JumpInsnNode.java b/src/asm/scala/tools/asm/tree/JumpInsnNode.java index 339ebbd2d0..339ebbd2d0 100644 --- a/src/compiler/scala/tools/asm/tree/JumpInsnNode.java +++ b/src/asm/scala/tools/asm/tree/JumpInsnNode.java diff --git a/src/compiler/scala/tools/asm/tree/LabelNode.java b/src/asm/scala/tools/asm/tree/LabelNode.java index 523a8d6442..523a8d6442 100644 --- a/src/compiler/scala/tools/asm/tree/LabelNode.java +++ b/src/asm/scala/tools/asm/tree/LabelNode.java diff --git a/src/compiler/scala/tools/asm/tree/LdcInsnNode.java b/src/asm/scala/tools/asm/tree/LdcInsnNode.java index f8d115acd5..f8d115acd5 100644 --- a/src/compiler/scala/tools/asm/tree/LdcInsnNode.java +++ b/src/asm/scala/tools/asm/tree/LdcInsnNode.java diff --git a/src/compiler/scala/tools/asm/tree/LineNumberNode.java b/src/asm/scala/tools/asm/tree/LineNumberNode.java index acc83c8d30..acc83c8d30 100644 --- a/src/compiler/scala/tools/asm/tree/LineNumberNode.java +++ b/src/asm/scala/tools/asm/tree/LineNumberNode.java diff --git a/src/compiler/scala/tools/asm/tree/LocalVariableNode.java b/src/asm/scala/tools/asm/tree/LocalVariableNode.java index 51cbd3ca00..51cbd3ca00 100644 --- a/src/compiler/scala/tools/asm/tree/LocalVariableNode.java +++ b/src/asm/scala/tools/asm/tree/LocalVariableNode.java diff --git a/src/compiler/scala/tools/asm/tree/LookupSwitchInsnNode.java b/src/asm/scala/tools/asm/tree/LookupSwitchInsnNode.java index 6d0f971c29..6d0f971c29 100644 --- a/src/compiler/scala/tools/asm/tree/LookupSwitchInsnNode.java +++ b/src/asm/scala/tools/asm/tree/LookupSwitchInsnNode.java diff --git a/src/compiler/scala/tools/asm/tree/MethodInsnNode.java b/src/asm/scala/tools/asm/tree/MethodInsnNode.java index c3036bc6b4..c3036bc6b4 100644 --- a/src/compiler/scala/tools/asm/tree/MethodInsnNode.java +++ b/src/asm/scala/tools/asm/tree/MethodInsnNode.java diff --git a/src/compiler/scala/tools/asm/tree/MethodNode.java b/src/asm/scala/tools/asm/tree/MethodNode.java index 70ec39e058..70ec39e058 100644 --- a/src/compiler/scala/tools/asm/tree/MethodNode.java +++ b/src/asm/scala/tools/asm/tree/MethodNode.java diff --git a/src/compiler/scala/tools/asm/tree/MultiANewArrayInsnNode.java b/src/asm/scala/tools/asm/tree/MultiANewArrayInsnNode.java index 9dfba77335..9dfba77335 100644 --- a/src/compiler/scala/tools/asm/tree/MultiANewArrayInsnNode.java +++ b/src/asm/scala/tools/asm/tree/MultiANewArrayInsnNode.java diff --git a/src/compiler/scala/tools/asm/tree/TableSwitchInsnNode.java b/src/asm/scala/tools/asm/tree/TableSwitchInsnNode.java index 929ad9b32b..929ad9b32b 100644 --- a/src/compiler/scala/tools/asm/tree/TableSwitchInsnNode.java +++ b/src/asm/scala/tools/asm/tree/TableSwitchInsnNode.java diff --git a/src/compiler/scala/tools/asm/tree/TryCatchBlockNode.java b/src/asm/scala/tools/asm/tree/TryCatchBlockNode.java index 375b4cfcb9..375b4cfcb9 100644 --- a/src/compiler/scala/tools/asm/tree/TryCatchBlockNode.java +++ b/src/asm/scala/tools/asm/tree/TryCatchBlockNode.java diff --git a/src/compiler/scala/tools/asm/tree/TypeInsnNode.java b/src/asm/scala/tools/asm/tree/TypeInsnNode.java index 0b2666c498..0b2666c498 100644 --- a/src/compiler/scala/tools/asm/tree/TypeInsnNode.java +++ b/src/asm/scala/tools/asm/tree/TypeInsnNode.java diff --git a/src/compiler/scala/tools/asm/tree/VarInsnNode.java b/src/asm/scala/tools/asm/tree/VarInsnNode.java index 89f572db59..89f572db59 100644 --- a/src/compiler/scala/tools/asm/tree/VarInsnNode.java +++ b/src/asm/scala/tools/asm/tree/VarInsnNode.java diff --git a/src/compiler/scala/tools/asm/tree/analysis/Analyzer.java b/src/asm/scala/tools/asm/tree/analysis/Analyzer.java index df387b0b8e..df387b0b8e 100644 --- a/src/compiler/scala/tools/asm/tree/analysis/Analyzer.java +++ b/src/asm/scala/tools/asm/tree/analysis/Analyzer.java diff --git a/src/compiler/scala/tools/asm/tree/analysis/AnalyzerException.java b/src/asm/scala/tools/asm/tree/analysis/AnalyzerException.java index a89bb3513f..a89bb3513f 100644 --- a/src/compiler/scala/tools/asm/tree/analysis/AnalyzerException.java +++ b/src/asm/scala/tools/asm/tree/analysis/AnalyzerException.java diff --git a/src/compiler/scala/tools/asm/tree/analysis/BasicInterpreter.java b/src/asm/scala/tools/asm/tree/analysis/BasicInterpreter.java index 64ddcc11e6..64ddcc11e6 100644 --- a/src/compiler/scala/tools/asm/tree/analysis/BasicInterpreter.java +++ b/src/asm/scala/tools/asm/tree/analysis/BasicInterpreter.java diff --git a/src/compiler/scala/tools/asm/tree/analysis/BasicValue.java b/src/asm/scala/tools/asm/tree/analysis/BasicValue.java index 6c449db9b0..6c449db9b0 100644 --- a/src/compiler/scala/tools/asm/tree/analysis/BasicValue.java +++ b/src/asm/scala/tools/asm/tree/analysis/BasicValue.java diff --git a/src/compiler/scala/tools/asm/tree/analysis/BasicVerifier.java b/src/asm/scala/tools/asm/tree/analysis/BasicVerifier.java index 9297dd9294..9297dd9294 100644 --- a/src/compiler/scala/tools/asm/tree/analysis/BasicVerifier.java +++ b/src/asm/scala/tools/asm/tree/analysis/BasicVerifier.java diff --git a/src/compiler/scala/tools/asm/tree/analysis/Frame.java b/src/asm/scala/tools/asm/tree/analysis/Frame.java index fe19c2c9ae..fe19c2c9ae 100644 --- a/src/compiler/scala/tools/asm/tree/analysis/Frame.java +++ b/src/asm/scala/tools/asm/tree/analysis/Frame.java diff --git a/src/compiler/scala/tools/asm/tree/analysis/Interpreter.java b/src/asm/scala/tools/asm/tree/analysis/Interpreter.java index 930c8f4af8..930c8f4af8 100644 --- a/src/compiler/scala/tools/asm/tree/analysis/Interpreter.java +++ b/src/asm/scala/tools/asm/tree/analysis/Interpreter.java diff --git a/src/compiler/scala/tools/asm/tree/analysis/SimpleVerifier.java b/src/asm/scala/tools/asm/tree/analysis/SimpleVerifier.java index c4f515d328..c4f515d328 100644 --- a/src/compiler/scala/tools/asm/tree/analysis/SimpleVerifier.java +++ b/src/asm/scala/tools/asm/tree/analysis/SimpleVerifier.java diff --git a/src/compiler/scala/tools/asm/tree/analysis/SmallSet.java b/src/asm/scala/tools/asm/tree/analysis/SmallSet.java index 205878d18c..205878d18c 100644 --- a/src/compiler/scala/tools/asm/tree/analysis/SmallSet.java +++ b/src/asm/scala/tools/asm/tree/analysis/SmallSet.java diff --git a/src/compiler/scala/tools/asm/tree/analysis/SourceInterpreter.java b/src/asm/scala/tools/asm/tree/analysis/SourceInterpreter.java index 067200b51e..067200b51e 100644 --- a/src/compiler/scala/tools/asm/tree/analysis/SourceInterpreter.java +++ b/src/asm/scala/tools/asm/tree/analysis/SourceInterpreter.java diff --git a/src/compiler/scala/tools/asm/tree/analysis/SourceValue.java b/src/asm/scala/tools/asm/tree/analysis/SourceValue.java index 57ff212fb2..57ff212fb2 100644 --- a/src/compiler/scala/tools/asm/tree/analysis/SourceValue.java +++ b/src/asm/scala/tools/asm/tree/analysis/SourceValue.java diff --git a/src/compiler/scala/tools/asm/tree/analysis/Subroutine.java b/src/asm/scala/tools/asm/tree/analysis/Subroutine.java index 038880ddcd..038880ddcd 100644 --- a/src/compiler/scala/tools/asm/tree/analysis/Subroutine.java +++ b/src/asm/scala/tools/asm/tree/analysis/Subroutine.java diff --git a/src/compiler/scala/tools/asm/tree/analysis/Value.java b/src/asm/scala/tools/asm/tree/analysis/Value.java index 1edf475ce7..1edf475ce7 100644 --- a/src/compiler/scala/tools/asm/tree/analysis/Value.java +++ b/src/asm/scala/tools/asm/tree/analysis/Value.java diff --git a/src/compiler/scala/tools/asm/util/ASMifiable.java b/src/asm/scala/tools/asm/util/ASMifiable.java index 6a31dd508f..6a31dd508f 100644 --- a/src/compiler/scala/tools/asm/util/ASMifiable.java +++ b/src/asm/scala/tools/asm/util/ASMifiable.java diff --git a/src/compiler/scala/tools/asm/util/ASMifier.java b/src/asm/scala/tools/asm/util/ASMifier.java index 5967c877d1..5967c877d1 100644 --- a/src/compiler/scala/tools/asm/util/ASMifier.java +++ b/src/asm/scala/tools/asm/util/ASMifier.java diff --git a/src/compiler/scala/tools/asm/util/CheckAnnotationAdapter.java b/src/asm/scala/tools/asm/util/CheckAnnotationAdapter.java index 8030c14f2e..8030c14f2e 100644 --- a/src/compiler/scala/tools/asm/util/CheckAnnotationAdapter.java +++ b/src/asm/scala/tools/asm/util/CheckAnnotationAdapter.java diff --git a/src/compiler/scala/tools/asm/util/CheckClassAdapter.java b/src/asm/scala/tools/asm/util/CheckClassAdapter.java index a455322531..a455322531 100644 --- a/src/compiler/scala/tools/asm/util/CheckClassAdapter.java +++ b/src/asm/scala/tools/asm/util/CheckClassAdapter.java diff --git a/src/compiler/scala/tools/asm/util/CheckFieldAdapter.java b/src/asm/scala/tools/asm/util/CheckFieldAdapter.java index bdcbe14b16..bdcbe14b16 100644 --- a/src/compiler/scala/tools/asm/util/CheckFieldAdapter.java +++ b/src/asm/scala/tools/asm/util/CheckFieldAdapter.java diff --git a/src/compiler/scala/tools/asm/util/CheckMethodAdapter.java b/src/asm/scala/tools/asm/util/CheckMethodAdapter.java index 7549765421..7549765421 100644 --- a/src/compiler/scala/tools/asm/util/CheckMethodAdapter.java +++ b/src/asm/scala/tools/asm/util/CheckMethodAdapter.java diff --git a/src/compiler/scala/tools/asm/util/CheckSignatureAdapter.java b/src/asm/scala/tools/asm/util/CheckSignatureAdapter.java index 3a6c3e780f..3a6c3e780f 100644 --- a/src/compiler/scala/tools/asm/util/CheckSignatureAdapter.java +++ b/src/asm/scala/tools/asm/util/CheckSignatureAdapter.java diff --git a/src/compiler/scala/tools/asm/util/Printer.java b/src/asm/scala/tools/asm/util/Printer.java index c39fd548ce..c39fd548ce 100644 --- a/src/compiler/scala/tools/asm/util/Printer.java +++ b/src/asm/scala/tools/asm/util/Printer.java diff --git a/src/compiler/scala/tools/asm/util/SignatureChecker.java b/src/asm/scala/tools/asm/util/SignatureChecker.java index 7b7eea4383..7b7eea4383 100644 --- a/src/compiler/scala/tools/asm/util/SignatureChecker.java +++ b/src/asm/scala/tools/asm/util/SignatureChecker.java diff --git a/src/compiler/scala/tools/asm/util/Textifiable.java b/src/asm/scala/tools/asm/util/Textifiable.java index b80d0139db..b80d0139db 100644 --- a/src/compiler/scala/tools/asm/util/Textifiable.java +++ b/src/asm/scala/tools/asm/util/Textifiable.java diff --git a/src/compiler/scala/tools/asm/util/Textifier.java b/src/asm/scala/tools/asm/util/Textifier.java index 8d40ebd026..8d40ebd026 100644 --- a/src/compiler/scala/tools/asm/util/Textifier.java +++ b/src/asm/scala/tools/asm/util/Textifier.java diff --git a/src/compiler/scala/tools/asm/util/TraceAnnotationVisitor.java b/src/asm/scala/tools/asm/util/TraceAnnotationVisitor.java index f112609031..f112609031 100644 --- a/src/compiler/scala/tools/asm/util/TraceAnnotationVisitor.java +++ b/src/asm/scala/tools/asm/util/TraceAnnotationVisitor.java diff --git a/src/compiler/scala/tools/asm/util/TraceClassVisitor.java b/src/asm/scala/tools/asm/util/TraceClassVisitor.java index bb830b71ce..bb830b71ce 100644 --- a/src/compiler/scala/tools/asm/util/TraceClassVisitor.java +++ b/src/asm/scala/tools/asm/util/TraceClassVisitor.java diff --git a/src/compiler/scala/tools/asm/util/TraceFieldVisitor.java b/src/asm/scala/tools/asm/util/TraceFieldVisitor.java index f537e83be1..f537e83be1 100644 --- a/src/compiler/scala/tools/asm/util/TraceFieldVisitor.java +++ b/src/asm/scala/tools/asm/util/TraceFieldVisitor.java diff --git a/src/compiler/scala/tools/asm/util/TraceMethodVisitor.java b/src/asm/scala/tools/asm/util/TraceMethodVisitor.java index 9aabf2079e..9aabf2079e 100644 --- a/src/compiler/scala/tools/asm/util/TraceMethodVisitor.java +++ b/src/asm/scala/tools/asm/util/TraceMethodVisitor.java diff --git a/src/compiler/scala/tools/asm/util/TraceSignatureVisitor.java b/src/asm/scala/tools/asm/util/TraceSignatureVisitor.java index a37b759811..a37b759811 100644 --- a/src/compiler/scala/tools/asm/util/TraceSignatureVisitor.java +++ b/src/asm/scala/tools/asm/util/TraceSignatureVisitor.java diff --git a/src/build/maven/maven-deploy.xml b/src/build/maven/maven-deploy.xml index ac0f8f745b..7ab54f81c3 100644 --- a/src/build/maven/maven-deploy.xml +++ b/src/build/maven/maven-deploy.xml @@ -112,6 +112,7 @@ <deploy-local name="scala-compiler" version="@{version}" repository="@{repository}" /> <deploy-local-plugin name="continuations" version="@{version}" repository="@{repository}"/> <deploy-local name="scala-actors" version="@{version}" repository="@{repository}" /> + <deploy-local name="scala-actors-migration" version="@{version}" repository="@{repository}" /> <deploy-local name="scala-swing" version="@{version}" repository="@{repository}"/> <deploy-local name="scalap" version="@{version}" repository="@{repository}"/> <deploy-local name="scala-partest" version="@{version}" repository="@{repository}"/> @@ -172,6 +173,7 @@ <deploy-remote name="scala-compiler" version="@{version}" repository="@{repository}" /> <deploy-remote name="scala-swing" version="@{version}" repository="@{repository}"/> <deploy-remote name="scala-actors" version="@{version}" repository="@{repository}"/> + <deploy-remote name="scala-actors-migration" version="@{version}" repository="@{repository}"/> <deploy-remote name="scalap" version="@{version}" repository="@{repository}"/> <deploy-remote name="scala-partest" version="@{version}" repository="@{repository}"/> <deploy-remote-plugin name="continuations" version="@{version}" repository="@{repository}"/> @@ -239,6 +241,7 @@ <deploy-remote-signed name="scala-compiler" version="@{version}" repository="@{repository}" /> <deploy-remote-signed name="scala-swing" version="@{version}" repository="@{repository}"/> <deploy-remote-signed name="scala-actors" version="@{version}" repository="@{repository}"/> + <deploy-remote-signed name="scala-actors-migration" version="@{version}" repository="@{repository}"/> <deploy-remote-signed name="scalap" version="@{version}" repository="@{repository}"/> <deploy-remote-signed name="scala-partest" version="@{version}" repository="@{repository}"/> </sequential> diff --git a/src/build/maven/scala-actors-migration-pom.xml b/src/build/maven/scala-actors-migration-pom.xml new file mode 100644 index 0000000000..93fc34ece9 --- /dev/null +++ b/src/build/maven/scala-actors-migration-pom.xml @@ -0,0 +1,66 @@ +<project + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.scala-lang</groupId> + <artifactId>scala-actors-migration</artifactId> + <packaging>jar</packaging> + <version>@VERSION@</version> + <name>Scala Migration Kit</name> + <description>Migration kit that enables easy transition from the Scala Actors to Akka.</description> + <url>http://www.scala-lang.org/</url> + <inceptionYear>2012</inceptionYear> + <organization> + <name>LAMP/EPFL</name> + <url>http://lamp.epfl.ch/</url> + </organization> + <licenses> + <license> + <name>BSD-like</name> + <url>http://www.scala-lang.org/downloads/license.html + </url> + <distribution>repo</distribution> + </license> + </licenses> + <scm> + <connection>scm:git:git://github.com/scala/scala.git</connection> + <url>https://github.com/scala/scala.git</url> + </scm> + <issueManagement> + <system>JIRA</system> + <url>https://issues.scala-lang.org/</url> + </issueManagement> + <dependencies> + <dependency> + <groupId>org.scala-lang</groupId> + <artifactId>scala-library</artifactId> + <version>@VERSION@</version> + </dependency> + <dependency> + <groupId>org.scala-lang</groupId> + <artifactId>scala-actors</artifactId> + <version>@VERSION@</version> + </dependency> + </dependencies> + <distributionManagement> + <repository> + <id>scala-tools.org</id> + <url>@RELEASE_REPOSITORY@</url> + </repository> + <snapshotRepository> + <id>scala-tools.org</id> + <url>@SNAPSHOT_REPOSITORY@</url> + <uniqueVersion>false</uniqueVersion> + </snapshotRepository> + </distributionManagement> + <developers> + <developer> + <id>lamp</id> + <name>EPFL LAMP</name> + </developer> + <developer> + <id>Typesafe</id> + <name>Typesafe, Inc.</name> + </developer> + </developers> +</project> diff --git a/src/build/pack.xml b/src/build/pack.xml index f96c6b9799..956beaef88 100644 --- a/src/build/pack.xml +++ b/src/build/pack.xml @@ -139,6 +139,7 @@ MAIN DISTRIBUTION PACKAGING <mvn-copy-lib mvn.artifact.name="scala-compiler"/> <mvn-copy-lib mvn.artifact.name="scala-swing"/> <mvn-copy-lib mvn.artifact.name="scala-actors"/> + <mvn-copy-lib mvn.artifact.name="scala-actors-migration"/> <mvn-copy-lib mvn.artifact.name="scala-partest"/> <mvn-copy-lib mvn.artifact.name="scalap"/> </target> @@ -201,6 +202,11 @@ MAIN DISTRIBUTION PACKAGING basedir="${build-docs.dir}/continuations-plugin"> <include name="**/*"/> </jar> + <jar destfile="${dists.dir}/maven/${version.number}/scala-actors-migration/scala-actors-migration-docs.jar" + basedir="${build-docs.dir}/actors-migration"> + <include name="**/*"/> + </jar> + <!-- TODO - Scala swing and actors should maybe have thier own jar, but creating it is SLOW. --> <copy tofile="${dists.dir}/maven/${version.number}/scala-swing/scala-swing-docs.jar" file="${dists.dir}/maven/${version.number}/scala-library/scala-library-docs.jar"/> diff --git a/src/compiler/scala/reflect/internal/Constants.scala b/src/compiler/scala/reflect/internal/Constants.scala index 135d18d5ad..861bc870a7 100644 --- a/src/compiler/scala/reflect/internal/Constants.scala +++ b/src/compiler/scala/reflect/internal/Constants.scala @@ -224,6 +224,7 @@ trait Constants extends api.Constants { case ClazzTag => "classOf[" + signature(typeValue) + "]" case CharTag => "'" + escapedChar(charValue) + "'" case LongTag => longValue.toString() + "L" + case EnumTag => symbolValue.name.toString() case _ => String.valueOf(value) } } diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala index 0612dcdfd4..0cdef9e79a 100644 --- a/src/compiler/scala/reflect/internal/Definitions.scala +++ b/src/compiler/scala/reflect/internal/Definitions.scala @@ -404,6 +404,7 @@ trait Definitions extends reflect.api.StandardDefinitions { lazy val JavaSerializableClass = requiredClass[java.io.Serializable] modifyInfo fixupAsAnyTrait lazy val ComparableClass = requiredClass[java.lang.Comparable[_]] modifyInfo fixupAsAnyTrait lazy val JavaCloneableClass = requiredClass[java.lang.Cloneable] + lazy val JavaNumberClass = requiredClass[java.lang.Number] lazy val RemoteInterfaceClass = requiredClass[java.rmi.Remote] lazy val RemoteExceptionClass = requiredClass[java.rmi.RemoteException] @@ -609,6 +610,7 @@ trait Definitions extends reflect.api.StandardDefinitions { def isTupleTypeOrSubtype(tp: Type) = isTupleType(tp) def tupleField(n: Int, j: Int) = getMember(TupleClass(n), nme.productAccessorName(j)) + // NOTE: returns true for NoSymbol since it's included in the TupleClass array -- is this intensional? def isTupleSymbol(sym: Symbol) = TupleClass contains unspecializedSymbol(sym) def isProductNClass(sym: Symbol) = ProductClass contains sym diff --git a/src/compiler/scala/reflect/internal/InfoTransformers.scala b/src/compiler/scala/reflect/internal/InfoTransformers.scala index 96d9d8f076..e53f714c0c 100644 --- a/src/compiler/scala/reflect/internal/InfoTransformers.scala +++ b/src/compiler/scala/reflect/internal/InfoTransformers.scala @@ -20,12 +20,14 @@ trait InfoTransformers { def transform(sym: Symbol, tpe: Type): Type def insert(that: InfoTransformer) { - assert(this.pid != that.pid) + assert(this.pid != that.pid, this.pid) + if (that.pid < this.pid) { prev insert that } else if (next.pid <= that.pid && next.pid != NoPhase.id) { next insert that } else { + log("Inserting info transformer %s following %s".format(phaseOf(that.pid), phaseOf(this.pid))) that.next = next that.prev = this next.prev = that diff --git a/src/compiler/scala/reflect/internal/Phase.scala b/src/compiler/scala/reflect/internal/Phase.scala index 89d643aacf..68dc5ce783 100644 --- a/src/compiler/scala/reflect/internal/Phase.scala +++ b/src/compiler/scala/reflect/internal/Phase.scala @@ -7,9 +7,10 @@ package scala.reflect package internal abstract class Phase(val prev: Phase) { + if ((prev ne null) && (prev ne NoPhase)) + prev.nx = this type Id = Int - val id: Id = if (prev eq null) 0 else prev.id + 1 /** New flags visible after this phase has completed */ @@ -18,12 +19,13 @@ abstract class Phase(val prev: Phase) { /** New flags visible once this phase has started */ def newFlags: Long = 0l - private var fmask: Long = - if (prev eq null) Flags.InitialFlags else prev.flagMask | prev.nextFlags | newFlags + val fmask = ( + if (prev eq null) Flags.InitialFlags + else prev.flagMask | prev.nextFlags | newFlags + ) def flagMask: Long = fmask private var nx: Phase = this - if ((prev ne null) && (prev ne NoPhase)) prev.nx = this def next: Phase = nx def hasNext = next != this diff --git a/src/compiler/scala/reflect/internal/util/Origins.scala b/src/compiler/scala/reflect/internal/util/Origins.scala index 19b3adda9d..0bd5ad55ca 100644 --- a/src/compiler/scala/reflect/internal/util/Origins.scala +++ b/src/compiler/scala/reflect/internal/util/Origins.scala @@ -15,7 +15,7 @@ import Origins._ * You could do this: * * {{{ - * private lazy val origins = Origins[SymbolTable]("phase_=") + * private lazy val origins = Origins("arbitraryTag") * // Commented out original enclosed for contrast * // final def phase_=(p: Phase): Unit = { * final def phase_=(p: Phase): Unit = origins { @@ -23,7 +23,7 @@ import Origins._ * * And that's it. When the JVM exits it would issue a report something like this: {{{ - >> Origins scala.tools.nsc.symtab.SymbolTable.phase_= logged 145585 calls from 51 distinguished sources. + >> Origins tag 'arbitraryTag' logged 145585 calls from 51 distinguished sources. 71114 scala.tools.nsc.symtab.Symbols$Symbol.unsafeTypeParams(Symbols.scala:862) 16584 scala.tools.nsc.symtab.Symbols$Symbol.rawInfo(Symbols.scala:757) @@ -37,29 +37,21 @@ import Origins._ */ abstract class Origins { type Rep + type StackSlice = Array[StackTraceElement] + + def tag: String + def isCutoff(el: StackTraceElement): Boolean def newRep(xs: StackSlice): Rep def repString(rep: Rep): String - def originClass: String - - private var _tag: String = null - def tag: String = _tag - def setTag(tag: String): this.type = { - _tag = tag - this - } private val origins = new mutable.HashMap[Rep, Int] withDefaultValue 0 private def add(xs: Rep) = origins(xs) += 1 private def total = origins.values.foldLeft(0L)(_ + _) - // We find the right line by dropping any from around here and any - // from the method's origin class. - private def dropStackElement(cn: String) = - (cn startsWith OriginsName) || (cn startsWith originClass) - // Create a stack and whittle it down to the interesting part. - private def readStack(): Array[StackTraceElement] = - (new Throwable).getStackTrace dropWhile (el => dropStackElement(el.getClassName)) + def readStack(): Array[StackTraceElement] = ( + Thread.currentThread.getStackTrace dropWhile (x => !isCutoff(x)) dropWhile isCutoff drop 1 + ) def apply[T](body: => T): T = { add(newRep(readStack())) @@ -67,7 +59,7 @@ abstract class Origins { } def clear() = origins.clear() def show() = { - println("\n>> Origins %s.%s logged %s calls from %s distinguished sources.\n".format(originClass, tag, total, origins.keys.size)) + println("\n>> Origins tag '%s' logged %s calls from %s distinguished sources.\n".format(tag, total, origins.keys.size)) origins.toList sortBy (-_._2) foreach { case (k, v) => println("%7s %s".format(v, repString(k))) } @@ -79,29 +71,49 @@ abstract class Origins { } object Origins { - private type StackSlice = Array[StackTraceElement] - private val OriginsName = classOf[Origins].getName - private val counters = new mutable.HashSet[Origins] + private val counters = mutable.HashMap[String, Origins]() + private val thisClass = this.getClass.getName - { - // Console.println("\nOrigins loaded: registering shutdown hook to display results.") - sys.addShutdownHook(counters foreach (_.purge())) + locally { + sys.addShutdownHook(counters.values foreach (_.purge())) } - def apply[T: ClassTag](tag: String): Origins = apply(tag, classTag[T].erasure) - def apply(tag: String, clazz: Class[_]): Origins = apply(tag, new OneLine(clazz)) - def apply(tag: String, orElse: => Origins): Origins = { - counters find (_.tag == tag) getOrElse { - val res = orElse setTag tag - counters += res - res - } + case class OriginId(className: String, methodName: String) { + def matches(el: StackTraceElement) = ( + (methodName == el.getMethodName) && (className startsWith el.getClassName) + ) } - class OneLine(clazz: Class[_]) extends Origins { - type Rep = StackTraceElement - val originClass = clazz.getName stripSuffix MODULE_SUFFIX_STRING - def newRep(xs: StackSlice): Rep = xs(0) - def repString(rep: Rep) = " " + rep + def lookup(tag: String, orElse: String => Origins): Origins = + counters.getOrElseUpdate(tag, orElse(tag)) + def register(x: Origins): Origins = { + counters(x.tag) = x + x + } + + private def preCutoff(el: StackTraceElement) = ( + (el.getClassName == thisClass) + || (el.getClassName startsWith "java.lang.") + ) + private def findCutoff() = { + val cutoff = Thread.currentThread.getStackTrace dropWhile preCutoff head; + OriginId(cutoff.getClassName, cutoff.getMethodName) + } + + def apply(tag: String): Origins = counters.getOrElseUpdate(tag, new OneLine(tag, findCutoff())) + def apply(tag: String, frames: Int): Origins = counters.getOrElseUpdate(tag, new MultiLine(tag, findCutoff(), frames)) + + class OneLine(val tag: String, id: OriginId) extends Origins { + type Rep = StackTraceElement + def isCutoff(el: StackTraceElement) = id matches el + def newRep(xs: StackSlice): Rep = if ((xs eq null) || (xs.length == 0)) null else xs(0) + def repString(rep: Rep) = " " + rep + } + class MultiLine(val tag: String, id: OriginId, numLines: Int) extends Origins { + type Rep = List[StackTraceElement] + def isCutoff(el: StackTraceElement) = id matches el + def newRep(xs: StackSlice): Rep = (xs take numLines).toList + def repString(rep: Rep) = rep.map("\n " + _).mkString + override def readStack() = super.readStack() drop 1 } } diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index c917ec9984..fb5b9b7169 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -1468,20 +1468,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb refreshProgress } - /** - * Attempt to locate a source file providing the given name as a top-level - * definition in the given context, and add it to the run via compileLate - * if found. - */ - def compileSourceFor(context : analyzer.Context, name : Name) = false - - /** - * Attempt to locate a source file providing the given name as a top-level - * definition with the given prefix, and add it to the run via compileLate - * if found. - */ - def compileSourceFor(qual : Tree, name : Name) = false - /** Reset package class to state at typer (not sure what this * is needed for?) */ diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index 23fcffd657..3a527676b4 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -208,6 +208,7 @@ abstract class TreeGen extends reflect.internal.TreeGen with TreeDSL { else AppliedTypeTree(Ident(clazz), targs map TypeTree) )) } + def mkSuperSelect = Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR) def wildcardStar(tree: Tree) = atPos(tree.pos) { Typed(tree, Ident(tpnme.WILDCARD_STAR)) } diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index adc490c8e1..6f1a8f488f 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -115,9 +115,7 @@ trait Trees extends reflect.internal.Trees { self: Global => // convert (implicit ... ) to ()(implicit ... ) if its the only parameter section if (vparamss1.isEmpty || !vparamss1.head.isEmpty && vparamss1.head.head.mods.isImplicit) vparamss1 = List() :: vparamss1; - val superRef: Tree = atPos(superPos) { - Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR) - } + val superRef: Tree = atPos(superPos)(gen.mkSuperSelect) val superCall = (superRef /: argss) (Apply) List( atPos(wrappingPos(superPos, lvdefs ::: argss.flatten)) ( diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index c02a7e493e..a0524d6932 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -385,7 +385,7 @@ self => Nil, List(Nil), TypeTree(), - Block(List(Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), Nil)), Literal(Constant(()))) + Block(List(Apply(gen.mkSuperSelect, Nil)), Literal(Constant(()))) ) // def main diff --git a/src/compiler/scala/tools/nsc/backend/icode/Members.scala b/src/compiler/scala/tools/nsc/backend/icode/Members.scala index 71795a02aa..019e887c4e 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Members.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Members.scala @@ -257,11 +257,23 @@ trait Members { var succ = bb do { succ = nextBlock(succ); - bb.removeLastInstruction - succ.toList foreach { i => bb.emit(i, i.pos) } - code.removeBlock(succ) + val lastInstr = bb.lastInstruction + /* Ticket SI-5672 + * Besides removing the control-flow instruction at the end of `bb` (usually a JUMP), we have to pop any values it pushes. + * Examples: + * `SWITCH` consisting of just the default case, or + * `CJUMP(targetBlock, targetBlock, _, _)` ie where success and failure targets coincide (this one consumes two stack values). + */ + val oldTKs = lastInstr.consumedTypes + assert(lastInstr.consumed == oldTKs.size, "Someone forgot to override consumedTypes() in " + lastInstr) + + bb.removeLastInstruction + for(tk <- oldTKs.reverse) { bb.emit(DROP(tk), lastInstr.pos) } + succ.toList foreach { i => bb.emit(i, i.pos) } + code.removeBlock(succ) + exh foreach { e => e.covered = e.covered - succ } + nextBlock -= bb - exh foreach { e => e.covered = e.covered - succ } } while (nextBlock.isDefinedAt(succ)) bb.close } else diff --git a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala index 576cc72f82..3179fc5c56 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala @@ -436,6 +436,8 @@ trait Opcodes { self: ICodes => override def consumed = 1 override def produced = 0 + override val consumedTypes = List(INT) + def flatTagsCount: Int = { var acc = 0; var rest = tags; while(rest.nonEmpty) { acc += rest.head.length; rest = rest.tail }; acc } // a one-liner } @@ -470,6 +472,8 @@ trait Opcodes { self: ICodes => override def consumed = 2 override def produced = 0 + + override val consumedTypes = List(kind, kind) } /** This class represents a CZJUMP instruction @@ -489,6 +493,8 @@ trait Opcodes { self: ICodes => override def consumed = 1 override def produced = 0 + + override val consumedTypes = List(kind) } @@ -499,6 +505,8 @@ trait Opcodes { self: ICodes => case class RETURN(kind: TypeKind) extends Instruction { override def consumed = if (kind == UNIT) 0 else 1 override def produced = 0 + + // TODO override val consumedTypes = List(kind) } /** This class represents a THROW instruction diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index 81fe907a0c..99832d1327 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -223,8 +223,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters { // unlike javaNameCache, reverseJavaName contains entries only for class symbols and their internal names.
val reverseJavaName = mutable.Map.empty[String, Symbol] ++= List(
- binarynme.RuntimeNothing.toString() -> NothingClass, // neither RuntimeNothingClass nor RuntimeNullClass belong to the co-domain of this map.
- binarynme.RuntimeNull.toString() -> NullClass
+ binarynme.RuntimeNothing.toString() -> RuntimeNothingClass, // RuntimeNothingClass is the bytecode-level return type of Scala methods with Nothing return-type.
+ binarynme.RuntimeNull.toString() -> RuntimeNullClass
)
private def mkFlags(args: Int*) = args.foldLeft(0)(_ | _)
@@ -587,7 +587,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { case None =>
reverseJavaName.put(internalName, trackedSym)
case Some(oldsym) =>
- assert(List(NothingClass, NullClass).contains(oldsym) || oldsym == trackedSym,
+ assert((oldsym == trackedSym) || List(RuntimeNothingClass, RuntimeNullClass).contains(oldsym), // NothingClass, NullClass,
"how can getCommonSuperclass() do its job if different class symbols get the same bytecode-level internal name.")
}
}
@@ -2542,7 +2542,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters { } else if(kind.isRefOrArrayType) { // REFERENCE(_) | ARRAY(_)
val Success = success
val Failure = failure
- (cond, nextBlock) match {
+ // @unchecked because references aren't compared with GT, GE, LT, LE.
+ ((cond, nextBlock) : @unchecked) match {
case (EQ, Success) => jcode emitIFNONNULL labels(failure)
case (NE, Failure) => jcode emitIFNONNULL labels(success)
case (EQ, Failure) => jcode emitIFNULL labels(success)
diff --git a/src/compiler/scala/tools/nsc/doc/html/page/Index.scala b/src/compiler/scala/tools/nsc/doc/html/page/Index.scala index 346780147e..8ed13e0da2 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/Index.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/Index.scala @@ -44,7 +44,7 @@ class Index(universe: doc.Universe, index: doc.Index) extends HtmlPage { </div> { browser } <div id="content" class="ui-layout-center"> - <iframe name="template" src={ relativeLinkTo{List("package.html")} }/> + <iframe id="template" name="template" src={ relativeLinkTo{List("package.html")} }/> </div> </body> diff --git a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala index 220321d225..49cd17c176 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala @@ -87,7 +87,7 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage { memberToCommentHtml(tpl, true) } <div id="mbrsel"> - <div id='textfilter'><span class='pre'/><span class='input'><input type='text' accesskey='/'/></span><span class='post'/></div> + <div id='textfilter'><span class='pre'/><span class='input'><input id='mbrsel-input' type='text' accesskey='/'/></span><span class='post'/></div> { if (tpl.linearizationTemplates.isEmpty && tpl.conversions.isEmpty) NodeSeq.Empty else <div id="order"> <span class="filtertype">Ordering</span> diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.css b/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.css index c6136c508e..2a8f9b570a 100644 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.css +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.css @@ -1,23 +1,32 @@ * { - color: inherit; - font-size: 10pt; - text-decoration: none; + color: inherit; + font-size: 10pt; + text-decoration: none; font-family: Arial, sans-serif; - border-width: 0px; - padding: 0px; - margin: 0px; + border-width: 0px; + padding: 0px; + margin: 0px; } a { - cursor: pointer; + cursor: pointer; } a:hover { - text-decoration: underline; + text-decoration: underline; } h1 { - display: none; + display: none; +} + +.selected { + -moz-box-shadow: inset 0px 5px 10px rgba(58, 88, 97, .36); + -webkit-box-shadow: inset 0px 5px 10px rgba(58, 88, 97, .36); + border-top: solid 1px rgba(119, 138, 153, 0.8); + border-bottom: solid 1px rgba(151, 173, 191, 0.4); + background-color: #ced2d9; + margin: -1px 0px; } /*.letters { @@ -33,81 +42,81 @@ h1 { } #browser { - top: 0px; - left: 0px; - bottom: 0px; - width: 100%; - display: block; - position: fixed; + top: 0px; + left: 0px; + bottom: 0px; + width: 100%; + display: block; + position: fixed; } #filter { - position: absolute; - display: block; -/* padding: 5px;*/ - right: 0; - left: 0; - top: 0; - background-image:url('filterbg.gif'); - background-repeat:repeat-x; - background-color: #ededee; /* light gray */ - /*background-color: #DADADA;*/ - border:1px solid #bbbbbb; - border-top:0; - border-left:0; - border-right:0; + position: absolute; + display: block; +/* padding: 5px;*/ + right: 0; + left: 0; + top: 0; + background-image:url('filterbg.gif'); + background-repeat:repeat-x; + background-color: #ededee; /* light gray */ + /*background-color: #DADADA;*/ + border:1px solid #bbbbbb; + border-top:0; + border-left:0; + border-right:0; } #textfilter { - position: relative; - display: block; - height: 20px; - margin-top: 5px; - margin-bottom: 5px; + position: relative; + display: block; + height: 20px; + margin-top: 5px; + margin-bottom: 5px; } #textfilter > .pre { - display: block; - position: absolute; - top: 0; - left: 0; - height: 23px; - width: 21px; - background: url("filter_box_left.png"); + display: block; + position: absolute; + top: 0; + left: 0; + height: 23px; + width: 21px; + background: url("filter_box_left.png"); } #textfilter > .input { - display: block; - position: absolute; - top: 0; - right: 20px; - left: 20px; + display: block; + position: absolute; + top: 0; + right: 20px; + left: 20px; } #textfilter > .input > input { - height: 20px; - padding: 1px; - font-weight: bold; - color: #000000; - background: #ffffff url("filterboxbarbg.png") repeat-x bottom left; - width: 100%; + height: 20px; + padding: 1px; + font-weight: bold; + color: #000000; + background: #ffffff url("filterboxbarbg.png") repeat-x bottom left; + width: 100%; } #textfilter > .post { - display: block; - position: absolute; - top: 0; - right: 0; - height: 23px; - width: 21px; - background: url("filter_box_right.png"); + display: block; + position: absolute; + top: 0; + right: 0; + height: 23px; + width: 21px; + background: url("filter_box_right.png"); } /*#textfilter { - position: relative; - display: block; + position: relative; + display: block; height: 20px; - margin-bottom: 5px; + margin-bottom: 5px; } #textfilter > .pre { @@ -121,7 +130,7 @@ h1 { } #textfilter > .input { - display: block; + display: block; position: absolute; top: 0; right: 20px; @@ -129,11 +138,11 @@ h1 { } #textfilter > .input > input { - height: 16px; - padding: 2px; - font-weight: bold; - color: darkblue; - background-color: white; + height: 16px; + padding: 2px; + font-weight: bold; + color: darkblue; + background-color: white; width: 100%; } @@ -148,22 +157,22 @@ h1 { }*/ #focusfilter { - position: relative; - text-align: center; - display: block; - padding: 5px; - background-color: #fffebd; /* light yellow*/ - text-shadow: #ffffff 0 1px 0; + position: relative; + text-align: center; + display: block; + padding: 5px; + background-color: #fffebd; /* light yellow*/ + text-shadow: #ffffff 0 1px 0; } #focusfilter .focuscoll { - font-weight: bold; - text-shadow: #ffffff 0 1px 0; + font-weight: bold; + text-shadow: #ffffff 0 1px 0; } #focusfilter img { - bottom: -2px; - position: relative; + bottom: -2px; + position: relative; } #kindfilter { @@ -182,10 +191,9 @@ h1 { } #kindfilter > a:hover { - color: #4C4C4C; - text-decoration: none; - text-shadow: #ffffff 0 1px 0; - + color: #4C4C4C; + text-decoration: none; + text-shadow: #ffffff 0 1px 0; } #letters { @@ -208,117 +216,117 @@ h1 { } #tpl { - display: block; - position: fixed; - overflow: auto; - right: 0; - left: 0; - bottom: 0; - top: 5px; - position: absolute; - display: block; + display: block; + position: fixed; + overflow: auto; + right: 0; + left: 0; + bottom: 0; + top: 5px; + position: absolute; + display: block; } #tpl .packhide { - display: block; - float: right; - font-weight: normal; - color: white; + display: block; + float: right; + font-weight: normal; + color: white; } #tpl .packfocus { - display: block; - float: right; - font-weight: normal; - color: white; + display: block; + float: right; + font-weight: normal; + color: white; } #tpl .packages > ol { - background-color: #dadfe6; - /*margin-bottom: 5px;*/ + background-color: #dadfe6; + /*margin-bottom: 5px;*/ } /*#tpl .packages > ol > li { - margin-bottom: 1px; + margin-bottom: 1px; }*/ #tpl .packages > li > a { - padding: 0px 5px; + padding: 0px 5px; } #tpl .packages > li > a.tplshow { - display: block; - color: white; - font-weight: bold; - display: block; - text-shadow: #000000 0 1px 0; + display: block; + color: white; + font-weight: bold; + display: block; + text-shadow: #000000 0 1px 0; } #tpl ol > li.pack { - padding: 3px 5px; - background: url("packagesbg.gif"); - background-repeat:repeat-x; - min-height: 14px; - background-color: #6e808e; + padding: 3px 5px; + background: url("packagesbg.gif"); + background-repeat:repeat-x; + min-height: 14px; + background-color: #6e808e; } #tpl ol > li { - display: block; + display: block; } #tpl .templates > li { - padding-left: 5px; - min-height: 18px; + padding-left: 5px; + min-height: 18px; } #tpl ol > li .icon { - padding-right: 5px; - bottom: -2px; - position: relative; + padding-right: 5px; + bottom: -2px; + position: relative; } #tpl .templates div.placeholder { - padding-right: 5px; - width: 13px; - display: inline-block; + padding-right: 5px; + width: 13px; + display: inline-block; } #tpl .templates span.tplLink { - padding-left: 5px; + padding-left: 5px; } #content { - border-left-width: 1px; - border-left-color: black; - border-left-style: white; - right: 0px; - left: 0px; - bottom: 0px; - top: 0px; - position: fixed; - margin-left: 300px; - display: block; + border-left-width: 1px; + border-left-color: black; + border-left-style: white; + right: 0px; + left: 0px; + bottom: 0px; + top: 0px; + position: fixed; + margin-left: 300px; + display: block; } #content > iframe { - display: block; - height: 100%; - width: 100%; + display: block; + height: 100%; + width: 100%; } .ui-layout-pane { - background: #FFF; - overflow: auto; + background: #FFF; + overflow: auto; } .ui-layout-resizer { - background-image:url('filterbg.gif'); - background-repeat:repeat-x; - background-color: #ededee; /* light gray */ - border:1px solid #bbbbbb; - border-top:0; - border-bottom:0; - border-left: 0; + background-image:url('filterbg.gif'); + background-repeat:repeat-x; + background-color: #ededee; /* light gray */ + border:1px solid #bbbbbb; + border-top:0; + border-bottom:0; + border-left: 0; } .ui-layout-toggler { diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js b/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js index b767722b8c..eb7f752440 100644 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js @@ -15,10 +15,10 @@ var lastHash = ""; $(document).ready(function() { $('body').layout({ west__size: '20%' }); - $('#browser').layout({ - center__paneSelector: ".ui-west-center" + $('#browser').layout({ + center__paneSelector: ".ui-west-center" //,center__initClosed:true - ,north__paneSelector: ".ui-west-north" + ,north__paneSelector: ".ui-west-north" }); $('iframe').bind("load", function(){ var subtitle = $(this).contents().find('title').text(); @@ -260,18 +260,95 @@ function prepareEntityList() { .prepend("<a class='packfocus'>focus</a>"); } +/* Handles all key presses while scrolling around with keyboard shortcuts in left panel */ +function keyboardScrolldownLeftPane() { + scheduler.add("init", function() { + $("#textfilter input").blur(); + var $items = $("#tpl li"); + $items.first().addClass('selected'); + + $(window).bind("keydown", function(e) { + var $old = $items.filter('.selected'), + $new; + + switch ( e.keyCode ) { + + case 9: // tab + $old.removeClass('selected'); + break; + + case 13: // enter + $old.removeClass('selected'); + var $url = $old.children().filter('a:last').attr('href'); + $("#template").attr("src",$url); + break; + + case 27: // escape + $old.removeClass('selected'); + $(window).unbind(e); + $("#textfilter input").focus(); + + break; + + case 38: // up + $new = $old.prev(); + + if (!$new.length) { + $new = $old.parent().prev(); + } + + if ($new.is('ol') && $new.children(':last').is('ol')) { + $new = $new.children().children(':last'); + } else if ($new.is('ol')) { + $new = $new.children(':last'); + } + + break; + + case 40: // down + $new = $old.next(); + if (!$new.length) { + $new = $old.parent().parent().next(); + } + if ($new.is('ol')) { + $new = $new.children(':first'); + } + break; + } + + if ($new.is('li')) { + $old.removeClass('selected'); + $new.addClass('selected'); + } else if (e.keyCode == 38) { + $(window).unbind(e); + $("#textfilter input").focus(); + } + }); + }); +} + /* Configures the text filter */ function configureTextFilter() { scheduler.add("init", function() { - $("#filter").append("<div id='textfilter'><span class='pre'/><span class='input'><input type='text' accesskey='/'/></span><span class='post'/></div>"); + $("#filter").append("<div id='textfilter'><span class='pre'/><span class='input'><input id='index-input' type='text' accesskey='/'/></span><span class='post'/></div>"); printAlphabet(); var input = $("#textfilter input"); resizeFilterBlock(); - input.bind("keyup", function(event) { + input.bind("keydown", function(event) { if (event.keyCode == 27) { // escape input.attr("value", ""); } - textFilter(); + if (event.keyCode == 9) { // tab + $("#template").contents().find("#mbrsel-input").focus(); + input.attr("value", ""); + return false; + } + if (event.keyCode == 40) { // down arrow + $(window).unbind("keydown"); + keyboardScrolldownLeftPane(); + return false; + } + textFilter(); }); input.focus(function(event) { input.select(); }); }); diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js index fd5a981cb0..33fbd83bee 100644 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js @@ -25,9 +25,37 @@ $(document).ready(function(){ // Member filter box var input = $("#textfilter input"); input.bind("keyup", function(event) { - if (event.keyCode == 27) - input.val(""); // escape key - filter(true); + + switch ( event.keyCode ) { + + case 27: // escape key + input.val(""); + filter(true); + break; + + case 38: // up + input.val(""); + filter(false); + window.scrollTo(0, $("body").offset().top); + input.focus(); + break; + + case 33: //page up + input.val(""); + filter(false); + break; + + case 34: //page down + input.val(""); + filter(false); + break; + + default: + window.scrollTo(0, $("#mbrsel").offset().top); + filter(true); + break; + + } }); input.focus(function(event) { input.select(); @@ -37,13 +65,13 @@ $(document).ready(function(){ filter(); }); $(document).keydown(function(event) { - if(!event.altKey && !event.ctrlKey && - (event.keyCode == 27 || (event.keyCode >= 48 && event.keyCode <= 90)) && - document.activeElement != $("#textfilter input")[0]) { - $("#textfilter input").focus(); + + if (event.keyCode == 9) { // tab + $("#index-input", window.parent.document).focus(); + input.attr("value", ""); + return false; } }); - $("#textfilter input").focus(); $("#linearization li").click(function(){ if ($(this).hasClass("in")) { @@ -251,7 +279,8 @@ function initInherit() { }); }; -function filter(scrollToMember) { +/* filter used to take boolean scrollToMember */ +function filter() { var query = $.trim($("#textfilter input").val()).toLowerCase(); query = query.replace(/[-[\]{}()*+?.,\\^$|#]/g, "\\$&").replace(/\s+/g, "|"); var queryRegExp = new RegExp(query, "i"); @@ -327,10 +356,6 @@ function filter(scrollToMember) { members.hide(); }; - if (scrollToMember) { - window.scrollTo(0, $("#mbrsel").offset().top); - } - return false; }; diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 7c61ec032e..7373a610d7 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -615,12 +615,11 @@ abstract class ClassfileParser { // sealed java enums (experimental) if (isEnum && opt.experimental) { - // need to give singleton type - sym setInfo info.narrow - if (!sym.superClass.isSealed) - sym.superClass setFlag SEALED + val enumClass = sym.owner.linkedClassOfClass + if (!enumClass.isSealed) + enumClass setFlag (SEALED | ABSTRACT) - sym.superClass addChild sym + enumClass addChild sym } } } diff --git a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala index cb7d64b9fc..e5fc98f23c 100644 --- a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala +++ b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala @@ -308,8 +308,7 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure => // body until now, because the typer knows that Any has no // constructor and won't accept a call to super.init. assert((clazz isSubClass AnyValClass) || clazz.info.parents.isEmpty, clazz) - val superCall = Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), Nil) - Block(List(superCall), expr) + Block(List(Apply(gen.mkSuperSelect, Nil)), expr) case Block(stats, expr) => // needs `hasSymbol` check because `supercall` could be a block (named / default args) diff --git a/src/compiler/scala/tools/nsc/transform/InfoTransform.scala b/src/compiler/scala/tools/nsc/transform/InfoTransform.scala index 0905fa86c6..880f0f0157 100644 --- a/src/compiler/scala/tools/nsc/transform/InfoTransform.scala +++ b/src/compiler/scala/tools/nsc/transform/InfoTransform.scala @@ -37,7 +37,7 @@ trait InfoTransform extends Transform { val changesBaseClasses = InfoTransform.this.changesBaseClasses def transform(sym: Symbol, tpe: Type): Type = transformInfo(sym, tpe) } - infoTransformers.insert(infoTransformer) + infoTransformers insert infoTransformer } } } diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 07a10ecb1f..f2e109a5ad 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -1388,26 +1388,23 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { tree case Select(qual, name) => - debuglog("[%s] looking at Select: %s sym: %s: %s [tree.tpe: %s]".format( - tree.pos.safeLine, tree, symbol, symbol.info, tree.tpe)) + debuglog("specializing Select %s [tree.tpe: %s]".format(symbol.defString, tree.tpe)) //log("!!! select " + tree + " -> " + symbol.info + " specTypeVars: " + specializedTypeVars(symbol.info)) if (specializedTypeVars(symbol.info).nonEmpty && name != nme.CONSTRUCTOR) { // log("!!! unifying " + (symbol, symbol.tpe) + " and " + (tree, tree.tpe)) val env = unify(symbol.tpe, tree.tpe, emptyEnv, false) // log("!!! found env: " + env + "; overloads: " + overloads(symbol)) - debuglog("checking for rerouting: " + tree + " with sym.tpe: " + symbol.tpe + " tree.tpe: " + tree.tpe + " env: " + env) if (!env.isEmpty) { + // debuglog("checking for rerouting: " + tree + " with sym.tpe: " + symbol.tpe + " tree.tpe: " + tree.tpe + " env: " + env) val specMember = overload(symbol, env) - //log("!!! found member: " + specMember) if (specMember.isDefined) { - // log("** routing " + tree + " to " + specMember.get.sym.fullName) localTyper.typedOperator(atPos(tree.pos)(Select(transform(qual), specMember.get.sym.name))) - } else { + } + else { val qual1 = transform(qual) val specMember = qual1.tpe.member(specializedName(symbol, env)).suchThat(_.tpe matches subst(env, symbol.tpe)) if (specMember ne NoSymbol) { - // log("** using spec member " + specMember + ": " + specMember.tpe) val tree1 = atPos(tree.pos)(Select(qual1, specMember)) if (specMember.isMethod) localTyper.typedOperator(tree1) @@ -1450,10 +1447,8 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { // log("--> method: " + ddef + " in " + ddef.symbol.owner + ", " + info(symbol)) if (symbol.isConstructor) { - val t = atOwner(symbol) { - val superRef: Tree = Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR) - forwardCtorCall(tree.pos, superRef, vparamss, symbol.owner) - } + val t = atOwner(symbol)(forwardCtorCall(tree.pos, gen.mkSuperSelect, vparamss, symbol.owner)) + if (symbol.isPrimaryConstructor) localTyper.typedPos(symbol.pos)(deriveDefDef(tree)(_ => Block(List(t), Literal(Constant())))) else // duplicate the original constructor diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index f01cbade18..8af12f3f10 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -77,9 +77,17 @@ abstract class UnCurry extends InfoTransform private var inConstructorFlag = 0L private val byNameArgs = mutable.HashSet[Tree]() private val noApply = mutable.HashSet[Tree]() - private val newMembers = mutable.ArrayBuffer[Tree]() + private val newMembers = mutable.Map[Symbol, mutable.Buffer[Tree]]() private val repeatedParams = mutable.Map[Symbol, List[ValDef]]() + /** Add a new synthetic member for `currentOwner` */ + private def addNewMember(t: Tree): Unit = + newMembers.getOrElseUpdate(currentOwner, mutable.Buffer()) += t + + /** Process synthetic members for `owner`. They are removed form the `newMembers` as a side-effect. */ + @inline private def useNewMembers[T](owner: Symbol)(f: List[Tree] => T): T = + f(newMembers.remove(owner).getOrElse(Nil).toList) + @inline private def withInPattern[T](value: Boolean)(body: => T): T = { inPattern = value try body @@ -573,7 +581,7 @@ abstract class UnCurry extends InfoTransform val vparamssNoRhs = dd.vparamss mapConserve (_ mapConserve {p => treeCopy.ValDef(p, p.mods, p.name, p.tpt, EmptyTree) }) - + if (dd.symbol hasAnnotation VarargsClass) saveRepeatedParams(dd) withNeedLift(false) { @@ -680,9 +688,8 @@ abstract class UnCurry extends InfoTransform tree match { /* Some uncurry post transformations add members to templates. - * When inside a template, the following sequence is available: - * - newMembers - * Any entry in this sequence will be added into the template + * + * Members registered by `addMembers` for the current template are added * once the template transformation has finished. * * In particular, this case will add: @@ -690,8 +697,10 @@ abstract class UnCurry extends InfoTransform */ case Template(_, _, _) => localTyper = typer.atOwner(tree, currentClass) - try deriveTemplate(tree)(transformTrees(newMembers.toList) ::: _) - finally newMembers.clear() + useNewMembers(currentClass) { + newMembers => + deriveTemplate(tree)(transformTrees(newMembers) ::: _) + } case dd @ DefDef(_, _, _, vparamss0, _, rhs0) => val flatdd = copyDefDef(dd)( @@ -763,7 +772,7 @@ abstract class UnCurry extends InfoTransform /* Called during post transform, after the method argument lists have been flattened. * It looks for the method in the `repeatedParams` map, and generates a Java-style - * varargs forwarder. It then adds the forwarder to the `newMembers` sequence. + * varargs forwarder. */ private def addJavaVarargsForwarders(dd: DefDef, flatdd: DefDef): DefDef = { if (!dd.symbol.hasAnnotation(VarargsClass) || !repeatedParams.contains(dd.symbol)) @@ -840,8 +849,7 @@ abstract class UnCurry extends InfoTransform case None => // enter symbol into scope currentClass.info.decls enter forwsym - // add the method to `newMembers` - newMembers += forwtree + addNewMember(forwtree) } flatdd diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index cb01faf619..affa9cd63b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -690,34 +690,44 @@ trait ContextErrors { setError(tree) } - def NoBestMethodAlternativeError(tree: Tree, argtpes: List[Type], pt: Type) = { + // side-effect on the tree, break the overloaded type cycle in infer + @inline + private def setErrorOnLastTry(lastTry: Boolean, tree: Tree) = if (lastTry) setError(tree) + + def NoBestMethodAlternativeError(tree: Tree, argtpes: List[Type], pt: Type, lastTry: Boolean) = { issueNormalTypeError(tree, applyErrorMsg(tree, " cannot be applied to ", argtpes, pt)) // since inferMethodAlternative modifies the state of the tree // we have to set the type of tree to ErrorType only in the very last - // fallback action that is done in the inference (tracking it manually is error prone). + // fallback action that is done in the inference. // This avoids entering infinite loop in doTypeApply. - if (implicitly[Context].reportErrors) setError(tree) + setErrorOnLastTry(lastTry, tree) } def AmbiguousMethodAlternativeError(tree: Tree, pre: Type, best: Symbol, - firstCompeting: Symbol, argtpes: List[Type], pt: Type) = { - val msg0 = - "argument types " + argtpes.mkString("(", ",", ")") + - (if (pt == WildcardType) "" else " and expected result type " + pt) - val (pos, msg) = ambiguousErrorMsgPos(tree.pos, pre, best, firstCompeting, msg0) - // discover last attempt in a similar way as for NoBestMethodAlternativeError - if (implicitly[Context].ambiguousErrors) setError(tree) - issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(tree, pos, msg)) + firstCompeting: Symbol, argtpes: List[Type], pt: Type, lastTry: Boolean) = { + + if (!(argtpes exists (_.isErroneous)) && !pt.isErroneous) { + val msg0 = + "argument types " + argtpes.mkString("(", ",", ")") + + (if (pt == WildcardType) "" else " and expected result type " + pt) + val (pos, msg) = ambiguousErrorMsgPos(tree.pos, pre, best, firstCompeting, msg0) + issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(tree, pos, msg)) + setErrorOnLastTry(lastTry, tree) + } else setError(tree) // do not even try further attempts because they should all fail + // even if this is not the last attempt (because of the SO's possibility on the horizon) + } - def NoBestExprAlternativeError(tree: Tree, pt: Type) = + def NoBestExprAlternativeError(tree: Tree, pt: Type, lastTry: Boolean) = { issueNormalTypeError(tree, withAddendum(tree.pos)(typeErrorMsg(tree.symbol.tpe, pt, isPossiblyMissingArgs(tree.symbol.tpe, pt)))) + setErrorOnLastTry(lastTry, tree) + } - def AmbiguousExprAlternativeError(tree: Tree, pre: Type, best: Symbol, firstCompeting: Symbol, pt: Type) = { + def AmbiguousExprAlternativeError(tree: Tree, pre: Type, best: Symbol, firstCompeting: Symbol, pt: Type, lastTry: Boolean) = { val (pos, msg) = ambiguousErrorMsgPos(tree.pos, pre, best, firstCompeting, "expected type " + pt) - setError(tree) issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(tree, pos, msg)) + setErrorOnLastTry(lastTry, tree) } // checkBounds @@ -874,8 +884,12 @@ trait ContextErrors { val s1 = if (prevSym.isModule) "case class companion " else "" val s2 = if (prevSym.isSynthetic) "(compiler-generated) " + s1 else "" val s3 = if (prevSym.isCase) "case class " + prevSym.name else "" + prevSym + val where = if (currentSym.owner.isPackageClass != prevSym.owner.isPackageClass) { + val inOrOut = if (prevSym.owner.isPackageClass) "outside of" else "in" + " %s package object %s".format(inOrOut, ""+prevSym.effectiveOwner.name) + } else "" - issueSymbolTypeError(currentSym, prevSym.name + " is already defined as " + s2 + s3) + issueSymbolTypeError(currentSym, prevSym.name + " is already defined as " + s2 + s3 + where) } def MaxParametersCaseClassError(tree: Tree) = diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 0924789948..f4f081252f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -438,8 +438,8 @@ trait Contexts { self: Analyzer => def enclosingContextChain: List[Context] = this :: outer.enclosingContextChain - override def toString = "Context(%s@%s unit=%s scope=%s errors=%b)".format( - owner.fullName, tree.shortClass, unit, scope.##, hasErrors + override def toString = "Context(%s@%s unit=%s scope=%s errors=%b, reportErrors=%b, throwErrors=%b)".format( + owner.fullName, tree.shortClass, unit, scope.##, hasErrors, reportErrors, throwErrors ) /** Is `sub` a subclass of `base` or a companion object of such a subclass? */ @@ -598,16 +598,16 @@ trait Contexts { self: Analyzer => * it is accessible, and if it is imported there is not already a local symbol * with the same names. Local symbols override imported ones. This fixes #2866. */ - private def isQualifyingImplicit(sym: Symbol, pre: Type, imported: Boolean) = + private def isQualifyingImplicit(name: Name, sym: Symbol, pre: Type, imported: Boolean) = sym.isImplicit && isAccessible(sym, pre) && !(imported && { - val e = scope.lookupEntry(sym.name) + val e = scope.lookupEntry(name) (e ne null) && (e.owner == scope) }) private def collectImplicits(syms: List[Symbol], pre: Type, imported: Boolean = false): List[ImplicitInfo] = - for (sym <- syms if isQualifyingImplicit(sym, pre, imported)) yield + for (sym <- syms if isQualifyingImplicit(sym.name, sym, pre, imported)) yield new ImplicitInfo(sym.name, pre, sym) private def collectImplicitImports(imp: ImportInfo): List[ImplicitInfo] = { @@ -621,7 +621,7 @@ trait Contexts { self: Analyzer => var impls = collect(sels1) filter (info => info.name != from) if (to != nme.WILDCARD) { for (sym <- imp.importedSymbol(to).alternatives) - if (isQualifyingImplicit(sym, pre, imported = true)) + if (isQualifyingImplicit(to, sym, pre, imported = true)) impls = new ImplicitInfo(to, pre, sym) :: impls } impls diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala index 2574a1d241..b7a6ea677e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala @@ -143,8 +143,8 @@ abstract class Duplicators extends Analyzer { else sym - private def invalidate(tree: Tree) { - debuglog("attempting to invalidate " + tree.symbol + ", owner - " + (if (tree.symbol ne null) tree.symbol.owner else "<NULL>")) + private def invalidate(tree: Tree, owner: Symbol = NoSymbol) { + debuglog("attempting to invalidate " + tree.symbol) if (tree.isDef && tree.symbol != NoSymbol) { debuglog("invalid " + tree.symbol) invalidSyms(tree.symbol) = tree @@ -158,18 +158,20 @@ abstract class Duplicators extends Analyzer { newsym.setInfo(fixType(ldef.symbol.info)) ldef.symbol = newsym debuglog("newsym: " + newsym + " info: " + newsym.info) - + case vdef @ ValDef(mods, name, _, rhs) if mods.hasFlag(Flags.LAZY) => debuglog("ValDef " + name + " sym.info: " + vdef.symbol.info) invalidSyms(vdef.symbol) = vdef - val newsym = vdef.symbol.cloneSymbol(context.owner) + val newowner = if (owner != NoSymbol) owner else context.owner + val newsym = vdef.symbol.cloneSymbol(newowner) newsym.setInfo(fixType(vdef.symbol.info)) vdef.symbol = newsym - debuglog("newsym: " + newsym + " info: " + newsym.info) - + debuglog("newsym: " + newsym + " info: " + newsym.info + ", owner: " + newsym.owner + ", " + newsym.owner.isClass) + if (newsym.owner.isClass) newsym.owner.info.decls enter newsym + case DefDef(_, name, tparams, vparamss, _, rhs) => // invalidate parameters - invalidate(tparams ::: vparamss.flatten) + invalidateAll(tparams ::: vparamss.flatten) tree.symbol = NoSymbol case _ => @@ -178,14 +180,14 @@ abstract class Duplicators extends Analyzer { } } - private def invalidate(stats: List[Tree]) { - stats foreach invalidate + private def invalidateAll(stats: List[Tree], owner: Symbol = NoSymbol) { + stats.foreach(invalidate(_, owner)) } def retypedMethod(ddef: DefDef, oldThis: Symbol, newThis: Symbol): Tree = { oldClassOwner = oldThis newClassOwner = newThis - invalidate(ddef.tparams) + invalidateAll(ddef.tparams) mforeach(ddef.vparamss) { vdef => invalidate(vdef) vdef.tpe = null @@ -239,15 +241,15 @@ abstract class Duplicators extends Analyzer { case Block(stats, res) => debuglog("invalidating block") - invalidate(stats) + invalidateAll(stats) invalidate(res) tree.tpe = null super.typed(tree, mode, pt) case ClassDef(_, _, _, tmpl @ Template(parents, _, stats)) => - // log("invalidating classdef " + tree.tpe) + // log("invalidating classdef " + tree) tmpl.symbol = tree.symbol.newLocalDummy(tree.pos) - invalidate(stats) + invalidateAll(stats, tree.symbol) tree.tpe = null super.typed(tree, mode, pt) diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 43b96314b0..217cadaab8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -558,7 +558,11 @@ trait Implicits { val itree = atPos(pos.focus) { if (info.pre == NoPrefix) Ident(info.name) - else Select(gen.mkAttributedQualifier(info.pre), info.name) + else { + // SI-2405 Not info.name, which might be an aliased import + val implicitMemberName = info.sym.name + Select(gen.mkAttributedQualifier(info.pre), implicitMemberName) + } } printTyping("typedImplicit1 %s, pt=%s, from implicit %s:%s".format( typeDebug.ptTree(itree), wildPt, info.name, info.tpe) @@ -933,7 +937,7 @@ trait Implicits { } case None => if (pre.isStable) { - val companion = sym.companionModule + val companion = companionSymbolOf(sym, context) companion.moduleClass match { case mc: ModuleClassSymbol => val infos = diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index e91050987b..85c2aebfab 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -305,9 +305,21 @@ trait Infer { } - def isCompatible(tp: Type, pt: Type): Boolean = { + /** "Compatible" means conforming after conversions. + * "Raising to a thunk" is not implicit; therefore, for purposes of applicability and + * specificity, an arg type `A` is considered compatible with cbn formal parameter type `=>A`. + * For this behavior, the type `pt` must have cbn params preserved; for instance, `formalTypes(removeByName = false)`. + * + * `isAsSpecific` no longer prefers A by testing applicability to A for both m(A) and m(=>A) + * since that induces a tie between m(=>A) and m(=>A,B*) [SI-3761] + */ + private def isCompatible(tp: Type, pt: Type): Boolean = { + def isCompatibleByName(tp: Type, pt: Type): Boolean = pt match { + case TypeRef(_, ByNameParamClass, List(res)) if !isByNameParamType(tp) => isCompatible(tp, res) + case _ => false + } val tp1 = normalize(tp) - (tp1 weak_<:< pt) || isCoercible(tp1, pt) + (tp1 weak_<:< pt) || isCoercible(tp1, pt) || isCompatibleByName(tp, pt) } def isCompatibleArgs(tps: List[Type], pts: List[Type]) = (tps corresponds pts)(isCompatible) @@ -662,7 +674,7 @@ trait Infer { case ExistentialType(tparams, qtpe) => isApplicable(undetparams, qtpe, argtpes0, pt) case MethodType(params, _) => - val formals = formalTypes(params map { _.tpe }, argtpes0.length) + val formals = formalTypes(params map { _.tpe }, argtpes0.length, removeByName = false) def tryTupleApply: Boolean = { // if 1 formal, 1 argtpe (a tuple), otherwise unmodified argtpes0 @@ -1435,10 +1447,10 @@ trait Infer { * If no alternative matches `pt`, take the parameterless one anyway. */ def inferExprAlternative(tree: Tree, pt: Type) = tree.tpe match { - case OverloadedType(pre, alts) => tryTwice { - val alts0 = alts filter (alt => isWeaklyCompatible(pre.memberType(alt), pt)) - val secondTry = alts0.isEmpty - val alts1 = if (secondTry) alts else alts0 + case OverloadedType(pre, alts) => tryTwice { isSecondTry => + val alts0 = alts filter (alt => isWeaklyCompatible(pre.memberType(alt), pt)) + val noAlternatives = alts0.isEmpty + val alts1 = if (noAlternatives) alts else alts0 //println("trying "+alts1+(alts1 map (_.tpe))+(alts1 map (_.locationString))+" for "+pt) def improves(sym1: Symbol, sym2: Symbol): Boolean = @@ -1466,10 +1478,10 @@ trait Infer { } } // todo: missing test case - NoBestExprAlternativeError(tree, pt) + NoBestExprAlternativeError(tree, pt, isSecondTry) } else if (!competing.isEmpty) { - if (secondTry) { NoBestExprAlternativeError(tree, pt); setError(tree) } - else if (!pt.isErroneous) AmbiguousExprAlternativeError(tree, pre, best, competing.head, pt) + if (noAlternatives) NoBestExprAlternativeError(tree, pt, isSecondTry) + else if (!pt.isErroneous) AmbiguousExprAlternativeError(tree, pre, best, competing.head, pt, isSecondTry) } else { // val applicable = alts1 filter (alt => // global.typer.infer.isWeaklyCompatible(pre.memberType(alt), pt)) @@ -1548,10 +1560,10 @@ trait Infer { * assignment expression. */ def inferMethodAlternative(tree: Tree, undetparams: List[Symbol], - argtpes: List[Type], pt0: Type, varArgsOnly: Boolean = false): Unit = tree.tpe match { + argtpes: List[Type], pt0: Type, varArgsOnly: Boolean = false, lastInferAttempt: Boolean = true): Unit = tree.tpe match { case OverloadedType(pre, alts) => val pt = if (pt0.typeSymbol == UnitClass) WildcardType else pt0 - tryTwice { + tryTwice { isSecondTry => debuglog("infer method alt "+ tree.symbol +" with alternatives "+ (alts map pre.memberType) +", argtpes = "+ argtpes +", pt = "+ pt) @@ -1573,13 +1585,10 @@ trait Infer { if (improves(alt, best)) alt else best) val competing = applicable.dropWhile(alt => best == alt || improves(best, alt)) if (best == NoSymbol) { - if (pt == WildcardType) NoBestMethodAlternativeError(tree, argtpes, pt) - else inferMethodAlternative(tree, undetparams, argtpes, WildcardType) + if (pt == WildcardType) NoBestMethodAlternativeError(tree, argtpes, pt, isSecondTry && lastInferAttempt) + else inferMethodAlternative(tree, undetparams, argtpes, WildcardType, lastInferAttempt = isSecondTry) } else if (!competing.isEmpty) { - if (!(argtpes exists (_.isErroneous)) && !pt.isErroneous) - AmbiguousMethodAlternativeError(tree, pre, best, competing.head, argtpes, pt) - else setError(tree) - () + AmbiguousMethodAlternativeError(tree, pre, best, competing.head, argtpes, pt, isSecondTry && lastInferAttempt) } else { // checkNotShadowed(tree.pos, pre, best, applicable) tree.setSymbol(best).setType(pre.memberType(best)) @@ -1593,29 +1602,28 @@ trait Infer { * * @param infer ... */ - def tryTwice(infer: => Unit): Unit = { + def tryTwice(infer: Boolean => Unit): Unit = { if (context.implicitsEnabled) { val saved = context.state var fallback = false context.setBufferErrors() - val res = try { - context.withImplicitsDisabled(infer) + try { + context.withImplicitsDisabled(infer(false)) if (context.hasErrors) { fallback = true context.restoreState(saved) context.flushBuffer() - infer + infer(true) } } catch { case ex: CyclicReference => throw ex case ex: TypeError => // recoverable cyclic references context.restoreState(saved) - if (!fallback) infer else () + if (!fallback) infer(true) else () } context.restoreState(saved) - res } - else infer + else infer(true) } /** Assign <code>tree</code> the type of all polymorphic alternatives diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 2f2278251b..4eba665b93 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -473,12 +473,8 @@ trait Namers extends MethodSynthesis { if (from != nme.WILDCARD && base != ErrorType) { if (isValid(from)) { - if (currentRun.compileSourceFor(expr, from)) { - // side effecting, apparently - typeSig(tree) - } // for Java code importing Scala objects - else if (!nme.isModuleName(from) || isValid(nme.stripModuleSuffix(from))) { + if (!nme.isModuleName(from) || isValid(nme.stripModuleSuffix(from))) { typer.TyperErrorGen.NotAMemberError(tree, expr, from) typer.infer.setError(tree) } @@ -636,7 +632,7 @@ trait Namers extends MethodSynthesis { classAndNamerOfModule(m) = (tree, null) } val owner = tree.symbol.owner - if (settings.lint.value && owner.isPackageObjectClass) { + if (settings.lint.value && owner.isPackageObjectClass && !mods.isImplicit) { context.unit.warning(tree.pos, "it is not recommended to define classes/objects inside of package objects.\n" + "If possible, define " + tree.symbol + " in " + owner.skipPackageObject + " instead." @@ -1306,14 +1302,18 @@ trait Namers extends MethodSynthesis { if (expr1.symbol != null && expr1.symbol.isRootPackage) RootImportError(tree) - val newImport = treeCopy.Import(tree, expr1, selectors).asInstanceOf[Import] - checkSelectors(newImport) - transformed(tree) = newImport - // copy symbol and type attributes back into old expression - // so that the structure builder will find it. - expr.symbol = expr1.symbol - expr.tpe = expr1.tpe - ImportType(expr1) + if (expr1.isErrorTyped) + ErrorType + else { + val newImport = treeCopy.Import(tree, expr1, selectors).asInstanceOf[Import] + checkSelectors(newImport) + transformed(tree) = newImport + // copy symbol and type attributes back into old expression + // so that the structure builder will find it. + expr.symbol = expr1.symbol + expr.tpe = expr1.tpe + ImportType(expr1) + } } val result = diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index 88e464a1f4..932e4548ef 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -260,19 +260,18 @@ trait NamesDefaults { self: Analyzer => def argValDefs(args: List[Tree], paramTypes: List[Type], blockTyper: Typer): List[ValDef] = { val context = blockTyper.context val symPs = map2(args, paramTypes)((arg, tpe) => { - val byName = isByNameParamType(tpe) - val (argTpe, repeated) = - if (isScalaRepeatedParamType(tpe)) arg match { - case Typed(expr, Ident(tpnme.WILDCARD_STAR)) => - (expr.tpe, true) - case _ => - (seqType(arg.tpe), true) - } else (arg.tpe, false) - val s = context.owner.newValue(unit.freshTermName("x$"), arg.pos) - val valType = if (byName) functionType(List(), argTpe) - else if (repeated) argTpe - else argTpe - s.setInfo(valType) + val byName = isByNameParamType(tpe) + val repeated = isScalaRepeatedParamType(tpe) + val argTpe = ( + if (repeated) arg match { + case Typed(expr, Ident(tpnme.WILDCARD_STAR)) => expr.tpe + case _ => seqType(arg.tpe) + } + else arg.tpe + ).widen // have to widen or types inferred from literal defaults will be singletons + val s = context.owner.newValue(unit.freshTermName("x$"), arg.pos) setInfo ( + if (byName) functionType(Nil, argTpe) else argTpe + ) (context.scope.enter(s), byName, repeated) }) map2(symPs, args) { @@ -482,6 +481,10 @@ trait NamesDefaults { self: Analyzer => try typer.silent { tpr => val res = tpr.typed(arg, subst(paramtpe)) // better warning for SI-5044: if `silent` was not actually silent give a hint to the user + // [H]: the reason why `silent` is not silent is because the cyclic reference exception is + // thrown in a context completely different from `context` here. The exception happens while + // completing the type, and TypeCompleter is created/run with a non-silent Namer `context` + // and there is at the moment no way to connect the two unless we go through some global state. if (errsBefore < reporter.ERROR.count) WarnAfterNonSilentRecursiveInference(param, arg)(context) res diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index 61e02edaff..4d66fb5617 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -12,6 +12,8 @@ import Flags.{MUTABLE, METHOD, LABEL, SYNTHETIC} import language.postfixOps import scala.tools.nsc.transform.TypingTransformers import scala.tools.nsc.transform.Transform +import scala.collection.mutable.HashSet +import scala.collection.mutable.HashMap /** Translate pattern matching. * @@ -306,7 +308,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // it tests the type, checks the outer pointer and casts to the expected type // TODO: the outer check is mandated by the spec for case classes, but we do it for user-defined unapplies as well [SPEC] // (the prefix of the argument passed to the unapply must equal the prefix of the type of the binder) - val treeMaker = TypeTestTreeMaker(patBinder, extractor.paramType, pos) + val treeMaker = TypeTestTreeMaker(patBinder, patBinder, extractor.paramType, extractor.paramType)(pos, extractorArgTypeTest = true) (List(treeMaker), treeMaker.nextBinder) } else (Nil, patBinder) @@ -364,7 +366,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // must treat Typed and Bind together -- we need to know the patBinder of the Bind pattern to get at the actual type case MaybeBoundTyped(subPatBinder, pt) => // a typed pattern never has any subtrees - noFurtherSubPats(TypeAndEqualityTestTreeMaker(subPatBinder, patBinder, pt, pos)) + noFurtherSubPats(TypeTestTreeMaker(subPatBinder, patBinder, pt, glb(List(patBinder.info.widen, pt)).normalize)(pos)) /** A pattern binder x@p consists of a pattern variable x and a pattern p. The type of the variable x is the static type T of the pattern p. @@ -562,8 +564,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL protected def lengthGuard(binder: Symbol): Option[Tree] = // no need to check unless it's an unapplySeq and the minimal length is non-trivially satisfied - if (!isSeq || (expectedLength < minLenToCheck)) None - else { import CODE._ + checkedLength map { expectedLength => import CODE._ // `binder.lengthCompare(expectedLength)` def checkExpectedLength = (seqTree(binder) DOT seqLenCmp)(LIT(expectedLength)) @@ -575,8 +576,14 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL else _ INT_== _ // `if (binder != null && $checkExpectedLength [== | >=] 0) then else zero` - Some((seqTree(binder) ANY_!= NULL) AND compareOp(checkExpectedLength, ZERO)) + (seqTree(binder) ANY_!= NULL) AND compareOp(checkExpectedLength, ZERO) } + + def checkedLength: Option[Int] = + // no need to check unless it's an unapplySeq and the minimal length is non-trivially satisfied + if (!isSeq || (expectedLength < minLenToCheck)) None + else Some(expectedLength) + } // TODO: to be called when there's a def unapplyProd(x: T): U @@ -650,7 +657,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // the extractor call (applied to the binder bound by the flatMap corresponding to the previous (i.e., enclosing/outer) pattern) val extractorApply = atPos(pos)(spliceApply(patBinderOrCasted)) val binder = freshSym(pos, pureType(resultInMonad)) // can't simplify this when subPatBinders.isEmpty, since UnitClass.tpe is definitely wrong when isSeq, and resultInMonad should always be correct since it comes directly from the extractor's result type - ExtractorTreeMaker(extractorApply, lengthGuard(binder), binder, Substitution(subPatBinders, subPatRefs(binder)))(resultType.typeSymbol == BooleanClass) + ExtractorTreeMaker(extractorApply, lengthGuard(binder), binder, Substitution(subPatBinders, subPatRefs(binder)))(resultType.typeSymbol == BooleanClass, checkedLength, patBinderOrCasted) } override protected def seqTree(binder: Symbol): Tree = @@ -762,7 +769,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL val (fromFiltered, toFiltered) = (from, to).zipped filter { (f, t) => !other.from.contains(f) } new Substitution(other.from ++ fromFiltered, other.to.map(apply) ++ toFiltered) // a quick benchmarking run indicates the `.map(apply)` is not too costly } - override def toString = (from zip to) mkString("Substitution(", ", ", ")") + override def toString = (from.map(_.name) zip to) mkString("Substitution(", ", ", ")") } object EmptySubstitution extends Substitution(Nil, Nil) { @@ -775,7 +782,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // the making of the trees /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// trait TreeMakers extends TypedSubstitution { self: CodegenCore => - def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): (List[List[TreeMaker]], List[Tree]) = + def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type, unchecked: Boolean): (List[List[TreeMaker]], List[Tree]) = (cases, Nil) def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type, matchFailGenOverride: Option[Tree => Tree]): Option[Tree] = @@ -819,11 +826,13 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL case class BodyTreeMaker(body: Tree, matchPt: Type) extends TreeMaker with NoNewBinders { def chainBefore(next: Tree)(casegen: Casegen): Tree = // assert(next eq EmptyTree) atPos(body.pos)(casegen.one(substitution(body))) // since SubstOnly treemakers are dropped, need to do it here + override def toString = "B"+(body, matchPt) } case class SubstOnlyTreeMaker(prevBinder: Symbol, nextBinder: Symbol) extends TreeMaker { val localSubstitution = Substitution(prevBinder, CODE.REF(nextBinder)) def chainBefore(next: Tree)(casegen: Casegen): Tree = substitution(next) + override def toString = "S"+ localSubstitution } abstract class FunTreeMaker extends TreeMaker { @@ -851,7 +860,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL * the function's body is determined by the next TreeMaker * in this function's body, and all the subsequent ones, references to the symbols in `from` will be replaced by the corresponding tree in `to` */ - case class ExtractorTreeMaker(extractor: Tree, extraCond: Option[Tree], nextBinder: Symbol, localSubstitution: Substitution)(extractorReturnsBoolean: Boolean) extends FunTreeMaker { + case class ExtractorTreeMaker(extractor: Tree, extraCond: Option[Tree], nextBinder: Symbol, localSubstitution: Substitution)(extractorReturnsBoolean: Boolean, val checkedLength: Option[Int], val prevBinder: Symbol) extends FunTreeMaker { def chainBefore(next: Tree)(casegen: Casegen): Tree = { val condAndNext = extraCond map (casegen.ifThenElseZero(_, next)) getOrElse next atPos(extractor.pos)( @@ -860,136 +869,157 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL ) } - override def toString = "X"+(extractor, nextBinder) + override def toString = "X"+(extractor, nextBinder.name) } // TODO: allow user-defined unapplyProduct - case class ProductExtractorTreeMaker(prevBinder: Symbol, extraCond: Option[Tree], localSubstitution: Substitution) extends TreeMaker { import CODE._ + case class ProductExtractorTreeMaker(prevBinder: Symbol, extraCond: Option[Tree], localSubstitution: Substitution) extends FunTreeMaker { import CODE._ + val nextBinder = prevBinder // just passing through def chainBefore(next: Tree)(casegen: Casegen): Tree = { val nullCheck = REF(prevBinder) OBJ_NE NULL val cond = extraCond map (nullCheck AND _) getOrElse nullCheck casegen.ifThenElseZero(cond, substitution(next)) } - override def toString = "P"+(prevBinder, extraCond getOrElse "", localSubstitution) + override def toString = "P"+(prevBinder.name, extraCond getOrElse "", localSubstitution) } - // tack an outer test onto `cond` if binder.info and expectedType warrant it - def maybeWithOuterCheck(binder: Symbol, expectedTp: Type)(cond: Tree): Tree = { import CODE._ - if ( !((expectedTp.prefix eq NoPrefix) || expectedTp.prefix.typeSymbol.isPackageClass) - && needsOuterTest(expectedTp, binder.info, matchOwner)) { - val expectedPrefix = expectedTp.prefix match { - case ThisType(clazz) => THIS(clazz) - case pre => REF(pre.prefix, pre.termSymbol) - } + // typetag-based tests are inserted by the type checker + def needsTypeTest(tp: Type, pt: Type): Boolean = !(tp <:< pt) + + object TypeTestTreeMaker { + // factored out so that we can consistently generate other representations of the tree that implements the test + // (e.g. propositions for exhaustivity and friends, boolean for isPureTypeTest) + trait TypeTestCondStrategy { + type Result + + def outerTest(testedBinder: Symbol, expectedTp: Type): Result + // TODO: can probably always widen + def typeTest(testedBinder: Symbol, expectedTp: Type): Result + def nonNullTest(testedBinder: Symbol): Result + def equalsTest(pat: Tree, testedBinder: Symbol): Result + def eqTest(pat: Tree, testedBinder: Symbol): Result + def and(a: Result, b: Result): Result + } + + object treeCondStrategy extends TypeTestCondStrategy { import CODE._ + type Result = Tree - // ExplicitOuter replaces `Select(q, outerSym) OBJ_EQ expectedPrefix` by `Select(q, outerAccessor(outerSym.owner)) OBJ_EQ expectedPrefix` - // if there's an outer accessor, otherwise the condition becomes `true` -- TODO: can we improve needsOuterTest so there's always an outerAccessor? - val outer = expectedTp.typeSymbol.newMethod(vpmName.outer) setInfo expectedTp.prefix setFlag SYNTHETIC - val outerCheck = (Select(codegen._asInstanceOf(binder, expectedTp), outer)) OBJ_EQ expectedPrefix - - // first check cond, since that should ensure we're not selecting outer on null - codegen.and(cond, outerCheck) - } - else - cond - } - - // containsUnchecked: also need to test when erasing pt loses crucial information (maybe we can recover it using a TypeTag) - def needsTypeTest(tp: Type, pt: Type): Boolean = !(tp <:< pt) // || containsUnchecked(pt) - // TODO: try to find the TypeTag for the binder's type and the expected type, and if they exists, - // check that the TypeTag of the binder's type conforms to the TypeTag of the expected type - private def typeTest(binderToTest: Symbol, expectedTp: Type, disableOuterCheck: Boolean = false, dynamic: Boolean = false): Tree = { import CODE._ - // def coreTest = - if (disableOuterCheck) codegen._isInstanceOf(binderToTest, expectedTp) else maybeWithOuterCheck(binderToTest, expectedTp)(codegen._isInstanceOf(binderToTest, expectedTp)) - // [Eugene to Adriaan] use `resolveErasureTag` instead of `findManifest`. please, provide a meaningful position - // if (opt.experimental && containsUnchecked(expectedTp)) { - // if (dynamic) { - // val expectedTpTagTree = findManifest(expectedTp, true) - // if (!expectedTpTagTree.isEmpty) - // ((expectedTpTagTree DOT "erasure".toTermName) DOT "isAssignableFrom".toTermName)(REF(binderToTest) DOT nme.getClass_) - // else - // coreTest - // } else { - // val expectedTpTagTree = findManifest(expectedTp, true) - // val binderTpTagTree = findManifest(binderToTest.info, true) - // if(!(expectedTpTagTree.isEmpty || binderTpTagTree.isEmpty)) - // coreTest AND (binderTpTagTree DOT nme.CONFORMS)(expectedTpTagTree) - // else - // coreTest - // } - // } else coreTest - } - - // need to substitute since binder may be used outside of the next extractor call (say, in the body of the case) - case class TypeTestTreeMaker(prevBinder: Symbol, nextBinderTp: Type, pos: Position) extends CondTreeMaker { - val cond = typeTest(prevBinder, nextBinderTp, dynamic = true) - val res = codegen._asInstanceOf(prevBinder, nextBinderTp) - override def toString = "TT"+(prevBinder, nextBinderTp) - } - - // implements the run-time aspects of (§8.2) (typedPattern has already done the necessary type transformations) - // TODO: normalize construction, which yields a combination of a EqualityTestTreeMaker (when necessary) and a TypeTestTreeMaker - case class TypeAndEqualityTestTreeMaker(prevBinder: Symbol, patBinder: Symbol, pt: Type, pos: Position) extends CondTreeMaker { - val nextBinderTp = glb(List(patBinder.info.widen, pt)).normalize - - /** Type patterns consist of types, type variables, and wildcards. A type pattern T is of one of the following forms: - - A reference to a class C, p.C, or T#C. - This type pattern matches any non-null instance of the given class. - Note that the prefix of the class, if it is given, is relevant for determining class instances. - For instance, the pattern p.C matches only instances of classes C which were created with the path p as prefix. - The bottom types scala.Nothing and scala.Null cannot be used as type patterns, because they would match nothing in any case. - - - A singleton type p.type. - This type pattern matches only the value denoted by the path p - (that is, a pattern match involved a comparison of the matched value with p using method eq in class AnyRef). // TODO: the actual pattern matcher uses ==, so that's what I'm using for now - // https://issues.scala-lang.org/browse/SI-4577 "pattern matcher, still disappointing us at equality time" - - - A compound type pattern T1 with ... with Tn where each Ti is a type pat- tern. - This type pattern matches all values that are matched by each of the type patterns Ti. - - - A parameterized type pattern T[a1,...,an], where the ai are type variable patterns or wildcards _. - This type pattern matches all values which match T for some arbitrary instantiation of the type variables and wildcards. - The bounds or alias type of these type variable are determined as described in (§8.3). - - - A parameterized type pattern scala.Array[T1], where T1 is a type pattern. // TODO - This type pattern matches any non-null instance of type scala.Array[U1], where U1 is a type matched by T1. - **/ - - // generate the tree for the run-time test that follows from the fact that - // a `scrut` of known type `scrutTp` is expected to have type `expectedTp` - // uses maybeWithOuterCheck to check the type's prefix - private def typeAndEqualityTest(patBinder: Symbol, pt: Type): Tree = { import CODE._ - // TODO: `null match { x : T }` will yield a check that (indirectly) tests whether `null ne null` - // don't bother (so that we don't end up with the warning "comparing values of types Null and Null using `ne' will always yield false") - def genEqualsAndInstanceOf(sym: Symbol): Tree - = codegen._equals(REF(sym), patBinder) AND typeTest(patBinder, pt.widen, disableOuterCheck = true) - - def isRefTp(tp: Type) = tp <:< AnyRefClass.tpe - - val patBinderTp = patBinder.info.widen - def isMatchUnlessNull = isRefTp(pt) && !needsTypeTest(patBinderTp, pt) - - // TODO: [SPEC] type test for Array - // TODO: use TypeTags to improve tests (for erased types we can do better when we have a TypeTag) - pt match { - case SingleType(_, sym) /*this implies sym.isStable*/ => genEqualsAndInstanceOf(sym) // TODO: [SPEC] the spec requires `eq` instead of `==` here - case ThisType(sym) if sym.isModule => genEqualsAndInstanceOf(sym) // must use == to support e.g. List() == Nil - case ThisType(sym) => REF(patBinder) OBJ_EQ This(sym) - case ConstantType(Constant(null)) if isRefTp(patBinderTp) => REF(patBinder) OBJ_EQ NULL - case ConstantType(const) => codegen._equals(Literal(const), patBinder) - case _ if isMatchUnlessNull => maybeWithOuterCheck(patBinder, pt)(REF(patBinder) OBJ_NE NULL) - case _ => typeTest(patBinder, pt) + def and(a: Result, b: Result): Result = a AND b + def typeTest(testedBinder: Symbol, expectedTp: Type) = codegen._isInstanceOf(testedBinder, expectedTp) + def nonNullTest(testedBinder: Symbol) = REF(testedBinder) OBJ_NE NULL + def equalsTest(pat: Tree, testedBinder: Symbol) = codegen._equals(pat, testedBinder) + def eqTest(pat: Tree, testedBinder: Symbol) = REF(testedBinder) OBJ_EQ pat + + def outerTest(testedBinder: Symbol, expectedTp: Type): Tree = { + val expectedOuter = expectedTp.prefix match { + case ThisType(clazz) => THIS(clazz) + case pre => REF(pre.prefix, pre.termSymbol) } + + // ExplicitOuter replaces `Select(q, outerSym) OBJ_EQ expectedPrefix` by `Select(q, outerAccessor(outerSym.owner)) OBJ_EQ expectedPrefix` + // if there's an outer accessor, otherwise the condition becomes `true` -- TODO: can we improve needsOuterTest so there's always an outerAccessor? + val outer = expectedTp.typeSymbol.newMethod(vpmName.outer) setInfo expectedTp.prefix setFlag SYNTHETIC + + (Select(codegen._asInstanceOf(testedBinder, expectedTp), outer)) OBJ_EQ expectedOuter + } } - val cond = typeAndEqualityTest(patBinder, pt) - val res = codegen._asInstanceOf(patBinder, nextBinderTp) + object pureTypeTestChecker extends TypeTestCondStrategy { + type Result = Boolean - // TODO: remove this - def isStraightTypeTest = cond match { case TypeApply(_, _) => cond.symbol == Any_isInstanceOf case _ => false } + def typeTest(testedBinder: Symbol, expectedTp: Type): Result = true - override def toString = "TET"+(patBinder, pt) + def outerTest(testedBinder: Symbol, expectedTp: Type): Result = false + def nonNullTest(testedBinder: Symbol): Result = false + def equalsTest(pat: Tree, testedBinder: Symbol): Result = false + def eqTest(pat: Tree, testedBinder: Symbol): Result = false + def and(a: Result, b: Result): Result = false // we don't and type tests, so the conjunction must include at least one false + } + } + + /** implements the run-time aspects of (§8.2) (typedPattern has already done the necessary type transformations) + * + * Type patterns consist of types, type variables, and wildcards. A type pattern T is of one of the following forms: + - A reference to a class C, p.C, or T#C. + This type pattern matches any non-null instance of the given class. + Note that the prefix of the class, if it is given, is relevant for determining class instances. + For instance, the pattern p.C matches only instances of classes C which were created with the path p as prefix. + The bottom types scala.Nothing and scala.Null cannot be used as type patterns, because they would match nothing in any case. + + - A singleton type p.type. + This type pattern matches only the value denoted by the path p + (that is, a pattern match involved a comparison of the matched value with p using method eq in class AnyRef). // TODO: the actual pattern matcher uses ==, so that's what I'm using for now + // https://issues.scala-lang.org/browse/SI-4577 "pattern matcher, still disappointing us at equality time" + + - A compound type pattern T1 with ... with Tn where each Ti is a type pat- tern. + This type pattern matches all values that are matched by each of the type patterns Ti. + + - A parameterized type pattern T[a1,...,an], where the ai are type variable patterns or wildcards _. + This type pattern matches all values which match T for some arbitrary instantiation of the type variables and wildcards. + The bounds or alias type of these type variable are determined as described in (§8.3). + + - A parameterized type pattern scala.Array[T1], where T1 is a type pattern. // TODO + This type pattern matches any non-null instance of type scala.Array[U1], where U1 is a type matched by T1. + **/ + case class TypeTestTreeMaker(prevBinder: Symbol, testedBinder: Symbol, expectedTp: Type, nextBinderTp: Type)(_pos: Position, extractorArgTypeTest: Boolean = false) extends CondTreeMaker { + val pos = _pos + + import TypeTestTreeMaker._ + // println("TTTM"+(prevBinder, extractorArgTypeTest, testedBinder, expectedTp, nextBinderTp)) + + lazy val outerTestNeeded = ( + !((expectedTp.prefix eq NoPrefix) || expectedTp.prefix.typeSymbol.isPackageClass) + && needsOuterTest(expectedTp, testedBinder.info, matchOwner)) + + // the logic to generate the run-time test that follows from the fact that + // a `prevBinder` is expected to have type `expectedTp` + // the actual tree-generation logic is factored out, since the analyses generate Cond(ition)s rather than Trees + // TODO: `null match { x : T }` will yield a check that (indirectly) tests whether `null ne null` + // don't bother (so that we don't end up with the warning "comparing values of types Null and Null using `ne' will always yield false") + def renderCondition(cs: TypeTestCondStrategy): cs.Result = { + import cs._ + + def default = + // do type test first to ensure we won't select outer on null + if (outerTestNeeded) and(typeTest(testedBinder, expectedTp), outerTest(testedBinder, expectedTp)) + else typeTest(testedBinder, expectedTp) + + // true when called to type-test the argument to an extractor + // don't do any fancy equality checking, just test the type + if (extractorArgTypeTest) default + else expectedTp match { + // TODO: [SPEC] the spec requires `eq` instead of `==` for singleton types + // this implies sym.isStable + case SingleType(_, sym) => and(equalsTest(CODE.REF(sym), testedBinder), typeTest(testedBinder, expectedTp.widen)) + // must use == to support e.g. List() == Nil + case ThisType(sym) if sym.isModule => and(equalsTest(CODE.REF(sym), testedBinder), typeTest(testedBinder, expectedTp.widen)) + case ConstantType(const) => equalsTest(Literal(const), testedBinder) + + case ThisType(sym) => eqTest(This(sym), testedBinder) + case ConstantType(Constant(null)) if testedBinder.info.widen <:< AnyRefClass.tpe + => eqTest(CODE.NULL, testedBinder) + + // TODO: verify that we don't need to special-case Array + // I think it's okay: + // - the isInstanceOf test includes a test for the element type + // - Scala's arrays are invariant (so we don't drop type tests unsoundly) + case _ if (expectedTp <:< AnyRefClass.tpe) && !needsTypeTest(testedBinder.info.widen, expectedTp) => + // do non-null check first to ensure we won't select outer on null + if (outerTestNeeded) and(nonNullTest(testedBinder), outerTest(testedBinder, expectedTp)) + else nonNullTest(testedBinder) + + case _ => default + } + } + + val cond = renderCondition(treeCondStrategy) + val res = codegen._asInstanceOf(testedBinder, nextBinderTp) + + // is this purely a type test, e.g. no outer check, no equality tests (used in switch emission) + def isPureTypeTest = renderCondition(pureTypeTestChecker) + + override def toString = "TT"+(expectedTp, testedBinder.name, nextBinderTp) } // need to substitute to deal with existential types -- TODO: deal with existentials better, don't substitute (see RichClass during quick.comp) @@ -1000,7 +1030,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // equals need not be well-behaved, so don't intersect with pattern's (stabilized) type (unlike MaybeBoundTyped's accumType, where it's required) val cond = codegen._equals(patTree, prevBinder) val res = CODE.REF(prevBinder) - override def toString = "ET"+(prevBinder, patTree) + override def toString = "ET"+(prevBinder.name, patTree) } case class AlternativesTreeMaker(prevBinder: Symbol, var altss: List[List[TreeMaker]], pos: Position) extends TreeMaker with NoNewBinders { @@ -1062,7 +1092,21 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def matchFailGen = (matchFailGenOverride orElse Some(CODE.MATCHERROR(_: Tree))) // println("combining cases: "+ (casesNoSubstOnly.map(_.mkString(" >> ")).mkString("{", "\n", "}"))) + def isSwitchAnnotation(tpe: Type) = tpe hasAnnotation SwitchClass + def isUncheckedAnnotation(tpe: Type) = tpe hasAnnotation UncheckedClass + + val (unchecked, requireSwitch) = scrut match { + case Typed(_, tpt) => + (isUncheckedAnnotation(tpt.tpe), + // matches with two or fewer cases need not apply for switchiness (if-then-else will do) + isSwitchAnnotation(tpt.tpe) && casesNoSubstOnly.lengthCompare(2) > 0) + case _ => + (false, false) + } + emitSwitch(scrut, scrutSym, casesNoSubstOnly, pt, matchFailGenOverride).getOrElse{ + if (requireSwitch) typer.context.unit.warning(scrut.pos, "could not emit switch for @switch annotated match") + if (casesNoSubstOnly nonEmpty) { // before optimizing, check casesNoSubstOnly for presence of a default case, // since DCE will eliminate trivial cases like `case _ =>`, even if they're the last one @@ -1077,7 +1121,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL }) None else matchFailGen - val (cases, toHoist) = optimizeCases(scrutSym, casesNoSubstOnly, pt) + val (cases, toHoist) = optimizeCases(scrutSym, casesNoSubstOnly, pt, unchecked) val matchRes = codegen.matcher(scrut, scrutSym, pt)(cases map combineExtractors, synthCatchAll) @@ -1262,7 +1306,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // decisions, decisions /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - trait TreeMakerApproximation extends TreeMakers { self: CodegenCore => + trait TreeMakerApproximation extends TreeMakers with Prettification{ self: CodegenCore => object Test { var currId = 0 } @@ -1281,9 +1325,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL val id = { Test.currId += 1; Test.currId} override def toString = - if (cond eq Top) "T" - else if(cond eq Havoc) "!?" - else "T"+ id + (if(reusedBy nonEmpty) "!["+ treeMaker +"]" else (if(reuses.isEmpty) "["+ treeMaker +"]" else " cf. T"+reuses.get.id)) + "T"+ id + "C("+ cond +")" //+ (reuses map ("== T"+_.id) getOrElse (if(reusedBy.isEmpty) treeMaker else reusedBy mkString (treeMaker+ " -->(", ", ",")"))) } object Cond { @@ -1304,10 +1346,11 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL } // does not contribute any knowledge - case object Top extends Cond + case object Top extends Cond {override def toString = "T"} + // takes away knowledge. e.g., a user-defined guard - case object Havoc extends Cond + case object Havoc extends Cond {override def toString = "_|_"} // we know everything! everything! // this either means the case is unreachable, @@ -1315,11 +1358,15 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // case object Bottom extends Cond + case class AndCond(a: Cond, b: Cond) extends Cond {override def toString = a +"/\\"+ b} + case class OrCond(a: Cond, b: Cond) extends Cond {override def toString = "("+a+") \\/ ("+ b +")"} + object EqualityCond { private val uniques = new collection.mutable.HashMap[(Tree, Tree), EqualityCond] def apply(testedPath: Tree, rhs: Tree): EqualityCond = uniques getOrElseUpdate((testedPath, rhs), new EqualityCond(testedPath, rhs)) + def unapply(c: EqualityCond) = Some(c.testedPath, c.rhs) } - class EqualityCond(testedPath: Tree, rhs: Tree) extends Cond { + class EqualityCond(val testedPath: Tree, val rhs: Tree) extends Cond { // def negation = TopCond // inequality doesn't teach us anything // do simplification when we know enough about the tree statically: // - collapse equal trees @@ -1329,103 +1376,873 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL override def toString = testedPath +" == "+ rhs +"#"+ id } - object TypeCond { - private val uniques = new collection.mutable.HashMap[(Tree, Type), TypeCond] - def apply(testedPath: Tree, pt: Type): TypeCond = uniques getOrElseUpdate((testedPath, pt), new TypeCond(testedPath, pt)) + object NonNullCond { + private val uniques = new collection.mutable.HashMap[Tree, NonNullCond] + def apply(testedPath: Tree): NonNullCond = uniques getOrElseUpdate(testedPath, new NonNullCond(testedPath)) + def unapply(c: NonNullCond) = Some(c.testedPath) } - class TypeCond(testedPath: Tree, pt: Type) extends Cond { - // def negation = TopCond // inequality doesn't teach us anything - // do simplification when we know enough about the tree statically: - // - collapse equal trees - // - accumulate tests when (in)equality not known statically - // - become bottom when we statically know this can never match - override def toString = testedPath +" <: "+ pt +"#"+ id + class NonNullCond(val testedPath: Tree) extends Cond { + override def toString = testedPath +" ne null " +"#"+ id } - object TypeAndEqualityCond { - private val uniques = new collection.mutable.HashMap[(Tree, Type), TypeAndEqualityCond] - def apply(testedPath: Tree, pt: Type): TypeAndEqualityCond = uniques getOrElseUpdate((testedPath, pt), new TypeAndEqualityCond(testedPath, pt)) + object TypeCond { + private val uniques = new collection.mutable.HashMap[(Tree, Type), TypeCond] + def apply(testedPath: Tree, pt: Type): TypeCond = uniques getOrElseUpdate((testedPath, pt), new TypeCond(testedPath, pt)) + def unapply(c: TypeCond) = Some(c.testedPath, c.pt) } - class TypeAndEqualityCond(testedPath: Tree, pt: Type) extends Cond { + class TypeCond(val testedPath: Tree, val pt: Type) extends Cond { // def negation = TopCond // inequality doesn't teach us anything // do simplification when we know enough about the tree statically: // - collapse equal trees // - accumulate tests when (in)equality not known statically // - become bottom when we statically know this can never match - override def toString = testedPath +" (<: && ==) "+ pt +"#"+ id + override def toString = testedPath +" : "+ pt +"#"+ id } - def approximateMatch(root: Symbol, cases: List[List[TreeMaker]]): List[List[Test]] = { +// class OuterEqCond(val testedPath: Tree, val expectedType: Type) extends Cond { +// val expectedOuter = expectedTp.prefix match { +// case ThisType(clazz) => THIS(clazz) +// case pre => REF(pre.prefix, pre.termSymbol) +// } +// +// // ExplicitOuter replaces `Select(q, outerSym) OBJ_EQ expectedPrefix` by `Select(q, outerAccessor(outerSym.owner)) OBJ_EQ expectedPrefix` +// // if there's an outer accessor, otherwise the condition becomes `true` -- TODO: can we improve needsOuterTest so there's always an outerAccessor? +// val outer = expectedTp.typeSymbol.newMethod(vpmName.outer) setInfo expectedTp.prefix setFlag SYNTHETIC +// +// (Select(codegen._asInstanceOf(testedBinder, expectedTp), outer)) OBJ_EQ expectedOuter +// } + + + // returns (tree, tests), where `tree` will be used to refer to `root` in `tests` + abstract class TreeMakersToConds(val root: Symbol, val cases: List[List[TreeMaker]]) { // a variable in this set should never be replaced by a tree that "does not consist of a selection on a variable in this set" (intuitively) - val pointsToBound = collection.mutable.HashSet(root) + private val pointsToBound = collection.mutable.HashSet(root) // the substitution that renames variables to variables in pointsToBound - var normalize: Substitution = EmptySubstitution + private var normalize: Substitution = EmptySubstitution // replaces a variable (in pointsToBound) by a selection on another variable in pointsToBound // TODO check: // pointsToBound -- accumSubst.from == Set(root) && (accumSubst.from.toSet -- pointsToBound) isEmpty - var accumSubst: Substitution = EmptySubstitution + private var accumSubst: Substitution = EmptySubstitution + + private val trees = new collection.mutable.HashSet[Tree] - val trees = new collection.mutable.HashSet[Tree] + // TODO: improve, e.g., for constants + def sameValue(a: Tree, b: Tree): Boolean = (a eq b) || ((a, b) match { + case (_ : Ident, _ : Ident) => a.symbol eq b.symbol + case _ => false + }) + + // hashconsing trees (modulo value-equality) + def unique(t: Tree, tpOverride: Type = NoType): Tree = + trees find (a => a.equalsStructure0(t)(sameValue)) match { + case Some(orig) => orig // println("unique: "+ (t eq orig, orig)); + case _ => + trees += t + if (tpOverride != NoType) t setType tpOverride + else t + } - def approximateTreeMaker(tm: TreeMaker): Test = { - val subst = tm.substitution + def uniqueTp(tp: Type): Type = tp match { + // typerefs etc are already hashconsed + case _ : UniqueType => tp + case tp@RefinedType(parents, EmptyScope) => tp.memo(tp: Type)(identity) // TODO: does this help? + case _ => tp + } + // produce the unique tree used to refer to this binder + // the type of the binder passed to the first invocation + // determines the type of the tree that'll be returned for that binder as of then + def binderToUniqueTree(b: Symbol) = + unique(accumSubst(normalize(CODE.REF(b))), b.tpe) + + // note that the sequencing of operations is important: must visit in same order as match execution + // binderToUniqueTree uses the type of the first symbol that was encountered as the type for all future binders + def treeMakerToCond(tm: TreeMaker): Cond = { + updateSubstitution(tm.substitution) + + tm match { + case ttm@TypeTestTreeMaker(prevBinder, testedBinder, pt, _) => + object condStrategy extends TypeTestTreeMaker.TypeTestCondStrategy { + type Result = Cond + def and(a: Result, b: Result) = AndCond(a, b) + def outerTest(testedBinder: Symbol, expectedTp: Type) = Top // TODO OuterEqCond(testedBinder, expectedType) + def typeTest(b: Symbol, pt: Type) = TypeCond(binderToUniqueTree(b), uniqueTp(pt)) + def nonNullTest(testedBinder: Symbol) = NonNullCond(binderToUniqueTree(testedBinder)) + def equalsTest(pat: Tree, testedBinder: Symbol) = EqualityCond(binderToUniqueTree(testedBinder), unique(pat)) + def eqTest(pat: Tree, testedBinder: Symbol) = EqualityCond(binderToUniqueTree(testedBinder), unique(pat)) // TODO: eq, not == + } + ttm.renderCondition(condStrategy) + case EqualityTestTreeMaker(prevBinder, patTree, _) => EqualityCond(binderToUniqueTree(prevBinder), unique(patTree)) + case AlternativesTreeMaker(_, altss, _) => altss map (_ map treeMakerToCond reduceLeft AndCond) reduceLeft OrCond + case ProductExtractorTreeMaker(testedBinder, None, subst) => NonNullCond(binderToUniqueTree(testedBinder)) + case ExtractorTreeMaker(_, _, _, _) + | GuardTreeMaker(_) + | ProductExtractorTreeMaker(_, Some(_), _) + | BodyTreeMaker(_, _) => Havoc + case SubstOnlyTreeMaker(_, _) => Top + } + } + + private def updateSubstitution(subst: Substitution) = { // find part of substitution that replaces bound symbols by new symbols, and reverse that part // so that we don't introduce new aliases for existing symbols, thus keeping the set of bound symbols minimal val (boundSubst, unboundSubst) = (subst.from zip subst.to) partition {case (f, t) => t.isInstanceOf[Ident] && (t.symbol ne NoSymbol) && pointsToBound(f) } val (boundFrom, boundTo) = boundSubst.unzip + val (unboundFrom, unboundTo) = unboundSubst.unzip + + // reverse substitution that would otherwise replace a variable we already encountered by a new variable + // NOTE: this forgets the more precise we have for these later variables, but that's probably okay normalize >>= Substitution(boundTo map (_.symbol), boundFrom map (CODE.REF(_))) // println("normalize: "+ normalize) - val (unboundFrom, unboundTo) = unboundSubst unzip val okSubst = Substitution(unboundFrom, unboundTo map (normalize(_))) // it's important substitution does not duplicate trees here -- it helps to keep hash consing simple, anyway pointsToBound ++= ((okSubst.from, okSubst.to).zipped filter { (f, t) => pointsToBound exists (sym => t.exists(_.symbol == sym)) })._1 // println("pointsToBound: "+ pointsToBound) accumSubst >>= okSubst // println("accumSubst: "+ accumSubst) + } + + def approximateTreeMaker(tm: TreeMaker): Test = + Test(treeMakerToCond(tm), tm) + + def approximateMatch: List[List[Test]] = cases.map { _ map approximateTreeMaker } + } + + def approximateMatch(root: Symbol, cases: List[List[TreeMaker]]): List[List[Test]] = { + object approximator extends TreeMakersToConds(root, cases) + approximator.approximateMatch + } + + def showTreeMakers(cases: List[List[TreeMaker]]) = { + println("treeMakers:") + println(alignAcrossRows(cases, ">>")) + } - // TODO: improve, e.g., for constants - def sameValue(a: Tree, b: Tree): Boolean = (a eq b) || ((a, b) match { - case (_ : Ident, _ : Ident) => a.symbol eq b.symbol - case _ => false - }) - - // hashconsing trees (modulo value-equality) - def unique(t: Tree): Tree = - trees find (a => a.equalsStructure0(t)(sameValue)) match { - case Some(orig) => orig // println("unique: "+ (t eq orig, orig)); - case _ => trees += t; t + def showTests(testss: List[List[Test]]) = { + println("tests: ") + println(alignAcrossRows(testss, "&")) + } + } + + trait Prettification { + private def max(xs: Seq[Int]) = if (xs isEmpty) 0 else xs max + + def alignedColumns(cols: Seq[AnyRef]): Seq[String] = { + def toString(x: AnyRef) = if (x eq null) "" else x.toString + if (cols.isEmpty || cols.tails.isEmpty) cols map toString + else { + val (colStrs, colLens) = cols map {c => val s = toString(c); (s, s.length)} unzip + val maxLen = max(colLens) + val avgLen = colLens.sum/colLens.length + val goalLen = maxLen min avgLen*2 + def pad(s: String) = { + val toAdd = ((goalLen - s.length) max 0) + 2 + (" " * (toAdd/2)) + s + (" " * (toAdd/2 + (toAdd%2))) + } + cols map (x => pad(toString(x))) + } + } + def alignAcrossRows(xss: List[List[AnyRef]], sep: String, lineSep: String = "\n"): String = { + val maxLen = max(xss map (_.length)) + val padded = xss map (xs => xs ++ List.fill(maxLen - xs.length)(null)) + padded.transpose.map(alignedColumns).transpose map (_.mkString(sep)) mkString(lineSep) + } + } + + // http://www.cis.upenn.edu/~cis510/tcl/chap3.pdf + // http://users.encs.concordia.ca/~ta_ahmed/ms_thesis.pdf + trait Logic extends Prettification { + class Prop + case class Eq(p: Var, q: Const) extends Prop + + type Const + type Var <: AbsVar + + trait AbsVar { + // if the domain is enumerable, at least one assignment must be true + def domainEnumerable: Boolean + def domain: Option[Set[Const]] + + // for this var, call it V, turn V = C into the equivalent proposition in boolean logic + def propForEqualsTo(c: Const): Prop + + def equalitySyms: Set[Sym] + } + + // would be nice to statically check whether a prop is equational or pure, + // but that requires typing relations like And(x: Tx, y: Ty) : (if(Tx == PureProp && Ty == PureProp) PureProp else Prop) + case class And(a: Prop, b: Prop) extends Prop + case class Or(a: Prop, b: Prop) extends Prop + case class Not(a: Prop) extends Prop + + case object True extends Prop + case object False extends Prop + + private def nextSymId = {_symId += 1; _symId}; private var _symId = 0 + + // symbols are propositions + case class Sym(val variable: Var, val const: Const) extends Prop { + override val toString = variable +"="+ const +"#"+ nextSymId + } + + trait PropTraverser { + def apply(x: Prop): Unit = x match { + case And(a, b) => apply(a); apply(b) + case Or(a, b) => apply(a); apply(b) + case Not(a) => apply(a) + case Eq(a, b) => applyVar(a); applyConst(b) + case _ => + } + def applyVar(x: Var): Unit = {} + def applyConst(x: Const): Unit = {} + } + + trait PropMap { + def apply(x: Prop): Prop = x match { // TODO: mapConserve + case And(a, b) => And(apply(a), apply(b)) + case Or(a, b) => Or(apply(a), apply(b)) + case Not(a) => Not(apply(a)) + case p => p + } + } + + // plan: (aka TODO) + + // convert finite domain propositional logic to boolean propositional logic + // for all c in C, there is a corresponding (meta-indexed) proposition Qv(c) that represents V = c, + // the only property of equality that is encoded is that a variable can at most be equal to one of the c in C: + // thus, for each distinct c, c', c'',... in C, a clause `not (Qv(c) /\ (Qv(c') \/ ... \/ Qv(c'')))` is added + def removeVarEq(prop: Prop): Prop = { + val vars = new collection.mutable.HashSet[Var] + + object dropEquational extends PropMap { + override def apply(p: Prop) = p match { + case Eq(v, c) => vars += v; v.propForEqualsTo(c) + case _ => super.apply(p) + } + } + + // dropEquational populates vars, and for each var in vars. var.equalitySyms + val pure = dropEquational(prop) + + // X = C is translated to P_X=C + // X = C' is translated to P_X=C' + // need to enforce X cannot simultaneously equal C and C' + // thus, all equality syms are mutually exclusive + // X = A, B, C, D --> Not(And(A, B)) /\ Not(And(A, C)) /\ Not(And(A, D)) + // /\ Not(And(B, C)) /\ Not(And(B, D)) + // /\ Not(And(C, D)) + // equivalently Or(Not(A), Not(B)) /\ Or(...) + + var eqAxioms: Prop = True + def mutex(a: Sym)(b: Sym) = + eqAxioms = And(eqAxioms, Or(Not(a), Not(b))) + + // at least one assignment from the domain must be true + def assigned(dom: Set[Sym]) = + eqAxioms = And(eqAxioms, dom.reduceLeft(Or)) + + // println("vars: "+ vars) + vars.foreach { v => + // is the domain enumerable? then create equality syms for all elements in the domain and + // assert at least one of them must be selected + // if v.domain.isEmpty, we must consider the domain to be infinite + v.domain foreach { dom => + // get the Syms for the constants in the domain (making fresh ones for those not encountered in the formula) + val domProps = dom map {c => v.propForEqualsTo(c)} + val domSyms = new collection.mutable.HashSet[Sym]() + object collectSyms extends PropTraverser { + override def apply(p: Prop) = p match { + case domSym: Sym => domSyms += domSym + case _ => super.apply(p) + } } + domProps foreach collectSyms.apply - def uniqueTp(tp: Type): Type = tp match { - // typerefs etc are already hashconsed - case _ : UniqueType => tp - case tp@RefinedType(parents, EmptyScope) => tp.memo(tp: Type)(identity) // TODO: does this help? - case _ => tp + // TODO: an empty domain means involved type tests can never be true --> always non-exhaustive? + if (domSyms.nonEmpty) assigned(domSyms.toSet) } - def binderToUniqueTree(b: Symbol) = unique(accumSubst(normalize(CODE.REF(b)))) + // recover mutual-exclusivity (a variable can never be assigned two different constants) + var syms = v.equalitySyms.toList + while (syms.nonEmpty) { + syms.tail.foreach(mutex(syms.head)) + syms = syms.tail + } + } - Test(tm match { - case ProductExtractorTreeMaker(pb, None, subst) => Top // TODO: NotNullTest(prevBinder) - case tm@TypeTestTreeMaker(prevBinder, nextBinderTp, _) => TypeCond(binderToUniqueTree(prevBinder), uniqueTp(nextBinderTp)) - case tm@TypeAndEqualityTestTreeMaker(_, patBinder, pt, _) => TypeAndEqualityCond(binderToUniqueTree(patBinder), uniqueTp(pt)) - case tm@EqualityTestTreeMaker(prevBinder, patTree, _) => EqualityCond(binderToUniqueTree(prevBinder), unique(patTree)) - case ExtractorTreeMaker(_, _, _, _) - | GuardTreeMaker(_) - | ProductExtractorTreeMaker(_, Some(_), _) => Havoc - case AlternativesTreeMaker(_, _, _) => Havoc // TODO: can do better here - case SubstOnlyTreeMaker(_, _) => Top - case BodyTreeMaker(_, _) => Havoc - }, tm) + // println("eqAxioms:\n"+ cnfString(conjunctiveNormalForm(negationNormalForm(eqAxioms)))) + // println("pure:\n"+ cnfString(conjunctiveNormalForm(negationNormalForm(pure)))) + + And(eqAxioms, pure) + } + + // convert propositional logic formula to CNF + // http://www.dcs.warwick.ac.uk/people/academic/Ranko.Lazic/fsv/fsv6.pdf + def negationNormalForm(p: Prop): Prop = p match { + case And(a, b) => And(negationNormalForm(a), negationNormalForm(b)) + case Or(a, b) => Or(negationNormalForm(a), negationNormalForm(b)) + case Not(And(a, b)) => negationNormalForm(Or(Not(a), Not(b))) + case Not(Or(a, b)) => negationNormalForm(And(Not(a), Not(b))) + case Not(Not(p)) => negationNormalForm(p) + case Not(True) => False + case Not(False) => True + case True + | False + | (_ : Sym) + | Not(_ : Sym) => p + } + + // CNF: a formula is a conjunction of clauses + type Formula = List[Clause] ; def formula(c: Clause*): Formula = c.toList + + // a clause is a disjunction of distinct literals + type Clause = Set[Lit] ; def clause(l: Lit*): Clause = l.toSet + + // a literal is a (possibly negated) variable + case class Lit(sym: Sym, pos: Boolean = true) { + override def toString = if (!pos) "-"+ sym.toString else sym.toString + def unary_- = Lit(sym, !pos) + } + + val TrueF = formula() + val FalseF = formula(clause()) + def lit(s: Sym) = formula(clause(Lit(s))) + def negLit(s: Sym) = formula(clause(Lit(s, false))) + + def conjunctiveNormalForm(p: Prop): Formula = { + def distribute(a: Formula, b: Formula): Formula = (a, b) match { + // true \/ _ = true + case (TrueF, _) => TrueF + // _ \/ true = true + case (_, TrueF) => TrueF + // lit \/ lit + case (List(a), List(b)) => formula(a ++ b) + // (c1 /\ ... /\ cn) \/ d = ((c1 \/ d) /\ ... /\ (cn \/ d)) + case (cs, d) if cs.tail nonEmpty => cs flatMap (c => distribute(formula(c), d)) + // d \/ (c1 /\ ... /\ cn) = ((d \/ c1) /\ ... /\ (d \/ cn)) + case (d, cs) if cs.tail nonEmpty => cs flatMap (c => distribute(d, formula(c))) } - cases.map { _ map approximateTreeMaker } + p match { + case True => TrueF + case False => FalseF + case s: Sym => lit(s) + case Not(s: Sym) => negLit(s) + case And(a, b) => conjunctiveNormalForm(a) ++ conjunctiveNormalForm(b) + case Or(a, b) => distribute(conjunctiveNormalForm(a), conjunctiveNormalForm(b)) + } + } + + def normalize(p: Prop) = conjunctiveNormalForm(negationNormalForm(removeVarEq(p))) + def cnfString(f: Formula) = alignAcrossRows(f map (_.toList) toList, "\\/", " /\\\n") + + // adapted from http://lara.epfl.ch/w/sav10:simple_sat_solver (original by Hossein Hojjat) + type Model = Map[Sym, Boolean] + val EmptyModel = Map.empty[Sym, Boolean] + + // returns all solutions, if any (TODO: better infinite recursion backstop -- detect fixpoint??) + def fullDPLL(f: Formula): List[Model] = { + // the negation of a model -(S1=True/False /\ ... /\ SN=True/False) = clause(S1=False/True, ...., SN=False/True) + def negateModel(m: Model) = clause(m.toSeq.map{ case (sym, pos) => Lit(sym, !pos) } : _*) + + def findAllModels(f: Formula, models: List[Model], recursionDepthAllowed: Int = 20): List[Model]= + if (recursionDepthAllowed == 0) models + else { + val (ok, model) = DPLL(f) + // if we found a solution, conjunct the formula with the model's negation and recurse + if (ok) findAllModels(f :+ negateModel(model), model :: models, recursionDepthAllowed - 1) + else models + } + + findAllModels(f, Nil) + } + + def DPLL(f: Formula): (Boolean, Model) = { + @inline def withLit(res: (Boolean, Model), l: Lit) = (res._1, res._2 + (l.sym -> l.pos)) + @inline def orElse(a: (Boolean, Model), b: => (Boolean, Model)) = if (a._1) a else b + +// println("dpll\n"+ cnfString(f)) + + if (f isEmpty) (true, EmptyModel) + else if(f exists (_.isEmpty)) (false, EmptyModel) + else f.find(_.size == 1) map { unitClause => + val unitLit = unitClause.head +// println("unit: "+ unitLit) + val negated = -unitLit + // drop entire clauses that are trivially true + // (i.e., disjunctions that contain the literal we're making true in the returned model), + // and simplify clauses by dropping the negation of the literal we're making true + // (since False \/ X == X) + val simplified = f.filterNot(_.contains(unitLit)).map(_ - negated) + withLit(DPLL(simplified), unitLit) + } getOrElse { + // partition symbols according to whether they appear in positive and/or negative literals + val pos = new HashSet[Sym]() + val neg = new HashSet[Sym]() + f.foreach{_.foreach{ lit => + if (lit.pos) pos += lit.sym else neg += lit.sym + }} + // appearing in both positive and negative + val impures = pos intersect neg + // appearing only in either positive/negative positions + val pures = (pos ++ neg) -- impures + + if (pures nonEmpty) { + val pureSym = pures.head + // turn it back into a literal + // (since equality on literals is in terms of equality + // of the underlying symbol and its positivity, simply construct a new Lit) + val pureLit = Lit(pureSym, pos(pureSym)) +// println("pure: "+ pureLit +" pures: "+ pures +" impures: "+ impures) + val simplified = f.filterNot(_.contains(pureLit)) + withLit(DPLL(simplified), pureLit) + } else { + val split = f.head.head +// println("split: "+ split) + orElse(DPLL(f :+ clause(split)), DPLL(f :+ clause(-split))) + } + } + } + } + + /** + * Represent a match as a formula in propositional logic that encodes whether the match matches (abstractly: we only consider types) + * + */ + trait SymbolicMatchAnalysis extends TreeMakerApproximation with Logic { self: CodegenCore => + object Var { + private var _nextId = 0 + def nextId = {_nextId += 1; _nextId} + + private val uniques = new collection.mutable.HashMap[Tree, Var] + def apply(x: Tree): Var = uniques getOrElseUpdate(x, new Var(x, x.tpe)) + } + class Var(val path: Tree, fullTp: Type, checked: Boolean = true) extends AbsVar { + // when looking at the domain, we only care about types we can check at run time + val domainTp: Type = checkableType(fullTp) + + // case None => domain is unknown, + // case Some(List(tps: _*)) => domain is exactly tps + // we enumerate the subtypes of the full type, as that allows us to filter out more types statically, + // once we go to run-time checks (on Const's), convert them to checkable types + // TODO: there seems to be bug for singleton domains (variable does not show up in model) + val domain = if (checked) enumerateSubtypes(fullTp).map(_.map(Const).toSet) else None + + def describe = toString + ": "+ fullTp + domain.map(_.map(_.tp).mkString(" ::= ", " | ", "")).getOrElse(" ::= ??") +" // = "+ path + def domainEnumerable = domain.nonEmpty + + private val domMap = new collection.mutable.HashMap[Const, Sym] + private def symForEqualsTo(c: Const) = { + domMap getOrElseUpdate(c, { + // println("creating symbol for equality "+ this +" = "+ c) + Sym(this, c) + }) + } + + // for this var, call it V, turn V = C into the equivalent proposition in boolean logic + // over all executions of this method on the same Var object, + def propForEqualsTo(c: Const): Prop = { + domain match { + case None => symForEqualsTo(c) + case Some(domainConsts) => + val domainTps = domainConsts map (_.tp) + val checkedTp = c.tp + // find all the domain types that could make the type test true + // if the checked type is a supertype of the lub of the domain, + // we'll end up \/'ing the whole domain together, + // but must not simplify to True, as the type test may also fail... + val matches = domainTps.filter(_ <:< checkedTp).map{ tp => symForEqualsTo(Const(tp)) } + // println("type-equals-prop for "+ this +" = "+ c +": "+ (checkedTp, domainTp, domainTps) +" matches: "+ matches) + + if (matches isEmpty) False else matches.reduceLeft(Or) + } + } + + def equalitySyms: Set[Sym] = domMap.values.toSet + + private[this] val id: Int = Var.nextId + override def toString = "V"+ id + } + + // all our variables range over types + // a literal constant becomes ConstantType(Constant(v)) when the type allows it (roughly, anyval + string + null) + // equality between variables: SingleType(x) (note that pattern variables cannot relate to each other -- it's always patternVar == nonPatternVar) + case class Const(tp: Type) { + override def toString = tp.toString + + def toValueString = tp match { + case ConstantType(c) => c.escapedStringValue + case _ => tp.toString + } + } + + // make sure it's not a primitive, else (5: Byte) match { case 5 => ... } sees no Byte + // TODO: domain of feasibly enumerable built-in types (enums, char?) + def enumerateSubtypes(tp: Type): Option[List[Type]] = + tp.typeSymbol match { + case BooleanClass => + // println("enum bool "+ tp) + Some(List(ConstantType(Constant(true)), ConstantType(Constant(false)))) + // TODO case _ if tp.isTupleType => // recurse into component types + case sym if !sym.isSealed || isPrimitiveValueClass(sym) => + // println("enum unsealed "+ (tp, sym, sym.isSealed, isPrimitiveValueClass(sym))) + None + case sym => + val subclasses = ( + sym.sealedDescendants.toList sortBy (_.sealedSortName) + // symbols which are both sealed and abstract need not be covered themselves, because + // all of their children must be and they cannot otherwise be created. + filterNot (x => x.isSealed && x.isAbstractClass && !isPrimitiveValueClass(x))) + // println("subclasses "+ (sym, subclasses)) + + val tpApprox = typer.infer.approximateAbstracts(tp) + val pre = tpApprox.prefix + // valid subtypes are turned into checkable types, as we are entering the realm of the dynamic + val validSubTypes = (subclasses flatMap {sym => + // have to filter out children which cannot match: see ticket #3683 for an example + // compare to the fully known type `tp` (modulo abstract types), + // so that we can rule out stuff like: sealed trait X[T]; class XInt extends X[Int] --> XInt not valid when enumerating X[String] + // however, must approximate abstract types in + val subTp = appliedType(pre.memberType(sym), sym.typeParams.map(_ => WildcardType)) + val subTpApprox = typer.infer.approximateAbstracts(subTp) // TODO: needed? + // println("subtp"+(subTpApprox <:< tpApprox, subTpApprox, tpApprox)) + if (subTpApprox <:< tpApprox) Some(checkableType(subTp)) + else None + }) + // println("enum sealed "+ (tp, tpApprox) + " as "+ validSubTypes) + Some(validSubTypes) + } + + def narrowTypeOf(p: Tree) = p match { + case Literal(c) => ConstantType(c) + case Ident(_) if p.symbol.isStable => singleType(p.tpe.prefix, p.symbol) + case x => x.tpe.narrow + } + + // approximate a type to the static type that is fully checkable at run time, + // hiding statically known but dynamically uncheckable information using existential quantification + // TODO: this is subject to the availability of TypeTags (since an abstract type with a type tag is checkable at run time) + def checkableType(tp: Type): Type = { + // TODO: this is extremely rough... + object toCheckable extends TypeMap { + def apply(tp: Type) = tp match { + case TypeRef(pre, sym, a :: as) if sym ne ArrayClass => + // replace type args by existentials, since they can't be checked + // TODO: when type tags are available, we will check -- when this is implemented, can we take that into account here? + // TODO: don't reuse sym.typeParams, they have bounds (and those must not be considered) + newExistentialType(sym.typeParams, sym.tpe).asSeenFrom(pre, sym.owner) + case _ => mapOver(tp) + } + } + val res = toCheckable(tp) + // println("checkable "+(tp, res)) + res + } + + // a type is "uncheckable" (for exhaustivity) if we don't statically know its subtypes (i.e., it's unsealed) + // we consider tuple types with at least one component of a checkable type as a checkable type + def uncheckableType(tp: Type): Boolean = { + @inline def tupleComponents(tp: Type) = tp.normalize.typeArgs + val checkable = ( + (isTupleType(tp) && tupleComponents(tp).exists(tp => !uncheckableType(tp))) + || enumerateSubtypes(tp).nonEmpty) + // if (!checkable) println("deemed uncheckable: "+ tp) + !checkable + } + + def exhaustive(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): List[String] = if (uncheckableType(prevBinder.info)) Nil else { + // customize TreeMakersToConds (which turns a tree of tree makers into a more abstract DAG of tests) + // - approximate the pattern `List()` (unapplySeq on List with empty length) as `Nil`, + // otherwise the common (xs: List[Any]) match { case List() => case x :: xs => } is deemed unexhaustive + // - back off (to avoid crying exhaustive too often) when: + // - there are guards --> + // - there are extractor calls (that we can't secretly/soundly) rewrite + var backoff = false + object exhaustivityApproximation extends TreeMakersToConds(prevBinder, cases) { + override def treeMakerToCond(tm: TreeMaker): Cond = tm match { + case p@ExtractorTreeMaker(extractor, Some(lenCheck), testedBinder, _) => + p.checkedLength match { + // pattern: `List()` (interpret as `Nil`) + // TODO: make it more general List(1, 2) => 1 :: 2 :: Nil + case Some(0) if testedBinder.tpe.typeSymbol == ListClass => // extractor.symbol.owner == SeqFactory + EqualityCond(binderToUniqueTree(p.prevBinder), unique(Ident(NilModule) setType NilModule.tpe)) + case _ => + super.treeMakerToCond(tm) + } + case ExtractorTreeMaker(_, _, _, _) => +// println("backing off due to "+ tm) + backoff = true + super.treeMakerToCond(tm) + case GuardTreeMaker(guard) => + guard.tpe match { + case ConstantType(Constant(true)) => Top + case ConstantType(Constant(false)) => Havoc + case _ => +// println("can't statically interpret guard: "+(guard, guard.tpe)) + backoff = true + Havoc + } + case _ => + super.treeMakerToCond(tm) + } + } + + def symbolic(t: Cond): Prop = t match { + case AndCond(a, b) => And(symbolic(a), symbolic(b)) + case OrCond(a, b) => Or(symbolic(a), symbolic(b)) + case Top => True + case Havoc => False + case TypeCond(p, pt) => Eq(Var(p), Const(checkableType(pt))) + case EqualityCond(p, q) => Eq(Var(p), Const(narrowTypeOf(q))) + case NonNullCond(p) => True // ignoring nullness because it generates too much noise Not(Eq(Var(p), Const(NullClass.tpe))) + } + + def symbolicCase(tests: List[Test]) = { + val testsBeforeBody = tests.takeWhile(t => !t.treeMaker.isInstanceOf[BodyTreeMaker]) + testsBeforeBody.map(t => symbolic(t.cond)).foldLeft(True: Prop)(And) + } + + val tests = exhaustivityApproximation.approximateMatch + + if (backoff) Nil else { + val symbolicCases = tests map symbolicCase + + val prevBinderTree = exhaustivityApproximation.binderToUniqueTree(prevBinder) + + // TODO: null tests generate too much noise, so disabled them -- is there any way to bring them back? + // assuming we're matching on a non-null scrutinee (prevBinder), when does the match fail? + // val nonNullScrutineeCond = + // assume non-null for all the components of the tuple we're matching on (if we're matching on a tuple) + // if (isTupleType(prevBinder.tpe)) + // prevBinder.tpe.typeArgs.mapWithIndex{case (_, i) => NonNullCond(codegen.tupleSel(prevBinderTree)(i))}.reduceLeft(AndCond) + // else + // NonNullCond(prevBinderTree) + // val matchFails = And(symbolic(nonNullScrutineeCond), Not(symbolicCases reduceLeft (Or(_, _)))) + + // when does the match fail? + val matchFails = Not(symbolicCases reduceLeft (Or(_, _))) + + + // debug output: + // println("analysing:") + // showTreeMakers(cases) + // showTests(tests) + // + // def gatherVariables(p: Prop): Set[Var] = { + // val vars = new HashSet[Var]() + // (new PropTraverser { + // override def applyVar(v: Var) = vars += v + // })(p) + // vars.toSet + // } + // val vars = gatherVariables(matchFails) + // println("\nvars:\n"+ (vars map (_.describe) mkString ("\n"))) + // + // println("\nmatchFails as CNF:\n"+ cnfString(normalize(matchFails))) + + // find the models (under which the match fails) + val matchFailModels = fullDPLL(normalize(matchFails)) + + val scrutVar = Var(prevBinderTree) + val counterExamples = matchFailModels.map(modelToCounterExample(scrutVar)) + + CounterExample.prune(counterExamples).map(_.toString).sorted + } + } + + object CounterExample { + def prune(examples: List[CounterExample]): List[CounterExample] = { + val distinct = examples.filterNot(_ == NoExample).toSet + distinct.filterNot(ce => distinct.exists(other => (ce ne other) && ce.coveredBy(other))).toList + } + } + + // a way to construct a value that will make the match fail: a constructor invocation, a constant, an object of some type) + class CounterExample { + protected[SymbolicMatchAnalysis] def flattenConsArgs: List[CounterExample] = Nil + def coveredBy(other: CounterExample): Boolean = this == other || other == WildcardExample + } + case class ValueExample(c: Const) extends CounterExample { override def toString = c.toValueString } + case class TypeExample(c: Const) extends CounterExample { override def toString = "(_ : "+ c +")" } + case class NegativeExample(nonTrivialNonEqualTo: List[Const]) extends CounterExample { + override def toString = { + val negation = + if (nonTrivialNonEqualTo.tail.isEmpty) nonTrivialNonEqualTo.head.toValueString + else nonTrivialNonEqualTo.map(_.toValueString).sorted.mkString("in (", ", ", ")") + "<not "+ negation +">" + } + } + case class ListExample(ctorArgs: List[CounterExample]) extends CounterExample { + protected[SymbolicMatchAnalysis] override def flattenConsArgs: List[CounterExample] = ctorArgs match { + case hd :: tl :: Nil => hd :: tl.flattenConsArgs + case _ => Nil + } + protected[SymbolicMatchAnalysis] lazy val elems = flattenConsArgs + + override def coveredBy(other: CounterExample): Boolean = + other match { + case other@ListExample(_) => + this == other || ((elems.length == other.elems.length) && (elems zip other.elems).forall{case (a, b) => a coveredBy b}) + case _ => super.coveredBy(other) + } + + override def toString = elems.mkString("List(", ", ", ")") + } + case class TupleExample(ctorArgs: List[CounterExample]) extends CounterExample { + override def toString = ctorArgs.mkString("(", ", ", ")") + + override def coveredBy(other: CounterExample): Boolean = + other match { + case TupleExample(otherArgs) => + this == other || ((ctorArgs.length == otherArgs.length) && (ctorArgs zip otherArgs).forall{case (a, b) => a coveredBy b}) + case _ => super.coveredBy(other) + } + } + case class ConstructorExample(cls: Symbol, ctorArgs: List[CounterExample]) extends CounterExample { + override def toString = cls.decodedName + (if (cls.isModuleClass) "" else ctorArgs.mkString("(", ", ", ")")) + } + + case object WildcardExample extends CounterExample { override def toString = "_" } + case object NoExample extends CounterExample { override def toString = "??" } + + // return constructor call when the model is a true counter example + // (the variables don't take into account type information derived from other variables, + // so, naively, you might try to construct a counter example like _ :: Nil(_ :: _, _ :: _), + // since we didn't realize the tail of the outer cons was a Nil) + def modelToCounterExample(scrutVar: Var)(model: Model): CounterExample = { + // x1 = ... + // x1.hd = ... + // x1.tl = ... + // x1.hd.hd = ... + // ... + val varAssignment = model.toSeq.groupBy{f => f match {case (sym, value) => sym.variable} }.mapValues{ xs => + val (trues, falses) = xs.partition(_._2) + (trues map (_._1.const), falses map (_._1.const)) + // should never be more than one value in trues... + } + + // println("var assignment:\n"+ + // varAssignment.toSeq.sortBy(_._1.toString).map { case (v, (trues, falses)) => + // val assignment = "== "+ (trues mkString("(", ", ", ")")) +" != ("+ (falses mkString(", ")) +")" + // v +"(="+ v.path +": "+ v.domainTp +") "+ assignment + // }.mkString("\n")) + + + // chop a path into a list of symbols + def chop(path: Tree): List[Symbol] = path match { + case Ident(_) => List(path.symbol) + case Select(pre, name) => chop(pre) :+ path.symbol + case _ => println("don't know how to chop "+ path); Nil + } + + // turn the variable assignments into a tree + // the root is the scrutinee (x1), edges are labelled by the fields that are assigned + // a node is a variable example (which is later turned into a counter example) + object VariableAssignment { + private def findVar(path: List[Symbol]) = path match { + case List(root) if root == scrutVar.path.symbol => Some(scrutVar) + case _ => varAssignment.find{case (v, a) => chop(v.path) == path}.map(_._1) + } + + private val uniques = new collection.mutable.HashMap[Var, VariableAssignment] + private def unique(variable: Var): VariableAssignment = + uniques.getOrElseUpdate(variable, { + val (eqTo, neqTo) = varAssignment.getOrElse(variable, (Nil, Nil)) // TODO + VariableAssignment(variable, eqTo.toList, neqTo.toList, HashMap.empty) + }) + + def apply(variable: Var): VariableAssignment = { + val path = chop(variable.path) + val pre = path.init + val field = path.last + + val newCtor = unique(variable) + + if (pre.isEmpty) newCtor + else { + findVar(pre) foreach { preVar => + val outerCtor = this(preVar) + outerCtor.fields(field) = newCtor + } + newCtor + } + } + } + + // node in the tree that describes how to construct a counter-example + case class VariableAssignment(variable: Var, equalTo: List[Const], notEqualTo: List[Const], fields: collection.mutable.Map[Symbol, VariableAssignment]) { + private lazy val ctor = (equalTo match { case List(Const(tp)) => tp case _ => variable.domainTp }).typeSymbol.primaryConstructor + private lazy val ctorParams = if (ctor == NoSymbol || ctor.paramss.isEmpty) Nil else ctor.paramss.head + private lazy val cls = if (ctor == NoSymbol) NoSymbol else ctor.owner + private lazy val caseFieldAccs = if (cls == NoSymbol) Nil else cls.caseFieldAccessors + + + def allFieldAssignmentsLegal: Boolean = + (fields.keySet subsetOf caseFieldAccs.toSet) && fields.values.forall(_.allFieldAssignmentsLegal) + + private lazy val nonTrivialNonEqualTo = notEqualTo.filterNot{c => val sym = c.tp.typeSymbol; sym == AnyClass } // sym == NullClass || + + // NoExample if the constructor call is ill-typed + // (thus statically impossible -- can we incorporate this into the formula?) + // beBrief is used to suppress negative information nested in tuples -- it tends to get too noisy + def toCounterExample(beBrief: Boolean = false): CounterExample = + if (!allFieldAssignmentsLegal) NoExample + else { +// println("describing "+ (variable, equalTo, notEqualTo, fields, cls, allFieldAssignmentsLegal)) + val res = equalTo match { + // a definite assignment to a value + case List(eq@Const(_: ConstantType)) if fields.isEmpty => ValueExample(eq) + + // constructor call + // or we did not gather any information about equality but we have information about the fields + // --> typical example is when the scrutinee is a tuple and all the cases first unwrap that tuple and only then test something interesting + case _ if cls != NoSymbol && + ( equalTo.nonEmpty + || (fields.nonEmpty && !isPrimitiveValueClass(cls) && equalTo.isEmpty && notEqualTo.isEmpty)) => + + @inline def args(brevity: Boolean = beBrief) = { + // figure out the constructor arguments from the field assignment + val argLen = (caseFieldAccs.length min ctorParams.length) + + (0 until argLen).map(i => fields.get(caseFieldAccs(i)).map(_.toCounterExample(brevity)) getOrElse WildcardExample).toList + } + + cls match { + case ConsClass => ListExample(args()) + case _ if isTupleSymbol(cls) => TupleExample(args(true)) + case _ => ConstructorExample(cls, args()) + } + + // a definite assignment to a type + case List(eq) if fields.isEmpty => TypeExample(eq) + + // negative information + case Nil if nonTrivialNonEqualTo.nonEmpty => + // negation tends to get pretty verbose + if (beBrief) WildcardExample else NegativeExample(nonTrivialNonEqualTo) + + // not a valid counter-example, possibly since we have a definite type but there was a field mismatch + // TODO: improve reasoning -- in the mean time, a false negative is better than an annoying false positive + case _ => NoExample + } +// println("described as: "+ res) + res + } + + override def toString = toCounterExample().toString + } + + // slurp in information from other variables + varAssignment.keys.foreach{ v => if (v != scrutVar) VariableAssignment(v) } + + // this is the variable we want a counter example for + VariableAssignment(scrutVar).toCounterExample() } } @@ -1446,28 +2263,49 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // interpret: val dependencies = new collection.mutable.LinkedHashMap[Test, Set[Cond]] val tested = new collection.mutable.HashSet[Cond] - testss foreach { tests => - tested.clear() - tests dropWhile { test => - val cond = test.cond - if ((cond eq Havoc) || (cond eq Top)) (cond eq Top) // stop when we encounter a havoc, skip top - else { - tested += cond + + def storeDependencies(test: Test) = { + val cond = test.cond + + def simplify(c: Cond): Set[Cond] = c match { + case AndCond(a, b) => simplify(a) ++ simplify(b) + case OrCond(_, _) => Set(Havoc) // TODO: supremum? + case NonNullCond(_) => Set(Top) // not worth remembering + case _ => Set(c) + } + val conds = simplify(cond) + + if (conds(Havoc)) false // stop when we encounter a havoc + else { + val nonTrivial = conds filterNot (_ == Top) + if (nonTrivial nonEmpty) { + tested ++= nonTrivial // is there an earlier test that checks our condition and whose dependencies are implied by ours? - dependencies find { case (priorTest, deps) => - ((priorTest.cond eq cond) || (deps contains cond)) && (deps subsetOf tested) - } foreach { case (priorTest, deps) => - // if so, note the dependency in both tests - priorTest registerReuseBy test + dependencies find { + case (priorTest, deps) => + ((simplify(priorTest.cond) == nonTrivial) || // our conditions are implied by priorTest if it checks the same thing directly + (nonTrivial subsetOf deps) // or if it depends on a superset of our conditions + ) && (deps subsetOf tested) // the conditions we've tested when we are here in the match satisfy the prior test, and hence what it tested + } foreach { + case (priorTest, _) => + // if so, note the dependency in both tests + priorTest registerReuseBy test } dependencies(test) = tested.toSet // copies - true } + true } } + + testss foreach { tests => + tested.clear() + tests dropWhile storeDependencies + } + // println("dependencies: "+ dependencies) + // find longest prefix of tests that reuse a prior test, and whose dependent conditions monotonically increase // then, collapse these contiguous sequences of reusing tests // store the result of the final test and the intermediate results in hoisted mutable variables (TODO: optimize: don't store intermediate results that aren't used) @@ -1476,7 +2314,12 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL var okToCall = false val reusedOrOrig = (tm: TreeMaker) => {assert(okToCall); reused.getOrElse(tm, tm)} - val res = testss map { tests => + // maybe collapse: replace shared prefix of tree makers by a ReusingCondTreeMaker + // once this has been computed, we'll know which tree makers are reused, + // and we'll replace those by the ReusedCondTreeMakers we've constructed (and stored in the reused map) + val collapsed = testss map { tests => + // map tests to the equivalent list of treemakers, replacing shared prefixes by a reusing treemaker + // if there's no sharing, simply map to the tree makers corresponding to the tests var currDeps = Set[Cond]() val (sharedPrefix, suffix) = tests span { test => (test.cond eq Top) || (for( @@ -1487,23 +2330,33 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL yield diff).nonEmpty } - val collapsedTreeMakers = if (sharedPrefix.nonEmpty) { // even sharing prefixes of length 1 brings some benefit (overhead-percentage for compiler: 26->24%, lib: 19->16%) - for (test <- sharedPrefix; reusedTest <- test.reuses) reusedTest.treeMaker match { - case reusedCTM: CondTreeMaker => reused(reusedCTM) = ReusedCondTreeMaker(reusedCTM) - case _ => - } + val collapsedTreeMakers = + if (sharedPrefix.isEmpty) None + else { // even sharing prefixes of length 1 brings some benefit (overhead-percentage for compiler: 26->24%, lib: 19->16%) + for (test <- sharedPrefix; reusedTest <- test.reuses) reusedTest.treeMaker match { + case reusedCTM: CondTreeMaker => reused(reusedCTM) = ReusedCondTreeMaker(reusedCTM) + case _ => + } - // println("sharedPrefix: "+ sharedPrefix) - for (lastShared <- sharedPrefix.reverse.dropWhile(_.cond eq Top).headOption; - lastReused <- lastShared.reuses) - yield ReusingCondTreeMaker(sharedPrefix, reusedOrOrig) :: suffix.map(_.treeMaker) - } else None + // println("sharedPrefix: "+ sharedPrefix) + // if the shared prefix contains interesting conditions (!= Top) + // and the last of such interesting shared conditions reuses another treemaker's test + // replace the whole sharedPrefix by a ReusingCondTreeMaker + for (lastShared <- sharedPrefix.reverse.dropWhile(_.cond eq Top).headOption; + lastReused <- lastShared.reuses) + yield ReusingCondTreeMaker(sharedPrefix, reusedOrOrig) :: suffix.map(_.treeMaker) + } collapsedTreeMakers getOrElse tests.map(_.treeMaker) // sharedPrefix need not be empty (but it only contains Top-tests, which are dropped above) } okToCall = true // TODO: remove (debugging) - res mapConserve (_ mapConserve reusedOrOrig) + // replace original treemakers that are reused (as determined when computing collapsed), + // by ReusedCondTreeMakers + val reusedMakers = collapsed mapConserve (_ mapConserve reusedOrOrig) +// println("after CSE:") +// showTreeMakers(reusedMakers) + reusedMakers } object ReusedCondTreeMaker { @@ -1520,28 +2373,45 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // TODO: finer-grained duplication def chainBefore(next: Tree)(casegen: Casegen): Tree = // assert(codegen eq optimizedCodegen) atPos(pos)(casegen.asInstanceOf[optimizedCodegen.OptimizedCasegen].flatMapCondStored(cond, storedCond, res, nextBinder, substitution(next).duplicate)) + + override def toString = "Memo"+(nextBinder.name, storedCond.name, cond, res, substitution) } case class ReusingCondTreeMaker(sharedPrefix: List[Test], toReused: TreeMaker => TreeMaker) extends TreeMaker { import CODE._ - lazy val dropped_priors = sharedPrefix map (t => (toReused(t.treeMaker), t.reuses map (test => toReused(test.treeMaker)))) lazy val localSubstitution = { - val (from, to) = dropped_priors.collect { - case (dropped: CondTreeMaker, Some(prior: ReusedCondTreeMaker)) => - (dropped.nextBinder, REF(prior.nextBinder)) - }.unzip - val oldSubs = dropped_priors.collect { - case (dropped: TreeMaker, _) => - dropped.substitution + // replace binder of each dropped treemaker by corresponding binder bound by the most recent reused treemaker + var mostRecentReusedMaker: ReusedCondTreeMaker = null + def mapToStored(droppedBinder: Symbol) = if (mostRecentReusedMaker eq null) Nil else List((droppedBinder, REF(mostRecentReusedMaker.nextBinder))) + val (from, to) = sharedPrefix.flatMap { dropped => + dropped.reuses.map(test => toReused(test.treeMaker)).foreach { + case reusedMaker: ReusedCondTreeMaker => + mostRecentReusedMaker = reusedMaker + case _ => } - oldSubs.foldLeft(Substitution(from, to))(_ >> _) + + // TODO: have super-trait for retrieving the variable that's operated on by a tree maker + // and thus assumed in scope, either because it binds it or because it refers to it + dropped.treeMaker match { + case dropped: FunTreeMaker => + mapToStored(dropped.nextBinder) + case _ => Nil + } + }.unzip + val rerouteToReusedBinders = Substitution(from, to) + + val collapsedDroppedSubst = sharedPrefix map (t => (toReused(t.treeMaker).substitution)) + + collapsedDroppedSubst.foldLeft(rerouteToReusedBinders)(_ >> _) } - def chainBefore(next: Tree)(casegen: Casegen): Tree = { - val cond = REF(dropped_priors.reverse.collectFirst{case (_, Some(ctm: ReusedCondTreeMaker)) => ctm}.get.storedCond) + lazy val lastReusedTreeMaker = sharedPrefix.reverse.flatMap(tm => tm.reuses map (test => toReused(test.treeMaker))).collectFirst{case x: ReusedCondTreeMaker => x}.head - // TODO: finer-grained duplication -- MUST duplicate though, or we'll get VerifyErrors since sharing trees confuses lambdalift, and its confusion it emits illegal casts (diagnosed by Grzegorz: checkcast T ; invokevirtual S.m, where T not a subtype of S) - casegen.ifThenElseZero(cond, substitution(next).duplicate) + def chainBefore(next: Tree)(casegen: Casegen): Tree = { + // TODO: finer-grained duplication -- MUST duplicate though, or we'll get VerifyErrors since sharing trees confuses lambdalift, + // and in its confusion it emits illegal casts (diagnosed by Grzegorz: checkcast T ; invokevirtual S.m, where T not a subtype of S) + casegen.ifThenElseZero(REF(lastReusedTreeMaker.storedCond), substitution(next).duplicate) } + override def toString = "R"+(lastReusedTreeMaker.storedCond.name, substitution) } } @@ -1646,7 +2516,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // TODO: if patterns allow switch but the type of the scrutinee doesn't, cast (type-test) the scrutinee to the corresponding switchable type and switch on the result if (regularSwitchMaker.switchableTpe(scrutSym.tpe)) { val caseDefsWithDefault = regularSwitchMaker(cases map {c => (scrutSym, c)}, pt) - if (caseDefsWithDefault.length <= 2) None // not worth emitting a switch... also, the optimizer has trouble digesting tiny switches, apparently, so let's be nice and not generate them + if (caseDefsWithDefault isEmpty) None // not worth emitting a switch. else { // match on scrutSym -- converted to an int if necessary -- not on scrut directly (to avoid duplicating scrut) val scrutToInt: Tree = @@ -1669,10 +2539,8 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // analyze the result of approximateTreeMaker rather than the TreeMaker itself object SwitchableTreeMaker extends SwitchableTreeMakerExtractor { def unapply(x: TreeMaker): Option[Tree] = x match { - case tm@TypeTestTreeMaker(_, _, _) => - Some(Bind(tm.nextBinder, Typed(Ident(nme.WILDCARD), TypeTree(tm.nextBinderTp)) /* not used by back-end */)) // -- TODO: use this if binder does not occur in the body - case tm@TypeAndEqualityTestTreeMaker(_, patBinder, pt, _) if tm.isStraightTypeTest => - Some(Bind(tm.nextBinder, Typed(Ident(nme.WILDCARD), TypeTree(tm.nextBinderTp)) /* not used by back-end */)) + case tm@TypeTestTreeMaker(_, _, pt, _) if tm.isPureTypeTest => // -- TODO: use this if binder does not occur in the body + Some(Bind(tm.nextBinder, Typed(Ident(nme.WILDCARD), TypeTree(pt)) /* not used by back-end */)) case _ => None } @@ -1823,8 +2691,18 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL trait MatchOptimizations extends CommonSubconditionElimination with DeadCodeElimination with SwitchEmission - with OptimizedCodegen { self: TreeMakers => - override def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): (List[List[TreeMaker]], List[Tree]) = { + with OptimizedCodegen + with SymbolicMatchAnalysis { self: TreeMakers => + override def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type, unchecked: Boolean): (List[List[TreeMaker]], List[Tree]) = { + val counterExamples = if (unchecked) Nil else exhaustive(prevBinder, cases, pt) + if (counterExamples.nonEmpty) { + val ceString = + if (counterExamples.tail.isEmpty) "input: " + counterExamples.head + else "inputs: " + counterExamples.mkString(", ") + + typer.context.unit.warning(prevBinder.pos, "match may not be exhaustive.\nIt would fail on the following "+ ceString) + } + val optCases = doCSE(prevBinder, doDCE(prevBinder, cases, pt), pt) val toHoist = ( for (treeMakers <- optCases) diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 4e578e3f0d..3373878beb 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1084,8 +1084,16 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R def isEitherNullable = (NullClass.tpe <:< receiver.info) || (NullClass.tpe <:< actual.info) def isBoolean(s: Symbol) = unboxedValueClass(s) == BooleanClass def isUnit(s: Symbol) = unboxedValueClass(s) == UnitClass - def isNumeric(s: Symbol) = isNumericValueClass(unboxedValueClass(s)) || (s isSubClass ScalaNumberClass) - def isSpecial(s: Symbol) = isPrimitiveValueClass(unboxedValueClass(s)) || (s isSubClass ScalaNumberClass) || isMaybeValue(s) + def isNumeric(s: Symbol) = isNumericValueClass(unboxedValueClass(s)) || isAnyNumber(s) + def isScalaNumber(s: Symbol) = s isSubClass ScalaNumberClass + // test is behind a platform guard + def isJavaNumber(s: Symbol) = !forMSIL && (s isSubClass JavaNumberClass) + // includes java.lang.Number if appropriate [SI-5779] + def isAnyNumber(s: Symbol) = isScalaNumber(s) || isJavaNumber(s) + def isMaybeAnyValue(s: Symbol) = isPrimitiveValueClass(unboxedValueClass(s)) || isMaybeValue(s) + // used to short-circuit unrelatedTypes check if both sides are special + def isSpecial(s: Symbol) = isMaybeAnyValue(s) || isAnyNumber(s) + // unused def possibleNumericCount = onSyms(_ filter (x => isNumeric(x) || isMaybeValue(x)) size) val nullCount = onSyms(_ filter (_ == NullClass) size) @@ -1155,7 +1163,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R if (isCaseEquals) { def thisCase = receiver.info.member(nme.equals_).owner actual.info.baseClasses.find(_.isCase) match { - case Some(p) if (p != thisCase) => nonSensible("case class ", false) + case Some(p) if p != thisCase => nonSensible("case class ", false) case None => // stronger message on (Some(1) == None) //if (receiver.isCase && receiver.isEffectivelyFinal && !(receiver isSubClass actual)) nonSensiblyNeq() diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala index a6a8d6009f..fde760c752 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -14,22 +14,7 @@ import util.returning abstract class TreeCheckers extends Analyzer { import global._ - private val everything = ListBuffer[(Phase, Map[Tree, (Symbol, Type)])]() - private val currentTrees = mutable.Map[Tree, (Symbol, Type)]() - private val tpeOfTree = mutable.HashMap[Tree, Type]() - - if (settings.debug.value) { - sys addShutdownHook { - for ((ph, map) <- everything.toList) { - println("\n>>>> " + ph + "\n") - for ((tree, (sym, tpe)) <- map.toList.sortBy(_._1.summaryString)) { - println("%20s %20s %s".format(sym, tpe, ("" + tree) take 50)) - } - } - } - } - - private def classstr(x: AnyRef) = (x.getClass.getName split """\\.|\\$""").last + private def classstr(x: AnyRef) = x.getClass.getName split """\\.|\\$""" last; private def typestr(x: Type) = " (tpe = " + x + ")" private def treestr(t: Tree) = t + " [" + classstr(t) + "]" + typestr(t.tpe) private def ownerstr(s: Symbol) = "'" + s + "'" + s.locationString @@ -50,14 +35,13 @@ abstract class TreeCheckers extends Analyzer { object SymbolTracker extends Traverser { type PhaseMap = mutable.HashMap[Symbol, List[Tree]] - val defSyms = mutable.HashMap[Symbol, List[DefTree]]() withDefaultValue Nil - val newSyms = mutable.HashSet[Symbol]() val maps = ListBuffer[(Phase, PhaseMap)]() - val movedMsgs = ListBuffer[String]() - def prev = maps.init.last._2 def latest = maps.last._2 - def sortedNewSyms = newSyms.toList.distinct sortBy (_.name) + val defSyms = mutable.HashMap[Symbol, List[DefTree]]() + val newSyms = mutable.HashSet[Symbol]() + val movedMsgs = new ListBuffer[String] + def sortedNewSyms = newSyms.toList.distinct sortBy (_.name.toString) def inPrev(sym: Symbol) = { (maps.size >= 2) && (prev contains sym) @@ -108,21 +92,18 @@ abstract class TreeCheckers extends Analyzer { if (maps.isEmpty || maps.last._1 != ph) maps += ((ph, new PhaseMap)) - currentTrees.clear() traverse(unit.body) - everything += ((ph, currentTrees.toMap)) - reportChanges() } override def traverse(tree: Tree): Unit = { val sym = tree.symbol - currentTrees(tree) = ((sym, tree.tpe)) - if (sym != null && sym != NoSymbol) { record(sym, tree) tree match { - case x: DefTree => defSyms(sym) :+= x - case _ => () + case x: DefTree => + if (defSyms contains sym) defSyms(sym) = defSyms(sym) :+ x + else defSyms(sym) = List(x) + case _ => () } } @@ -130,6 +111,8 @@ abstract class TreeCheckers extends Analyzer { } } + lazy val tpeOfTree = mutable.HashMap[Tree, Type]() + def posstr(p: Position) = try p.source.path + ":" + p.line catch { case _: UnsupportedOperationException => p.toString } @@ -144,9 +127,20 @@ abstract class TreeCheckers extends Analyzer { def assertFn(cond: Boolean, msg: => Any) = if (!cond) errorFn(msg) + private def wrap[T](msg: => Any)(body: => Unit) { + try body + catch { case x => + Console.println("Caught " + x) + Console.println(msg) + x.printStackTrace + } + } + def checkTrees() { - informFn("[consistency check at the beginning of phase " + phase + "]") - currentRun.units foreach check + if (settings.verbose.value) + Console.println("[consistency check at the beginning of phase " + phase + "]") + + currentRun.units foreach (x => wrap(x)(check(x))) } def printingTypings[T](body: => T): T = { @@ -168,7 +162,7 @@ abstract class TreeCheckers extends Analyzer { informProgress("checking "+unit) val context = rootContext(unit) context.checking = true - tpeOfTree.clear() + tpeOfTree.clear SymbolTracker.check(phase, unit) val checker = new TreeChecker(context) runWithUnit(unit) { @@ -215,11 +209,11 @@ abstract class TreeCheckers extends Analyzer { tree.tpe = null saved }) - super.typed(tree, mode, pt) match { + wrap(tree)(super.typed(tree, mode, pt) match { case _: Literal => () case x if x ne tree => treesDiffer(tree, x) case _ => () - } + }) case _ => () } @@ -286,7 +280,12 @@ abstract class TreeCheckers extends Analyzer { if (sym.owner != currentOwner) { val expected = currentOwner.ownerChain find (x => cond(x)) getOrElse fail("DefTree can't find owner: ") if (sym.owner != expected) - fail("Expected owner %s (out of %s), found %s: ".format(expected, currentOwner.ownerChain, sym.owner)) + fail("""| + | currentOwner chain: %s + | symbol chain: %s""".stripMargin.format( + currentOwner.ownerChain take 3 mkString " -> ", + sym.ownerChain mkString " -> ") + ) } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index e40a567f1d..75174ca494 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2327,7 +2327,7 @@ trait Typers extends Modes with Adaptations with Taggings { 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 match_ = methodBodyTyper.typedMatch(selector, cases, mode, ptRes) + val match_ = methodBodyTyper.typedMatch(gen.mkUnchecked(selector), cases, mode, ptRes) val resTp = match_.tpe val methFormals = paramSyms map (_.tpe) @@ -2367,7 +2367,7 @@ trait Typers extends Modes with Adaptations with Taggings { 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 match_ = methodBodyTyper.typedMatch(selector, cases, mode, ptRes) + val match_ = methodBodyTyper.typedMatch(gen.mkUnchecked(selector), cases, mode, ptRes) val resTp = match_.tpe anonClass setInfo ClassInfoType(parentsPartial(List(argTp, resTp)), newScope, anonClass) @@ -2394,7 +2394,7 @@ trait Typers extends Modes with Adaptations with Taggings { paramSyms foreach (methodBodyTyper.context.scope enter _) methodSym setInfoAndEnter MethodType(paramSyms, BooleanClass.tpe) - val match_ = methodBodyTyper.typedMatch(selector, casesTrue, mode, BooleanClass.tpe) + val match_ = methodBodyTyper.typedMatch(gen.mkUnchecked(selector), casesTrue, mode, BooleanClass.tpe) val body = methodBodyTyper.virtualizedMatch(match_ withAttachment DefaultOverrideMatchAttachment(FALSE_typed), mode, BooleanClass.tpe) DefDef(methodSym, body) @@ -2510,10 +2510,7 @@ trait Typers extends Modes with Adaptations with Taggings { namer.enterSyms(stats) // need to delay rest of typedRefinement to avoid cyclic reference errors unit.toCheck += { () => - // go to next outer context which is not silent, see #3614 - var c = context - while (c.bufferErrors) c = c.outer - val stats1 = newTyper(c).typedStats(stats, NoSymbol) + val stats1 = typedStats(stats, NoSymbol) for (stat <- stats1 if stat.isDef) { val member = stat.symbol if (!(context.owner.ancestors forall @@ -2552,9 +2549,11 @@ trait Typers extends Modes with Adaptations with Taggings { else stat match { case imp @ Import(_, _) => - context = context.makeNewImport(imp) imp.symbol.initialize - typedImport(imp) + if (!imp.symbol.isError) { + context = context.makeNewImport(imp) + typedImport(imp) + } else EmptyTree case _ => if (localTarget && !includesTargetPos(stat)) { // skip typechecking of statements in a sequence where some other statement includes @@ -4422,10 +4421,6 @@ trait Typers extends Modes with Adaptations with Taggings { var cx = startingIdentContext 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. - currentRun.compileSourceFor(context.asInstanceOf[analyzer.Context], name) pre = cx.enclClass.prefix defEntry = cx.scope.lookupEntry(name) if ((defEntry ne null) && qualifies(defEntry.sym)) { diff --git a/src/library/scala/collection/mutable/FlatHashTable.scala b/src/library/scala/collection/mutable/FlatHashTable.scala index ee6d4d1d22..4070174902 100644 --- a/src/library/scala/collection/mutable/FlatHashTable.scala +++ b/src/library/scala/collection/mutable/FlatHashTable.scala @@ -50,6 +50,10 @@ trait FlatHashTable[A] extends FlatHashTable.HashUtils[A] { protected def capacity(expectedSize: Int) = if (expectedSize == 0) 1 else powerOfTwo(expectedSize) + /** The initial size of the hash table. + */ + def initialSize: Int = 32 + private def initialCapacity = capacity(initialSize) protected def randomSeed = seedGenerator.get.nextInt() @@ -361,10 +365,6 @@ private[collection] object FlatHashTable { def defaultLoadFactor: Int = 450 final def loadFactorDenum = 1000 - /** The initial size of the hash table. - */ - def initialSize: Int = 32 - def sizeForThreshold(size: Int, _loadFactor: Int) = math.max(32, (size.toLong * loadFactorDenum / _loadFactor).toInt) def newThreshold(_loadFactor: Int, size: Int) = { diff --git a/src/library/scala/collection/mutable/HashTable.scala b/src/library/scala/collection/mutable/HashTable.scala index a2f78dbde9..c307e6dcab 100644 --- a/src/library/scala/collection/mutable/HashTable.scala +++ b/src/library/scala/collection/mutable/HashTable.scala @@ -56,7 +56,15 @@ trait HashTable[A, Entry >: Null <: HashEntry[A, Entry]] extends HashTable.HashU protected def tableSizeSeed = Integer.bitCount(table.length - 1) - protected def initialSize: Int = HashTable.initialSize + /** The initial size of the hash table. + */ + protected def initialSize: Int = 16 + + /** The initial threshold. + */ + private def initialThreshold(_loadFactor: Int): Int = newThreshold(_loadFactor, initialCapacity) + + private def initialCapacity = capacity(initialSize) private def lastPopulatedIndex = { var idx = table.length - 1 @@ -354,16 +362,6 @@ private[collection] object HashTable { private[collection] final def defaultLoadFactor: Int = 750 // corresponds to 75% private[collection] final def loadFactorDenum = 1000; - /** The initial size of the hash table. - */ - private[collection] final def initialSize: Int = 16 - - /** The initial threshold. - */ - private[collection] final def initialThreshold(_loadFactor: Int): Int = newThreshold(_loadFactor, initialCapacity) - - private[collection] final def initialCapacity = capacity(initialSize) - private[collection] final def newThreshold(_loadFactor: Int, size: Int) = ((size.toLong * _loadFactor) / loadFactorDenum).toInt private[collection] final def sizeForThreshold(_loadFactor: Int, thr: Int) = ((thr.toLong * loadFactorDenum) / _loadFactor).toInt diff --git a/src/library/scala/collection/mutable/PriorityQueue.scala b/src/library/scala/collection/mutable/PriorityQueue.scala index 12dee45bae..af55a01ed6 100644 --- a/src/library/scala/collection/mutable/PriorityQueue.scala +++ b/src/library/scala/collection/mutable/PriorityQueue.scala @@ -161,10 +161,13 @@ class PriorityQueue[A](implicit val ord: Ordering[A]) */ def clear(): Unit = { resarr.p_size0 = 1 } - /** Returns an iterator which yields all the elements of the priority - * queue in descending priority order. + /** Returns an iterator which yields all the elements. * - * @return an iterator over all elements sorted in descending order. + * Note: The order of elements returned is undefined. + * If you want to traverse the elements in priority queue + * order, use `clone().dequeueAll.iterator`. + * + * @return an iterator over all the elements. */ override def iterator: Iterator[A] = new AbstractIterator[A] { private var i = 1 @@ -176,7 +179,6 @@ class PriorityQueue[A](implicit val ord: Ordering[A]) } } - /** Returns the reverse of this queue. The priority queue that gets * returned will have an inversed ordering - if for some elements * `x` and `y` the original queue's ordering @@ -198,6 +200,13 @@ class PriorityQueue[A](implicit val ord: Ordering[A]) revq } + /** Returns an iterator which yields all the elements in the reverse order + * than that returned by the method `iterator`. + * + * Note: The order of elements returned is undefined. + * + * @return an iterator over all elements sorted in descending order. + */ def reverseIterator: Iterator[A] = new AbstractIterator[A] { private var i = resarr.p_size0 - 1 def hasNext: Boolean = i >= 1 @@ -217,6 +226,8 @@ class PriorityQueue[A](implicit val ord: Ordering[A]) throw new UnsupportedOperationException("unsuitable as hash key") /** Returns a regular queue containing the same elements. + * + * Note: the order of elements is undefined. */ def toQueue: Queue[A] = new Queue[A] ++= this.iterator @@ -225,6 +236,13 @@ class PriorityQueue[A](implicit val ord: Ordering[A]) * @return the string representation of this queue. */ override def toString() = toList.mkString("PriorityQueue(", ", ", ")") + + /** Converts this $coll to a list. + * + * Note: the order of elements is undefined. + * + * @return a list containing all elements of this $coll. + */ override def toList = this.iterator.toList /** This method clones the priority queue. diff --git a/src/library/scala/collection/mutable/StackProxy.scala b/src/library/scala/collection/mutable/StackProxy.scala index 8884f03bbd..9eadfe4045 100644 --- a/src/library/scala/collection/mutable/StackProxy.scala +++ b/src/library/scala/collection/mutable/StackProxy.scala @@ -54,6 +54,11 @@ trait StackProxy[A] extends Stack[A] with Proxy { this } + override def push(elem: A): this.type = { + self.push(elem) + this + } + /** Returns the top element of the stack. This method will not remove * the element from the stack. An error is signaled if there is no * element on the stack. diff --git a/src/library/scala/collection/package.scala b/src/library/scala/collection/package.scala index 237ca28018..ac5cb66942 100644 --- a/src/library/scala/collection/package.scala +++ b/src/library/scala/collection/package.scala @@ -45,7 +45,7 @@ package scala * * The most common way to create a collection is to use the companion objects as factories. * Of these, the three most common - * are [[scala.collection.immutable.Seq]], [[scala.collection.immutable.Set]], and [[scala.collection.immutable.Map]]. Their + * are [[scala.collection.Seq]], [[scala.collection.immutable.Set]], and [[scala.collection.immutable.Map]]. Their * companion objects are all available * as type aliases the either the [[scala]] package or in `scala.Predef`, and can be used * like so: @@ -61,7 +61,7 @@ package scala * }}} * * It is also typical to use the [[scala.collection.immutable]] collections over those - * in [[scala.collection.mutable]]; The types aliased in the [[scala]] package and + * in [[scala.collection.mutable]]; The types aliased in * the `scala.Predef` object are the immutable versions. * * Also note that the collections library was carefully designed to include several implementations of diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 4df2bb63af..c42393eee2 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -82,9 +82,29 @@ import language.higherKinds * {{{ * f flatMap { (x: Int) => g map { (y: Int) => x + y } } * }}} + * + * @define callbackInContext + * The provided callback always runs in the provided implicit + *`ExecutionContext`, though there is no guarantee that the + * `execute()` method on the `ExecutionContext` will be called once + * per callback or that `execute()` will be called in the current + * thread. That is, the implementation may run multiple callbacks + * in a batch within a single `execute()` and it may run + * `execute()` either immediately or asynchronously. */ trait Future[+T] extends Awaitable[T] { + // The executor within the lexical scope + // of the Future trait. Note that this will + // (modulo bugs) _never_ execute a callback + // other than those below in this same file. + // As a nice side benefit, having this implicit + // here forces an ambiguity in those methods + // that also have an executor parameter, which + // keeps us from accidentally forgetting to use + // the executor parameter. + private implicit def internalExecutor: ExecutionContext = Future.InternalCallbackExecutor + /* Callbacks */ /** When this future is completed successfully (i.e. with a value), @@ -95,11 +115,12 @@ trait Future[+T] extends Awaitable[T] { * this will either be applied immediately or be scheduled asynchronously. * * $multipleCallbacks + * $callbackInContext */ - def onSuccess[U](pf: PartialFunction[T, U]): Unit = onComplete { + def onSuccess[U](pf: PartialFunction[T, U])(implicit executor: ExecutionContext): Unit = onComplete { case Right(v) if pf isDefinedAt v => pf(v) case _ => - } + }(executor) /** When this future is completed with a failure (i.e. with a throwable), * apply the provided callback to the throwable. @@ -112,11 +133,12 @@ trait Future[+T] extends Awaitable[T] { * Will not be called in case that the future is completed with a value. * * $multipleCallbacks + * $callbackInContext */ - def onFailure[U](callback: PartialFunction[Throwable, U]): Unit = onComplete { + def onFailure[U](callback: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Unit = onComplete { case Left(t) if (isFutureThrowable(t) && callback.isDefinedAt(t)) => callback(t) case _ => - } + }(executor) /** When this future is completed, either through an exception, or a value, * apply the provided function. @@ -125,8 +147,9 @@ trait Future[+T] extends Awaitable[T] { * this will either be applied immediately or be scheduled asynchronously. * * $multipleCallbacks + * $callbackInContext */ - def onComplete[U](func: Either[Throwable, T] => U): Unit + def onComplete[U](func: Either[Throwable, T] => U)(implicit executor: ExecutionContext): Unit /* Miscellaneous */ @@ -182,10 +205,10 @@ trait Future[+T] extends Awaitable[T] { * * Will not be called if the future fails. */ - def foreach[U](f: T => U): Unit = onComplete { + def foreach[U](f: T => U)(implicit executor: ExecutionContext): Unit = onComplete { case Right(r) => f(r) case _ => // do nothing - } + }(executor) /** Creates a new future by applying the 's' function to the successful result of * this future, or the 'f' function to the failed result. If there is any non-fatal @@ -198,7 +221,7 @@ trait Future[+T] extends Awaitable[T] { * the returned future * @return a future that will be completed with the transformed value */ - def transform[S](s: T => S, f: Throwable => Throwable): Future[S] = { + def transform[S](s: T => S, f: Throwable => Throwable)(implicit executor: ExecutionContext): Future[S] = { val p = Promise[S]() onComplete { @@ -211,7 +234,7 @@ trait Future[+T] extends Awaitable[T] { } catch { case NonFatal(t) => p failure t } - } + }(executor) p.future } @@ -222,7 +245,7 @@ trait Future[+T] extends Awaitable[T] { * * $forComprehensionExamples */ - def map[S](f: T => S): Future[S] = { // transform(f, identity) + def map[S](f: T => S)(implicit executor: ExecutionContext): Future[S] = { // transform(f, identity) val p = Promise[S]() onComplete { @@ -235,7 +258,7 @@ trait Future[+T] extends Awaitable[T] { } catch { case NonFatal(t) => p failure t } - } + }(executor) p.future } @@ -247,21 +270,21 @@ trait Future[+T] extends Awaitable[T] { * * $forComprehensionExamples */ - def flatMap[S](f: T => Future[S]): Future[S] = { + def flatMap[S](f: T => Future[S])(implicit executor: ExecutionContext): Future[S] = { val p = Promise[S]() onComplete { case l: Left[_, _] => p complete l.asInstanceOf[Left[Throwable, S]] case Right(v) => try { - f(v) onComplete { + f(v).onComplete({ case l: Left[_, _] => p complete l.asInstanceOf[Left[Throwable, S]] case Right(v) => p success v - } + })(internalExecutor) } catch { case NonFatal(t) => p failure t } - } + }(executor) p.future } @@ -282,7 +305,7 @@ trait Future[+T] extends Awaitable[T] { * await(h, 0) // throw a NoSuchElementException * }}} */ - def filter(pred: T => Boolean): Future[T] = { + def filter(pred: T => Boolean)(implicit executor: ExecutionContext): Future[T] = { val p = Promise[T]() onComplete { @@ -294,14 +317,14 @@ trait Future[+T] extends Awaitable[T] { } catch { case NonFatal(t) => p failure t } - } + }(executor) p.future } /** Used by for-comprehensions. */ - final def withFilter(p: T => Boolean): Future[T] = filter(p) + final def withFilter(p: T => Boolean)(implicit executor: ExecutionContext): Future[T] = filter(p)(executor) // final def withFilter(p: T => Boolean) = new FutureWithFilter[T](this, p) // final class FutureWithFilter[+S](self: Future[S], p: S => Boolean) { @@ -331,7 +354,7 @@ trait Future[+T] extends Awaitable[T] { * await(h, 0) // throw a NoSuchElementException * }}} */ - def collect[S](pf: PartialFunction[T, S]): Future[S] = { + def collect[S](pf: PartialFunction[T, S])(implicit executor: ExecutionContext): Future[S] = { val p = Promise[S]() onComplete { @@ -343,7 +366,7 @@ trait Future[+T] extends Awaitable[T] { } catch { case NonFatal(t) => p failure t } - } + }(executor) p.future } @@ -360,7 +383,7 @@ trait Future[+T] extends Awaitable[T] { * future (6 / 2) recover { case e: ArithmeticException ⇒ 0 } // result: 3 * }}} */ - def recover[U >: T](pf: PartialFunction[Throwable, U]): Future[U] = { + def recover[U >: T](pf: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Future[U] = { val p = Promise[U]() onComplete { @@ -370,7 +393,7 @@ trait Future[+T] extends Awaitable[T] { case NonFatal(t) => p failure t } case otherwise => p complete otherwise - } + }(executor) p.future } @@ -388,7 +411,7 @@ trait Future[+T] extends Awaitable[T] { * future (6 / 0) recoverWith { case e: ArithmeticException => f } // result: Int.MaxValue * }}} */ - def recoverWith[U >: T](pf: PartialFunction[Throwable, Future[U]]): Future[U] = { + def recoverWith[U >: T](pf: PartialFunction[Throwable, Future[U]])(implicit executor: ExecutionContext): Future[U] = { val p = Promise[U]() onComplete { @@ -399,7 +422,7 @@ trait Future[+T] extends Awaitable[T] { case NonFatal(t) => p failure t } case otherwise => p complete otherwise - } + }(executor) p.future } @@ -498,12 +521,12 @@ trait Future[+T] extends Awaitable[T] { * } * }}} */ - def andThen[U](pf: PartialFunction[Either[Throwable, T], U]): Future[T] = { + def andThen[U](pf: PartialFunction[Either[Throwable, T], U])(implicit executor: ExecutionContext): Future[T] = { val p = Promise[T]() onComplete { case r => try if (pf isDefinedAt r) pf(r) finally p complete r - } + }(executor) p.future } @@ -656,6 +679,33 @@ object Future { for (r <- fr; b <- fb) yield (r += b) }.map(_.result) + // This is used to run callbacks which are internal + // to scala.concurrent; our own callbacks are only + // ever used to eventually run another callback, + // and that other callback will have its own + // executor because all callbacks come with + // an executor. Our own callbacks never block + // and have no "expected" exceptions. + // As a result, this executor can do nothing; + // some other executor will always come after + // it (and sometimes one will be before it), + // and those will be performing the "real" + // dispatch to code outside scala.concurrent. + // Because this exists, ExecutionContext.defaultExecutionContext + // isn't instantiated by Future internals, so + // if some code for some reason wants to avoid + // ever starting up the default context, it can do so + // by just not ever using it itself. scala.concurrent + // doesn't need to create defaultExecutionContext as + // a side effect. + private[concurrent] object InternalCallbackExecutor extends ExecutionContext { + def execute(runnable: Runnable): Unit = + runnable.run() + def internalBlockingCall[T](awaitable: Awaitable[T], atMost: Duration): T = + throw new IllegalStateException("bug in scala.concurrent, called blocking() from internal callback") + def reportFailure(t: Throwable): Unit = + throw new IllegalStateException("problem in scala.concurrent internal callback", t) + } } diff --git a/src/library/scala/concurrent/Promise.scala b/src/library/scala/concurrent/Promise.scala index 2e9de4a0d0..578642966f 100644 --- a/src/library/scala/concurrent/Promise.scala +++ b/src/library/scala/concurrent/Promise.scala @@ -25,6 +25,11 @@ package scala.concurrent */ trait Promise[T] { + // used for internal callbacks defined in + // the lexical scope of this trait; + // _never_ for application callbacks. + private implicit def internalExecutor: ExecutionContext = Future.InternalCallbackExecutor + /** Future containing the value of this promise. */ def future: Future[T] @@ -106,26 +111,23 @@ object Promise { /** Creates a promise object which can be completed with a value. * * @tparam T the type of the value in the promise - * @param executor the execution context on which the promise is created on * @return the newly created `Promise` object */ - def apply[T]()(implicit executor: ExecutionContext): Promise[T] = new impl.Promise.DefaultPromise[T]() + def apply[T](): Promise[T] = new impl.Promise.DefaultPromise[T]() /** Creates an already completed Promise with the specified exception. * * @tparam T the type of the value in the promise - * @param executor the execution context on which the promise is created on * @return the newly created `Promise` object */ - def failed[T](exception: Throwable)(implicit executor: ExecutionContext): Promise[T] = new impl.Promise.KeptPromise[T](Left(exception)) + def failed[T](exception: Throwable): Promise[T] = new impl.Promise.KeptPromise[T](Left(exception)) /** Creates an already completed Promise with the specified result. * * @tparam T the type of the value in the promise - * @param executor the execution context on which the promise is created on * @return the newly created `Promise` object */ - def successful[T](result: T)(implicit executor: ExecutionContext): Promise[T] = new impl.Promise.KeptPromise[T](Right(result)) + def successful[T](result: T): Promise[T] = new impl.Promise.KeptPromise[T](Right(result)) } diff --git a/src/library/scala/concurrent/impl/Future.scala b/src/library/scala/concurrent/impl/Future.scala index a54e81bd05..47534e398b 100644 --- a/src/library/scala/concurrent/impl/Future.scala +++ b/src/library/scala/concurrent/impl/Future.scala @@ -17,8 +17,6 @@ import scala.collection.mutable.Stack private[concurrent] trait Future[+T] extends scala.concurrent.Future[T] with Awaitable[T] { - implicit def executor: ExecutionContext - } private[concurrent] object Future { diff --git a/src/library/scala/concurrent/impl/Promise.scala b/src/library/scala/concurrent/impl/Promise.scala index 78053f5117..1d573ef818 100644 --- a/src/library/scala/concurrent/impl/Promise.scala +++ b/src/library/scala/concurrent/impl/Promise.scala @@ -42,7 +42,7 @@ object Promise { /** Default promise implementation. */ - class DefaultPromise[T](implicit val executor: ExecutionContext) extends AbstractPromise with Promise[T] { self => + class DefaultPromise[T] extends AbstractPromise with Promise[T] { self => updateState(null, Nil) // Start at "No callbacks" //FIXME switch to Unsafe instead of ARFU protected final def tryAwait(atMost: Duration): Boolean = { @@ -108,21 +108,26 @@ object Promise { }) match { case null => false case cs if cs.isEmpty => true - case cs => Future.dispatchFuture(executor, () => cs.foreach(f => notifyCompleted(f, resolved))); true + // this assumes that f(resolved) will go via dispatchFuture + // and notifyCompleted (see onComplete below) + case cs => cs.foreach(f => f(resolved)); true } } - def onComplete[U](func: Either[Throwable, T] => U): Unit = { + def onComplete[U](func: Either[Throwable, T] => U)(implicit executor: ExecutionContext): Unit = { + val bound: Either[Throwable, T] => Unit = (either: Either[Throwable, T]) => + Future.dispatchFuture(executor, () => notifyCompleted(func, either)) + @tailrec //Tries to add the callback, if already completed, it dispatches the callback to be executed def dispatchOrAddCallback(): Unit = getState match { - case r: Either[_, _] => Future.dispatchFuture(executor, () => notifyCompleted(func, r.asInstanceOf[Either[Throwable, T]])) - case listeners: List[_] => if (updateState(listeners, func :: listeners)) () else dispatchOrAddCallback() + case r: Either[_, _] => bound(r.asInstanceOf[Either[Throwable, T]]) + case listeners: List[_] => if (updateState(listeners, bound :: listeners)) () else dispatchOrAddCallback() } dispatchOrAddCallback() } - private final def notifyCompleted(func: Either[Throwable, T] => Any, result: Either[Throwable, T]) { + private final def notifyCompleted(func: Either[Throwable, T] => Any, result: Either[Throwable, T])(implicit executor: ExecutionContext) { try { func(result) } catch { @@ -135,7 +140,7 @@ object Promise { * * Useful in Future-composition when a value to contribute is already available. */ - final class KeptPromise[T](suppliedValue: Either[Throwable, T])(implicit val executor: ExecutionContext) extends Promise[T] { + final class KeptPromise[T](suppliedValue: Either[Throwable, T]) extends Promise[T] { val value = Some(resolveEither(suppliedValue)) @@ -143,7 +148,7 @@ object Promise { def tryComplete(value: Either[Throwable, T]): Boolean = false - def onComplete[U](func: Either[Throwable, T] => U): Unit = { + def onComplete[U](func: Either[Throwable, T] => U)(implicit executor: ExecutionContext): Unit = { val completedAs = value.get // Avoid closing over "this" Future.dispatchFuture(executor, () => func(completedAs)) } diff --git a/src/partest/scala/tools/partest/PartestTask.scala b/src/partest/scala/tools/partest/PartestTask.scala index ad2e155182..67b38d2e24 100644 --- a/src/partest/scala/tools/partest/PartestTask.scala +++ b/src/partest/scala/tools/partest/PartestTask.scala @@ -309,6 +309,16 @@ class PartestTask extends Task with CompilationPathProperty { } } getOrElse sys.error("Provided classpath does not contain a Scala actors.") + val scalaActorsMigration = { + (classpath.list map { fs => new File(fs) }) find { f => + f.getName match { + case "scala-actors-migration.jar" => true + case "actors-migration" if (f.getParentFile.getName == "classes") => true + case _ => false + } + } + } getOrElse sys.error("Provided classpath does not contain a Scala actors.") + def scalacArgsFlat: Option[Seq[String]] = scalacArgs map (_ flatMap { a => val parts = a.getParts if(parts eq null) Seq[String]() else parts.toSeq @@ -335,6 +345,7 @@ class PartestTask extends Task with CompilationPathProperty { antFileManager.LATEST_COMP = scalaCompiler.getAbsolutePath antFileManager.LATEST_PARTEST = scalaPartest.getAbsolutePath antFileManager.LATEST_ACTORS = scalaActors.getAbsolutePath + antFileManager.LATEST_ACTORS_MIGRATION = scalaActorsMigration.getAbsolutePath javacmd foreach (x => antFileManager.JAVACMD = x.getAbsolutePath) javaccmd foreach (x => antFileManager.JAVAC_CMD = x.getAbsolutePath) diff --git a/src/partest/scala/tools/partest/nest/AntRunner.scala b/src/partest/scala/tools/partest/nest/AntRunner.scala index e77385d6e9..dc83e4ea66 100644 --- a/src/partest/scala/tools/partest/nest/AntRunner.scala +++ b/src/partest/scala/tools/partest/nest/AntRunner.scala @@ -23,6 +23,7 @@ class AntRunner extends DirectRunner { var LATEST_COMP: String = _ var LATEST_PARTEST: String = _ var LATEST_ACTORS: String = _ + var LATEST_ACTORS_MIGRATION: String = _ val testRootPath: String = "test" val testRootDir: Directory = Directory(testRootPath) } diff --git a/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala b/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala index 8d239a84bd..b270a6b65a 100644 --- a/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala +++ b/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala @@ -84,6 +84,7 @@ class ConsoleFileManager extends FileManager { latestFile = testClassesDir.parent / "bin" latestLibFile = testClassesDir / "library" latestActorsFile = testClassesDir / "library" / "actors" + latestActMigFile = testClassesDir / "actors-migration" latestCompFile = testClassesDir / "compiler" latestPartestFile = testClassesDir / "partest" latestFjbgFile = testParent / "lib" / "fjbg.jar" @@ -94,6 +95,7 @@ class ConsoleFileManager extends FileManager { latestFile = dir / "bin" latestLibFile = dir / "lib/scala-library.jar" latestActorsFile = dir / "lib/scala-actors.jar" + latestActMigFile = dir / "lib/scala-actors-migration.jar" latestCompFile = dir / "lib/scala-compiler.jar" latestPartestFile = dir / "lib/scala-partest.jar" latestFjbgFile = testParent / "lib" / "fjbg.jar" @@ -104,6 +106,7 @@ class ConsoleFileManager extends FileManager { latestFile = prefixFile("build/quick/bin") latestLibFile = prefixFile("build/quick/classes/library") latestActorsFile = prefixFile("build/quick/classes/library/actors") + latestActMigFile = prefixFile("build/quick/classes/actors-migration") latestCompFile = prefixFile("build/quick/classes/compiler") latestPartestFile = prefixFile("build/quick/classes/partest") } @@ -114,6 +117,7 @@ class ConsoleFileManager extends FileManager { latestFile = prefixFileWith(p, "bin") latestLibFile = prefixFileWith(p, "lib/scala-library.jar") latestActorsFile = prefixFileWith(p, "lib/scala-actors.jar") + latestActMigFile = prefixFileWith(p, "lib/scala-actors-migration.jar") latestCompFile = prefixFileWith(p, "lib/scala-compiler.jar") latestPartestFile = prefixFileWith(p, "lib/scala-partest.jar") } @@ -123,6 +127,7 @@ class ConsoleFileManager extends FileManager { latestFile = prefixFile("dists/latest/bin") latestLibFile = prefixFile("dists/latest/lib/scala-library.jar") latestActorsFile = prefixFile("dists/latest/lib/scala-actors.jar") + latestActMigFile = prefixFile("dists/latest/lib/scala-actors-migration.jar") latestCompFile = prefixFile("dists/latest/lib/scala-compiler.jar") latestPartestFile = prefixFile("dists/latest/lib/scala-partest.jar") } @@ -132,6 +137,7 @@ class ConsoleFileManager extends FileManager { latestFile = prefixFile("build/pack/bin") latestLibFile = prefixFile("build/pack/lib/scala-library.jar") latestActorsFile = prefixFile("build/pack/lib/scala-actors.jar") + latestActMigFile = prefixFile("build/pack/lib/scala-actors-migration.jar") latestCompFile = prefixFile("build/pack/lib/scala-compiler.jar") latestPartestFile = prefixFile("build/pack/lib/scala-partest.jar") } @@ -167,16 +173,19 @@ class ConsoleFileManager extends FileManager { LATEST_COMP = latestCompFile.getAbsolutePath LATEST_PARTEST = latestPartestFile.getAbsolutePath LATEST_ACTORS = latestActorsFile.getAbsolutePath + LATEST_ACTORS_MIGRATION = latestActMigFile.getAbsolutePath } var LATEST_LIB: String = "" var LATEST_COMP: String = "" var LATEST_PARTEST: String = "" var LATEST_ACTORS: String = "" + var LATEST_ACTORS_MIGRATION: String = "" var latestFile: File = _ var latestLibFile: File = _ var latestActorsFile: File = _ + var latestActMigFile: File = _ var latestCompFile: File = _ var latestPartestFile: File = _ var latestFjbgFile: File = _ diff --git a/src/partest/scala/tools/partest/nest/DirectRunner.scala b/src/partest/scala/tools/partest/nest/DirectRunner.scala index 20f435cfbb..815c27f567 100644 --- a/src/partest/scala/tools/partest/nest/DirectRunner.scala +++ b/src/partest/scala/tools/partest/nest/DirectRunner.scala @@ -61,10 +61,10 @@ trait DirectRunner { val latestLibFile = new File(fileManager.LATEST_LIB) val latestPartestFile = new File(fileManager.LATEST_PARTEST) val latestActorsFile = new File(fileManager.LATEST_ACTORS) - + val latestActMigFile = new File(fileManager.LATEST_ACTORS_MIGRATION) val scalacheckURL = PathSettings.scalaCheck.toURL val scalaCheckParentClassLoader = ScalaClassLoader.fromURLs( - scalacheckURL :: (List(latestCompFile, latestLibFile, latestActorsFile, latestPartestFile).map(_.toURI.toURL)) + scalacheckURL :: (List(latestCompFile, latestLibFile, latestActorsFile, latestActMigFile, latestPartestFile).map(_.toURI.toURL)) ) Output.init() diff --git a/src/partest/scala/tools/partest/nest/FileManager.scala b/src/partest/scala/tools/partest/nest/FileManager.scala index 6d9e64730f..cf7160f521 100644 --- a/src/partest/scala/tools/partest/nest/FileManager.scala +++ b/src/partest/scala/tools/partest/nest/FileManager.scala @@ -63,6 +63,7 @@ trait FileManager extends FileUtil { var LATEST_COMP: String var LATEST_PARTEST: String var LATEST_ACTORS: String + var LATEST_ACTORS_MIGRATION: String var showDiff = false var updateCheck = false diff --git a/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala b/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala index a0511774a9..a5d5952ff7 100644 --- a/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala +++ b/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala @@ -48,9 +48,9 @@ class ReflectiveRunner { new ConsoleFileManager import fileManager. - { latestCompFile, latestLibFile, latestPartestFile, latestFjbgFile, latestScalapFile, latestActorsFile } + { latestCompFile, latestLibFile, latestPartestFile, latestFjbgFile, latestScalapFile, latestActorsFile, latestActMigFile } val files = - Array(latestCompFile, latestLibFile, latestPartestFile, latestFjbgFile, latestScalapFile, latestActorsFile) map (x => io.File(x)) + Array(latestCompFile, latestLibFile, latestPartestFile, latestFjbgFile, latestScalapFile, latestActorsFile, latestActMigFile) map (x => io.File(x)) val sepUrls = files map (_.toURL) var sepLoader = new URLClassLoader(sepUrls, null) diff --git a/src/partest/scala/tools/partest/nest/SBTRunner.scala b/src/partest/scala/tools/partest/nest/SBTRunner.scala index 5d994eeb37..14e2dc3df9 100644 --- a/src/partest/scala/tools/partest/nest/SBTRunner.scala +++ b/src/partest/scala/tools/partest/nest/SBTRunner.scala @@ -16,6 +16,7 @@ object SBTRunner extends DirectRunner { var LATEST_COMP: String = _ var LATEST_PARTEST: String = _ var LATEST_ACTORS: String = _ + var LATEST_ACTORS_MIGRATION: String = _ val testRootPath: String = "test" val testRootDir: Directory = Directory(testRootPath) } @@ -65,6 +66,7 @@ object SBTRunner extends DirectRunner { fileManager.LATEST_COMP = findClasspath("scala-compiler", "scala-compiler") getOrElse sys.error("No scala-compiler found! Classpath = " + fileManager.CLASSPATH) fileManager.LATEST_PARTEST = findClasspath("scala-partest", "partest") getOrElse sys.error("No scala-partest found! Classpath = " + fileManager.CLASSPATH) fileManager.LATEST_ACTORS = findClasspath("scala-actors", "actors") getOrElse sys.error("No scala-actors found! Classpath = " + fileManager.CLASSPATH) + fileManager.LATEST_ACTORS_MIGRATION = findClasspath("scala-actors-migration", "actors-migration") getOrElse sys.error("No scala-actors-migration found! Classpath = " + fileManager.CLASSPATH) // TODO - Do something useful here!!! fileManager.JAVAC_CMD = "javac" diff --git a/src/partest/scala/tools/partest/nest/Worker.scala b/src/partest/scala/tools/partest/nest/Worker.scala index 9326a8b232..40325c6375 100644 --- a/src/partest/scala/tools/partest/nest/Worker.scala +++ b/src/partest/scala/tools/partest/nest/Worker.scala @@ -56,6 +56,7 @@ class ScalaCheckFileManager(val origmanager: FileManager) extends FileManager { var LATEST_COMP: String = origmanager.LATEST_COMP var LATEST_PARTEST: String = origmanager.LATEST_PARTEST var LATEST_ACTORS: String = origmanager.LATEST_ACTORS + var LATEST_ACTORS_MIGRATION: String = origmanager.LATEST_ACTORS_MIGRATION } object Output { |