diff options
120 files changed, 2192 insertions, 1051 deletions
diff --git a/lib/forkjoin.jar.desired.sha1 b/lib/forkjoin.jar.desired.sha1 index d37b84d8c7..d31539daf4 100644 --- a/lib/forkjoin.jar.desired.sha1 +++ b/lib/forkjoin.jar.desired.sha1 @@ -1 +1 @@ -996fc132b05046112b9d4dc62e2d2c9057d836bc ?forkjoin.jar +b5baf94dff8d3ca991d44a59add7bcbf6640379b ?forkjoin.jar diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala index 4d1dc69afc..3150b674af 100644 --- a/src/compiler/scala/reflect/internal/Definitions.scala +++ b/src/compiler/scala/reflect/internal/Definitions.scala @@ -292,7 +292,7 @@ trait Definitions extends reflect.api.StandardDefinitions { sealed abstract class BottomClassSymbol(name: TypeName, parent: Symbol) extends ClassSymbol(ScalaPackageClass, NoPosition, name) { locally { - this initFlags ABSTRACT | TRAIT | FINAL + this initFlags ABSTRACT | FINAL this setInfoAndEnter ClassInfoType(List(parent.tpe), newScope, this) } final override def isBottomClass = true @@ -1021,10 +1021,10 @@ trait Definitions extends reflect.api.StandardDefinitions { val flatname = nme.flattenedName(owner.name, name) getMember(pkg, flatname) } else { - throw new FatalError(owner + " does not have a member " + name) - } + throw new FatalError(owner + " does not have a member " + name) } } + } def getMemberIfDefined(owner: Symbol, name: Name): Symbol = owner.info.nonPrivateMember(name) diff --git a/src/compiler/scala/reflect/internal/StdNames.scala b/src/compiler/scala/reflect/internal/StdNames.scala index ac2cf178bf..bf468affe6 100644 --- a/src/compiler/scala/reflect/internal/StdNames.scala +++ b/src/compiler/scala/reflect/internal/StdNames.scala @@ -206,6 +206,7 @@ trait StdNames extends NameManglers { self: SymbolTable => val MIRROR_FREE_PREFIX: NameType = "free$" val MIRROR_FREE_THIS_SUFFIX: NameType = "$this" val MIRROR_FREE_VALUE_SUFFIX: NameType = "$value" + val MIRROR_SYMDEF_PREFIX: NameType = "symdef$" val MIXIN_CONSTRUCTOR: NameType = "$init$" val MODULE_INSTANCE_FIELD: NameType = NameTransformer.MODULE_INSTANCE_NAME // "MODULE$" val OUTER: NameType = "$outer" diff --git a/src/compiler/scala/reflect/internal/SymbolCreations.scala b/src/compiler/scala/reflect/internal/SymbolCreations.scala index a1163b0f57..ac82ffe62a 100644 --- a/src/compiler/scala/reflect/internal/SymbolCreations.scala +++ b/src/compiler/scala/reflect/internal/SymbolCreations.scala @@ -11,7 +11,6 @@ import scala.collection.mutable.ListBuffer import util.Statistics._ import Flags._ import api.Modifier -import scala.tools.util.StringOps.{ ojoin } trait SymbolCreations { self: SymbolTable => diff --git a/src/compiler/scala/reflect/internal/SymbolFlags.scala b/src/compiler/scala/reflect/internal/SymbolFlags.scala index febcec8c7c..7741d700b9 100644 --- a/src/compiler/scala/reflect/internal/SymbolFlags.scala +++ b/src/compiler/scala/reflect/internal/SymbolFlags.scala @@ -11,7 +11,6 @@ import scala.collection.mutable.ListBuffer import util.Statistics._ import Flags._ import api.Modifier -import scala.tools.util.StringOps.{ ojoin } trait SymbolFlags { self: SymbolTable => diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index 3bb57cea04..6eaae7f1ee 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -64,6 +64,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => this: Symbol => def kind: String = kindString + def isExistential: Boolean = this.isExistentiallyBound def newNestedSymbol(name: Name, pos: Position, newFlags: Long, isClass: Boolean): Symbol = name match { case n: TermName => newTermSymbol(n, pos, newFlags) @@ -706,7 +707,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => || isAnonOrRefinementClass // has uninteresting <anon> or <refinement> prefix || nme.isReplWrapperName(name) // has ugly $iw. prefix (doesn't call isInterpreterWrapper due to nesting) ) - def isFBounded = info.baseTypeSeq exists (_ contains this) + def isFBounded = info match { + case TypeBounds(_, _) => info.baseTypeSeq exists (_ contains this) + case _ => false + } /** Is symbol a monomorphic type? * assumption: if a type starts out as monomorphic, it will not acquire @@ -897,8 +901,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => if (!owner.isLocatable) return false if (owner.isTerm) return false + if (isLocalDummy) return false if (isType && isNonClassType) return false + if (isRefinementClass) return false return true } @@ -1202,8 +1208,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Set new info valid from start of this phase. */ def updateInfo(info: Type): Symbol = { - assert(phaseId(infos.validFrom) <= phase.id) - if (phaseId(infos.validFrom) == phase.id) infos = infos.prev + val pid = phaseId(infos.validFrom) + assert(pid <= phase.id, (pid, phase.id)) + if (pid == phase.id) infos = infos.prev infos = TypeHistory(currentPeriod, info, infos) _validTo = if (info.isComplete) currentPeriod else NoPeriod this @@ -2965,7 +2972,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def isFreeTerm = true } - // [Eugene] the NoSymbol origin works for type parameters. what about existential free types? class FreeType(name0: TypeName, value0: => Any, val origin: String) extends TypeSkolem(NoSymbol, NoPosition, name0, NoSymbol) { def value = value0 override def isFreeType = true diff --git a/src/compiler/scala/reflect/internal/TreeInfo.scala b/src/compiler/scala/reflect/internal/TreeInfo.scala index a8cca1625f..039c8e557a 100644 --- a/src/compiler/scala/reflect/internal/TreeInfo.scala +++ b/src/compiler/scala/reflect/internal/TreeInfo.scala @@ -590,7 +590,7 @@ abstract class TreeInfo { } object Reified { - def unapply(tree: Tree): Option[(Tree, List[ValDef], Tree)] = tree match { + def unapply(tree: Tree): Option[(Tree, List[Tree], Tree)] = tree match { case ReifiedTree(reifee, symbolTable, reified, _) => Some(reifee, symbolTable, reified) case ReifiedType(reifee, symbolTable, reified) => @@ -601,16 +601,16 @@ abstract class TreeInfo { } object ReifiedTree { - def unapply(tree: Tree): Option[(Tree, List[ValDef], Tree, Tree)] = tree match { + def unapply(tree: Tree): Option[(Tree, List[Tree], Tree, Tree)] = tree match { case reifee @ Block((mrDef @ ValDef(_, _, _, _)) :: symbolTable, Apply(Apply(_, List(tree)), List(Apply(_, List(tpe))))) if mrDef.name == nme.MIRROR_SHORT => - Some(reifee, symbolTable map (_.asInstanceOf[ValDef]), tree, tpe) + Some(reifee, symbolTable, tree, tpe) case _ => None } } object InlineableTreeSplice { - def unapply(tree: Tree): Option[(Tree, List[ValDef], Tree, Tree, Symbol)] = tree match { + def unapply(tree: Tree): Option[(Tree, List[Tree], Tree, Tree, Symbol)] = tree match { case select @ Select(ReifiedTree(splicee, symbolTable, tree, tpe), _) if select.symbol == ExprEval || select.symbol == ExprValue => Some(splicee, symbolTable, tree, tpe, select.symbol) case _ => @@ -619,7 +619,7 @@ abstract class TreeInfo { } object InlinedTreeSplice { - def unapply(tree: Tree): Option[(Tree, List[ValDef], Tree, Tree)] = tree match { + def unapply(tree: Tree): Option[(Tree, List[Tree], Tree, Tree)] = tree match { case Select(ReifiedTree(splicee, symbolTable, tree, tpe), name) if name == ExprTree.name => Some(splicee, symbolTable, tree, tpe) case _ => @@ -628,16 +628,16 @@ abstract class TreeInfo { } object ReifiedType { - def unapply(tree: Tree): Option[(Tree, List[ValDef], Tree)] = tree match { + def unapply(tree: Tree): Option[(Tree, List[Tree], Tree)] = tree match { case reifee @ Block((mrDef @ ValDef(_, _, _, _)) :: symbolTable, Apply(_, List(tpe))) if mrDef.name == nme.MIRROR_SHORT => - Some(reifee, symbolTable map (_.asInstanceOf[ValDef]), tpe) + Some(reifee, symbolTable, tpe) case _ => None } } object InlinedTypeSplice { - def unapply(tree: Tree): Option[(Tree, List[ValDef], Tree)] = tree match { + def unapply(tree: Tree): Option[(Tree, List[Tree], Tree)] = tree match { case Select(ReifiedType(splicee, symbolTable, tpe), name) if name == TypeTagTpe.name => Some(splicee, symbolTable, tpe) case _ => diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala index 17c9b955ca..fc57a130d1 100644 --- a/src/compiler/scala/reflect/internal/Types.scala +++ b/src/compiler/scala/reflect/internal/Types.scala @@ -266,6 +266,35 @@ trait Types extends api.Types { self: SymbolTable => def typeArguments = typeArgs def erasure = transformedType(this) def substituteTypes(from: List[Symbol], to: List[Type]): Type = subst(from, to) + + // [Eugene] to be discussed and refactored + def isConcrete = { + def notConcreteSym(sym: Symbol) = + sym.isAbstractType && !sym.isExistential + + def notConcreteTpe(tpe: Type): Boolean = tpe match { + case ThisType(_) => false + case SuperType(_, _) => false + case SingleType(pre, sym) => notConcreteSym(sym) + case ConstantType(_) => false + case TypeRef(_, sym, _) => notConcreteSym(sym) + case RefinedType(_, _) => false + case ExistentialType(_, _) => false + case AnnotatedType(_, tp, _) => notConcreteTpe(tp) + case _ => true + } + + !notConcreteTpe(this) + } + + // [Eugene] is this comprehensive? + // the only thingies that we want to splice are: 1) type parameters, 2) type members + // the thingies that we don't want to splice are: 1) concrete types (obviously), 2) existential skolems + // this check seems to cover them all, right? + // todo. after we discuss this, move the check to subclasses + def isSpliceable = { + this.isInstanceOf[TypeRef] && typeSymbol.isAbstractType && !typeSymbol.isExistential + } } /** The base class for all types */ @@ -2053,7 +2082,7 @@ trait Types extends api.Types { self: SymbolTable => // TODO: is there another way a typeref's symbol can refer to a symbol defined in its pre? case _ => sym } - + override def kind = "AliasTypeRef" } trait AbstractTypeRef extends NonClassTypeRef { @@ -2106,6 +2135,7 @@ trait Types extends api.Types { self: SymbolTable => override def bounds = thisInfo.bounds // def transformInfo(tp: Type): Type = appliedType(tp.asSeenFrom(pre, sym.owner), typeArgsOrDummies) override protected[Types] def baseTypeSeqImpl: BaseTypeSeq = transform(bounds.hi).baseTypeSeq prepend this + override def kind = "AbstractTypeRef" } /** A class for named types of the form @@ -2147,7 +2177,7 @@ trait Types extends api.Types { self: SymbolTable => sym.isPackageClass || pre.isGround && args.forall(_.isGround) ) - + def etaExpand: Type = { // must initialise symbol, see test/files/pos/ticket0137.scala val tpars = initializedTypeParams @@ -2550,7 +2580,7 @@ trait Types extends api.Types { self: SymbolTable => !(qset contains sym) && !isQuantified(pre) case _ => false - } + } } override def safeToString: String = { @@ -2763,7 +2793,7 @@ trait Types extends api.Types { self: SymbolTable => zippedArgs map { case (p, a) => p.name + "=" + a } mkString (origin + "[", ", ", "]") ) } - + trait UntouchableTypeVar extends TypeVar { override def untouchable = true override def isGround = true diff --git a/src/compiler/scala/reflect/reify/codegen/AnnotationInfos.scala b/src/compiler/scala/reflect/reify/codegen/AnnotationInfos.scala new file mode 100644 index 0000000000..1d218317dc --- /dev/null +++ b/src/compiler/scala/reflect/reify/codegen/AnnotationInfos.scala @@ -0,0 +1,56 @@ +package scala.reflect.reify +package codegen + +trait AnnotationInfos { + self: Reifier => + + import mirror._ + import definitions._ + import treeInfo._ + + // usually annotations are reified as their originals from Modifiers + // however, when reifying free and tough types, we're forced to reify annotation infos as is + // why is that bad? take a look inside + def reifyAnnotationInfo(ann: AnnotationInfo): Tree = { + val reifiedArgs = ann.args map { arg => + val saved1 = reifyTreeSymbols + val saved2 = reifyTreeTypes + + try { + // one more quirk of reifying annotations + // + // when reifying AnnotatedTypes we need to reify all the types and symbols of inner ASTs + // that's because a lot of logic expects post-typer trees to have non-null tpes + // + // Q: reified trees are pre-typer, so there's shouldn't be a problem. + // reflective typechecker will fill in missing symbols and types, right? + // A: actually, no. annotation ASTs live inside AnnotatedTypes, + // and insides of the types is the place where typechecker doesn't look. + reifyTreeSymbols = true + reifyTreeTypes = true + + // todo. every AnnotationInfo is an island, entire of itself + // no regular Traverser or Transformer can reach it + // hence we need to run its contents through the entire reification pipeline + // e.g. to apply reshaping or to check metalevels + reify(arg) + } finally { + reifyTreeSymbols = saved1 + reifyTreeTypes = saved2 + } + } + + def reifyClassfileAnnotArg(arg: ClassfileAnnotArg): Tree = arg match { + case LiteralAnnotArg(const) => + mirrorFactoryCall(nme.LiteralAnnotArg, reifyProduct(const)) + case ArrayAnnotArg(args) => + mirrorFactoryCall(nme.ArrayAnnotArg, scalaFactoryCall(nme.Array, args map reifyClassfileAnnotArg: _*)) + case NestedAnnotArg(ann) => + mirrorFactoryCall(nme.NestedAnnotArg, reifyAnnotationInfo(ann)) + } + + // if you reify originals of anns, you get SO when trying to reify AnnotatedTypes, so screw it - after all, it's not that important + val reifiedAssocs = ann.assocs map (assoc => scalaFactoryCall(nme.Tuple2, reify(assoc._1), reifyClassfileAnnotArg(assoc._2))) + mirrorFactoryCall(nme.AnnotationInfo, reify(ann.atp), mkList(reifiedArgs), mkList(reifiedAssocs)) + } +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/reify/codegen/Symbols.scala b/src/compiler/scala/reflect/reify/codegen/Symbols.scala index 3328f5e402..2fc0002838 100644 --- a/src/compiler/scala/reflect/reify/codegen/Symbols.scala +++ b/src/compiler/scala/reflect/reify/codegen/Symbols.scala @@ -46,13 +46,8 @@ trait Symbols { } } else { // todo. make sure that free methods and free local defs work correctly - if (sym.isTerm) { - if (reifyDebug) println("Free term" + (if (sym.isCapturedVariable) " (captured)" else "") + ": " + sym) - reifyFreeTerm(sym, Ident(sym)) - } else { - if (reifyDebug) println("Free type: " + sym) - reifyFreeType(sym, Ident(sym)) - } + if (sym.isTerm) reifyFreeTerm(sym, Ident(sym)) + else reifyFreeType(sym, Ident(sym)) } } @@ -61,13 +56,16 @@ trait Symbols { case Some(reified) => reified case None => + if (reifyDebug) println("Free term" + (if (sym.isCapturedVariable) " (captured)" else "") + ": " + sym + "(" + sym.accurateKindString + ")") + var name = newTermName(nme.MIRROR_FREE_PREFIX + sym.name) + if (sym.isType) name = name.append(nme.MIRROR_FREE_THIS_SUFFIX) if (sym.isCapturedVariable) { assert(value.isInstanceOf[Ident], showRaw(value)) val capturedTpe = capturedVariableType(sym) val capturedValue = referenceCapturedVariable(sym) - locallyReify(sym, mirrorCall(nme.newFreeTerm, reify(sym.name.toString), reify(capturedTpe), capturedValue, reify(origin(sym)))) + locallyReify(sym, name, mirrorCall(nme.newFreeTerm, reify(sym.name.toString), reify(capturedTpe), capturedValue, reify(origin(sym)))) } else { - locallyReify(sym, mirrorCall(nme.newFreeTerm, reify(sym.name.toString), reify(sym.tpe), value, reify(origin(sym)))) + locallyReify(sym, name, mirrorCall(nme.newFreeTerm, reify(sym.name.toString), reify(sym.tpe), value, reify(origin(sym)))) } } @@ -76,36 +74,104 @@ trait Symbols { case Some(reified) => reified case None => + if (reifyDebug) println("Free type: %s (%s)".format(sym, sym.accurateKindString)) + var name = newTermName(nme.MIRROR_FREE_PREFIX + sym.name) val phantomTypeTag = Apply(TypeApply(Select(Ident(nme.MIRROR_SHORT), nme.TypeTag), List(value)), List(Literal(Constant(null)))) // todo. implement info reification for free types: type bounds, HK-arity, whatever else that can be useful - locallyReify(sym, mirrorCall(nme.newFreeType, reify(sym.name.toString), reify(sym.info), phantomTypeTag, reify(origin(sym)))) + locallyReify(sym, name, mirrorCall(nme.newFreeType, reify(sym.name.toString), reify(sym.info), phantomTypeTag, reify(origin(sym)))) + } + + def reifySymDef(sym: Symbol): Tree = + locallyReified get sym match { + case Some(reified) => + reified + case None => + if (reifyDebug) println("Sym def: %s (%s)".format(sym, sym.accurateKindString)) + assert(!sym.isLocatable, sym) // if this assertion fires, then tough type reification needs to be rethought + sym.owner.ownersIterator find (!_.isLocatable) foreach reifySymDef + var name = newTermName(nme.MIRROR_SYMDEF_PREFIX + sym.name) + locallyReify(sym, name, Apply(Select(reify(sym.owner), nme.newNestedSymbol), List(reify(sym.name), reify(sym.pos), reify(sym.flags), reify(sym.isClass)))) } + // todo. very brittle abstraction, needs encapsulation import scala.collection.mutable._ - private val localReifications = ArrayBuffer[ValDef]() + private val localReifications = ArrayBuffer[Tree]() private val locallyReified = Map[Symbol, Tree]() - def symbolTable: List[ValDef] = localReifications.toList - def symbolTable_=(newSymbolTable: List[ValDef]): Unit = { + private var filledIn = false + def symbolTable: List[Tree] = { fillInSymbolTable(); localReifications.toList } + def symbolTable_=(newSymbolTable: List[Tree]): Unit = { localReifications.clear() locallyReified.clear() + filledIn = false newSymbolTable foreach { - case freedef @ FreeDef(_, name, binding, _) => - if (!(locallyReified contains binding.symbol)) { - localReifications += freedef - locallyReified(binding.symbol) = Ident(name) + case entry => + val att = entry.attachment + att match { + case sym: Symbol => + // don't duplicate reified symbols when merging inlined reifee + if (!(locallyReified contains sym)) { + val ValDef(_, name, _, _) = entry + localReifications += entry + locallyReified(sym) = Ident(name) + } + case other => + // do nothing => symbol table fill-ins will be repopulated later } } } - private def locallyReify(sym: Symbol, reificode: => Tree): Tree = { + private def localName(name0: TermName): TermName = { + var name = name0.toString + name = name.replace(".type", "$type") + name = name.replace(" ", "$") + val fresh = typer.context.unit.fresh + newTermName(fresh.newName(name)) + } + + private def locallyReify(sym: Symbol, name0: TermName, reificode: => Tree): Tree = { val reified = reificode - val Apply(Select(_, flavor), _) = reified - // [Eugene] name clashes are impossible, right? - var name = newTermName(nme.MIRROR_FREE_PREFIX + sym.name) - if (flavor == nme.newFreeTerm && sym.isType) name = name.append(nme.MIRROR_FREE_THIS_SUFFIX); - // todo. also reify annotations for free vars - localReifications += ValDef(NoMods, name, TypeTree(), reified) + val name = localName(name0) + // todo. tried to declare a private class here to carry an attachment, but it's path-dependent + // so got troubles with exchanging free variables between nested and enclosing quasiquotes + // attaching just Symbol isn't good either, so we need to think of a principled solution + val local = ValDef(NoMods, name, TypeTree(), reified) setAttachment sym + localReifications += local + filledIn = false locallyReified(sym) = Ident(name) locallyReified(sym) } + + /** Sets type signatures and annotations for locally reified symbols */ + private def fillInSymbolTable() = { + if (!filledIn) { + val fillIns = new ArrayBuffer[Tree] + var i = 0 + while (i < localReifications.length) { + // fillInSymbol might create new locallyReified symbols, that's why this is done iteratively + val reified = localReifications(i) + reified.attachment match { + case sym: Symbol => fillIns += fillInSymbol(sym) + case other => // do nothing + } + i += 1 + } + + filledIn = true + localReifications ++= fillIns.toList + } + } + + /** Generate code to add type and annotation info to a reified symbol */ + private def fillInSymbol(sym: Symbol): Tree = { + if (reifyDebug) println("Filling in: %s (%s)".format(sym, sym.accurateKindString)) + val isFree = locallyReified(sym) match { case Ident(name) => name startsWith nme.MIRROR_FREE_PREFIX } + if (isFree) { + if (sym.annotations.isEmpty) EmptyTree + else Apply(Select(locallyReified(sym), nme.setAnnotations), List(reify(sym.annotations))) + } else { + val rset = Apply(Select(locallyReified(sym), nme.setTypeSignature), List(reifyType(sym.info))) + if (sym.annotations.isEmpty) rset + else Apply(Select(rset, nme.setAnnotations), List(reify(sym.annotations))) + } + } }
\ No newline at end of file diff --git a/src/compiler/scala/reflect/reify/codegen/Trees.scala b/src/compiler/scala/reflect/reify/codegen/Trees.scala index 22f42aea49..5ad53c0009 100644 --- a/src/compiler/scala/reflect/reify/codegen/Trees.scala +++ b/src/compiler/scala/reflect/reify/codegen/Trees.scala @@ -8,6 +8,11 @@ trait Trees { import definitions._ import treeInfo._ + // unfortunately, these are necessary to reify AnnotatedTypes + // I'd gladly got rid of them, but I don't fancy making a metaprogramming API that doesn't work with annotated types + var reifyTreeSymbols = false + var reifyTreeTypes = false + /** * Reify a tree. * For internal use only, use ``reified'' instead. @@ -59,6 +64,17 @@ trait Trees { reifyProduct(tree) } + // usually we don't reify symbols/types, because they can be re-inferred during subsequent reflective compilation + // however, reification of AnnotatedTypes is special. see ``reifyType'' to find out why. + if (reifyTreeSymbols && tree.hasSymbol) { + if (reifyDebug) println("reifying symbol %s for tree %s".format(tree.symbol, tree)) + rtree = Apply(Select(rtree, nme.setSymbol), List(reifySymRef(tree.symbol))) + } + if (reifyTreeTypes && tree.tpe != null) { + if (reifyDebug) println("reifying type %s for tree %s".format(tree.tpe, tree)) + rtree = Apply(Select(rtree, nme.setType), List(reifyType(tree.tpe))) + } + rtree } @@ -82,7 +98,7 @@ trait Trees { case InlinedTreeSplice(_, inlinedSymbolTable, tree, _) => if (reifyDebug) println("inlining the splicee") // all free vars local to the enclosing reifee should've already been inlined by ``Metalevels'' - inlinedSymbolTable foreach { case freedef @ FreeDef(_, _, binding, _) => assert(!binding.symbol.isLocalToReifee, freedef) } + inlinedSymbolTable collect { case freedef @ FreeDef(_, _, binding, _) if binding.symbol.isLocalToReifee => assert(false, freedef) } symbolTable ++= inlinedSymbolTable tree case tree => @@ -152,7 +168,7 @@ trait Trees { val tpe = tpe0.dealias if (reifyDebug) println("reifying bound type %s (underlying type is %s, dealiased is %s)".format(sym0, tpe0, tpe)) - if (eligibleForSplicing(tpe)) { + if (tpe.isSpliceable) { val spliced = spliceType(tpe) if (spliced == EmptyTree) { if (reifyDebug) println("splicing failed: reify as is") diff --git a/src/compiler/scala/reflect/reify/codegen/Types.scala b/src/compiler/scala/reflect/reify/codegen/Types.scala index 9bc113e8a4..948728088e 100644 --- a/src/compiler/scala/reflect/reify/codegen/Types.scala +++ b/src/compiler/scala/reflect/reify/codegen/Types.scala @@ -55,11 +55,9 @@ trait Types { case tpe @ NullaryMethodType(restpe) => reifyProduct(tpe) case tpe @ AnnotatedType(anns, underlying, selfsym) => -// reifyAnnotatedType(tpe) - CannotReifyType(tpe) + reifyAnnotatedType(tpe) case _ => -// reifyToughType(tpe) - CannotReifyType(tpe) + reifyToughType(tpe) } } @@ -70,13 +68,6 @@ trait Types { var maybeConcrete = true var definitelyConcrete = true - def eligibleForSplicing(tpe: Type): Boolean = { - // [Eugene] is this comprehensive? - // the only thingies that we want to splice are: 1) type parameters, 2) type members - // this check seems to cover them all, right? - tpe.isInstanceOf[TypeRef] && tpe.typeSymbol.isAbstractType - } - private type SpliceCacheKey = (Symbol, Symbol) private lazy val spliceCache: collection.mutable.Map[SpliceCacheKey, Tree] = { val cache = analyzer.perRunMacroCache.getOrElseUpdate(MacroContextReify, collection.mutable.Map[Any, Any]()) @@ -84,7 +75,7 @@ trait Types { } def spliceType(tpe: Type): Tree = { - if (eligibleForSplicing(tpe)) { + if (tpe.isSpliceable) { if (reifyDebug) println("splicing " + tpe) if (spliceTypesEnabled) { @@ -113,7 +104,7 @@ trait Types { splice match { case InlinedTypeSplice(_, inlinedSymbolTable, tpe) => // all free vars local to the enclosing reifee should've already been inlined by ``Metalevels'' - inlinedSymbolTable foreach { case freedef @ FreeDef(_, _, binding, _) => assert(!binding.symbol.isLocalToReifee, freedef) } + inlinedSymbolTable collect { case freedef @ FreeDef(_, _, binding, _) if binding.symbol.isLocalToReifee => assert(false, freedef) } symbolTable ++= inlinedSymbolTable reifyTrace("inlined the splicee: ")(tpe) case tpe => @@ -134,93 +125,39 @@ trait Types { EmptyTree } - // yet another thingie disabled for simplicity - // in principle, we could retain and reify AnnotatedTypes - // but that'd require reifying every type and symbol inside ann.args - // however, since we've given up on tough types for the moment, the former would be problematic -// private def reifyAnnotatedType(tpe: AnnotatedType): Tree = { -// // ``Reshaper'' transforms annotation infos from symbols back into Modifier.annotations, which are trees -// // so the only place on Earth that can lead to reification of AnnotationInfos is the Ay Tee Land -// // therefore this function is as local as possible, don't move it out of this scope -// def reifyAnnotationInfo(ann: AnnotationInfo): Tree = { -// val reifiedArgs = ann.args map { arg => -// val saved1 = reifyTreeSymbols -// val saved2 = reifyTreeTypes -// -// try { -// // one more quirk of reifying annotations -// // -// // when reifying AnnotatedTypes we need to reify all the types and symbols of inner ASTs -// // that's because a lot of logic expects post-typer trees to have non-null tpes -// // -// // Q: reified trees are pre-typer, so there's shouldn't be a problem. -// // reflective typechecker will fill in missing symbols and types, right? -// // A: actually, no. annotation ASTs live inside AnnotatedTypes, -// // and insides of the types is the place where typechecker doesn't look. -// reifyTreeSymbols = true -// reifyTreeTypes = true -// -// // todo. every AnnotationInfo is an island, entire of itself -// // no regular Traverser or Transformer can reach it -// // hence we need to run its contents through the entire reification pipeline -// // e.g. to apply reshaping or to check metalevels -// reify(arg) -// } finally { -// reifyTreeSymbols = saved1 -// reifyTreeTypes = saved2 -// } -// } -// -// def reifyClassfileAnnotArg(arg: ClassfileAnnotArg): Tree = arg match { -// case LiteralAnnotArg(const) => -// mirrorFactoryCall(nme.LiteralAnnotArg, reifyProduct(const)) -// case ArrayAnnotArg(args) => -// mirrorFactoryCall(nme.ArrayAnnotArg, scalaFactoryCall(nme.Array, args map reifyClassfileAnnotArg: _*)) -// case NestedAnnotArg(ann) => -// mirrorFactoryCall(nme.NestedAnnotArg, reifyAnnotationInfo(ann)) -// } -// -// // if you reify originals of anns, you get SO when trying to reify AnnotatedTypes, so screw it - after all, it's not that important -// val reifiedAssocs = ann.assocs map (assoc => scalaFactoryCall(nme.Tuple2, reify(assoc._1), reifyClassfileAnnotArg(assoc._2))) -// mirrorFactoryCall(nme.AnnotationInfo, reify(ann.atp), mkList(reifiedArgs), mkList(reifiedAssocs)) -// } -// -// val AnnotatedType(anns, underlying, selfsym) = tpe -// mirrorFactoryCall(nme.AnnotatedType, mkList(anns map reifyAnnotationInfo), reify(underlying), reify(selfsym)) -// } - - // previous solution to reifying tough types involved creating dummy symbols (see ``registerReifiableSymbol'' calls below) - // however such symbols lost all the connections with their origins and became almost useless, except for typechecking - // hence this approach was replaced by less powerful, but more principled one based on ``reifyFreeType'' - // it's possible that later on we will revise and revive ``reifyToughType'', but for now it's disabled under an implementation restriction -// /** Reify a tough type, i.e. the one that leads to creation of auxiliary symbols */ -// // This is the uncharted territory in the reifier -// private def reifyToughType(tpe: Type): Tree = { -// if (reifyDebug) println("tough type: %s (%s)".format(tpe, tpe.kind)) -// -// def reifyScope(scope: Scope): Tree = { -// scope foreach registerReifiableSymbol -// mirrorCall(nme.newScopeWith, scope.toList map reify: _*) -// } -// -// tpe match { -// case tpe @ RefinedType(parents, decls) => -// registerReifiableSymbol(tpe.typeSymbol) -// mirrorFactoryCall(tpe, reify(parents), reifyScope(decls), reify(tpe.typeSymbol)) -// case tpe @ ExistentialType(tparams, underlying) => -// tparams foreach registerReifiableSymbol -// mirrorFactoryCall(tpe, reify(tparams), reify(underlying)) -// case tpe @ ClassInfoType(parents, decls, clazz) => -// registerReifiableSymbol(clazz) -// mirrorFactoryCall(tpe, reify(parents), reifyScope(decls), reify(tpe.typeSymbol)) -// case tpe @ MethodType(params, restpe) => -// params foreach registerReifiableSymbol -// mirrorFactoryCall(tpe, reify(params), reify(restpe)) -// case tpe @ PolyType(tparams, underlying) => -// tparams foreach registerReifiableSymbol -// mirrorFactoryCall(tpe, reify(tparams), reify(underlying)) -// case _ => -// throw new Error("internal error: %s (%s) is not supported".format(tpe, tpe.kind)) -// } -// } + /** Reify an annotated type, i.e. the one that makes us deal with AnnotationInfos */ + private def reifyAnnotatedType(tpe: AnnotatedType): Tree = { + val AnnotatedType(anns, underlying, selfsym) = tpe + mirrorFactoryCall(nme.AnnotatedType, mkList(anns map reifyAnnotationInfo), reify(underlying), reify(selfsym)) + } + + /** Reify a tough type, i.e. the one that leads to creation of auxiliary symbols */ + private def reifyToughType(tpe: Type): Tree = { + if (reifyDebug) println("tough type: %s (%s)".format(tpe, tpe.kind)) + + def reifyScope(scope: Scope): Tree = { + scope foreach reifySymDef + mirrorCall(nme.newScopeWith, scope.toList map reify: _*) + } + + tpe match { + case tpe @ RefinedType(parents, decls) => + reifySymDef(tpe.typeSymbol) + mirrorFactoryCall(tpe, reify(parents), reifyScope(decls), reify(tpe.typeSymbol)) + case tpe @ ExistentialType(tparams, underlying) => + tparams foreach reifySymDef + mirrorFactoryCall(tpe, reify(tparams), reify(underlying)) + case tpe @ ClassInfoType(parents, decls, clazz) => + reifySymDef(clazz) + mirrorFactoryCall(tpe, reify(parents), reifyScope(decls), reify(tpe.typeSymbol)) + case tpe @ MethodType(params, restpe) => + params foreach reifySymDef + mirrorFactoryCall(tpe, reify(params), reify(restpe)) + case tpe @ PolyType(tparams, underlying) => + tparams foreach reifySymDef + mirrorFactoryCall(tpe, reify(tparams), reify(underlying)) + case _ => + throw new Error("internal error: %s (%s) is not supported".format(tpe, tpe.kind)) + } + } }
\ No newline at end of file diff --git a/src/compiler/scala/reflect/reify/phases/Metalevels.scala b/src/compiler/scala/reflect/reify/phases/Metalevels.scala index a329a1043d..bb0b8ac138 100644 --- a/src/compiler/scala/reflect/reify/phases/Metalevels.scala +++ b/src/compiler/scala/reflect/reify/phases/Metalevels.scala @@ -134,6 +134,8 @@ trait Metalevels { // FreeRef(_, _) check won't work, because metalevels of symbol table and body are different, hence, freerefs in symbol table look different from freerefs in body // todo. also perform garbage collection on local symbols // so that local symbols used only in type signatures of free vars get removed + // todo. same goes for auxiliary symbol defs reified to support tough types + // some of them need to be rebuilt, some of them need to be removed, because they're no longer necessary case FreeRef(mr, name) if freedefsToInline contains name => if (reifyDebug) println("inlineable free ref: %s in %s".format(name, showRaw(tree))) val freedef @ FreeDef(_, _, binding, _) = freedefsToInline(name) diff --git a/src/compiler/scala/reflect/reify/phases/Reify.scala b/src/compiler/scala/reflect/reify/phases/Reify.scala index f6d6423605..02a96987ed 100644 --- a/src/compiler/scala/reflect/reify/phases/Reify.scala +++ b/src/compiler/scala/reflect/reify/phases/Reify.scala @@ -9,6 +9,7 @@ trait Reify extends Symbols with Types with Names with Trees + with AnnotationInfos with Positions with Util { @@ -30,6 +31,9 @@ trait Reify extends Symbols case tpe: Type => reifyType(tpe) case name: Name => reifyName(name) case tree: Tree => reifyTree(tree) + // disabled because this is a very special case that I plan to remove later + // why do I dislike annotations? see comments to `reifyAnnotationInfo` +// case ann: AnnotationInfo => reifyAnnotationInfo(ann) case pos: Position => reifyPosition(pos) case mods: mirror.Modifiers => reifyModifiers(mods) case xs: List[_] => reifyList(xs) diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala index 369c59cd43..767df0791e 100644 --- a/src/compiler/scala/tools/nsc/CompilationUnits.scala +++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala @@ -5,7 +5,7 @@ package scala.tools.nsc -import util.{ FreshNameCreator, Position, NoPosition, SourceFile, NoSourceFile } +import util.{ FreshNameCreator, Position, NoPosition, BatchSourceFile, SourceFile, NoSourceFile } import scala.collection.mutable import scala.collection.mutable.{ LinkedHashSet, ListBuffer } diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 7cfccf3baf..bbeee3220b 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -950,6 +950,11 @@ class Global(var currentSettings: Settings, var reporter: NscReporter) extends S reporter.warning(NoPosition, "there were %d %s warnings; re-run with %s for details".format(warnings.size, what, option.name)) } + def newUnitParser(code: String) = new syntaxAnalyzer.UnitParser(newCompilationUnit(code)) + def newUnitScanner(code: String) = new syntaxAnalyzer.UnitScanner(newCompilationUnit(code)) + def newCompilationUnit(code: String) = new CompilationUnit(newSourceFile(code)) + def newSourceFile(code: String) = new BatchSourceFile("<console>", code) + /** A Run is a single execution of the compiler on a sets of units */ class Run { @@ -1276,7 +1281,7 @@ class Global(var currentSettings: Settings, var reporter: NscReporter) extends S // nothing to compile, but we should still report use of deprecated options if (sources.isEmpty) { - checkDeprecatedSettings(new CompilationUnit(new BatchSourceFile("<no file>", ""))) + checkDeprecatedSettings(newCompilationUnit("")) reportCompileErrors() return } diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index eb4deeeee2..bca1cc4596 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -317,7 +317,7 @@ self => * by compilationUnit(). */ def scriptBody(): Tree = { - val stmts = templateStatSeq(false)._2 + val stmts = templateStats() accept(EOF) def mainModuleName = newTermName(settings.script.value) @@ -2869,6 +2869,13 @@ self => stats.toList } + /** Informal - for the repl and other direct parser accessors. + */ + def templateStats(): List[Tree] = templateStatSeq(false)._2 match { + case Nil => List(EmptyTree) + case stats => stats + } + /** {{{ * TemplateStatSeq ::= [id [`:' Type] `=>'] TemplateStat {semi TemplateStat} * TemplateStat ::= Import diff --git a/src/compiler/scala/tools/nsc/interpreter/CodeHandlers.scala b/src/compiler/scala/tools/nsc/interpreter/CodeHandlers.scala index 42a47896a2..453b106808 100644 --- a/src/compiler/scala/tools/nsc/interpreter/CodeHandlers.scala +++ b/src/compiler/scala/tools/nsc/interpreter/CodeHandlers.scala @@ -17,9 +17,7 @@ import scala.util.control.ControlThrowable trait CodeHandlers[T] { self => - // Expressions are composed of operators and operands. - def expr(code: String): T - +/* // A declaration introduces names and assigns them types. // It can form part of a class definition (§5.1) or of a refinement in a compound type (§3.2.7). // (Ed: aka abstract members.) @@ -34,6 +32,10 @@ trait CodeHandlers[T] { // ‘val’ PatDef | ‘var’ VarDef | ‘def’ FunDef | ‘type’ {nl} TypeDef | // [‘case’] ‘class’ ClassDef | [‘case’] ‘object’ ObjectDef | ‘trait’ TraitDef def defn(code: String): T +*/ + + // Expressions are composed of operators and operands. + def expr(code: String): T // An import clause has the form import p.I where p is a stable identifier (§3.1) and I is an import expression. def impt(code: String): T @@ -52,9 +54,9 @@ trait CodeHandlers[T] { case _: NoSuccess => Nil } + // def decl(code: String) = try Some(self.decl(code)) catch handler + // def defn(code: String) = try Some(self.defn(code)) catch handler def expr(code: String) = try Some(self.expr(code)) catch handler - def decl(code: String) = try Some(self.decl(code)) catch handler - def defn(code: String) = try Some(self.defn(code)) catch handler def impt(code: String) = try Some(self.impt(code)) catch handler def stmt(code: String) = try Some(self.stmt(code)) catch handler def stmts(code: String) = try (self.stmts(code) map (x => Some(x))) catch handlerSeq diff --git a/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala b/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala index a2ce8439de..0cd918b6a5 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala @@ -22,8 +22,7 @@ trait ExprTyper { object codeParser extends { val global: repl.global.type = repl.global } with CodeHandlers[Tree] { def applyRule[T](code: String, rule: UnitParser => T): T = { reporter.reset() - val unit = new CompilationUnit(new BatchSourceFile("<console>", code)) - val scanner = new UnitParser(unit) + val scanner = newUnitParser(code) val result = rule(scanner) if (!reporter.hasErrors) @@ -33,23 +32,17 @@ trait ExprTyper { } def tokens(code: String) = { reporter.reset() - val unit = new CompilationUnit(new BatchSourceFile("<tokens>", code)) - val in = new UnitScanner(unit) + val in = newUnitScanner(code) in.init() - new Tokenizer(in) tokenIterator } - def decl(code: String) = CodeHandlers.fail("todo") - def defn(code: String) = CodeHandlers.fail("todo") + def defns(code: String) = stmts(code) collect { case x: DefTree => x } def expr(code: String) = applyRule(code, _.expr()) def impt(code: String) = applyRule(code, _.importExpr()) def impts(code: String) = applyRule(code, _.importClause()) - def stmts(code: String) = applyRule(code, _.templateStatSeq(false)._2) - def stmt(code: String) = stmts(code) match { - case List(t) => t - case xs => CodeHandlers.fail("Not a single statement: " + xs.mkString(", ")) - } + def stmts(code: String) = applyRule(code, _.templateStats()) + def stmt(code: String) = stmts(code).last // guaranteed nonempty } /** Parse a line into a sequence of trees. Returns None if the input is incomplete. */ @@ -62,77 +55,69 @@ trait ExprTyper { else Some(trees) } } - def tokens(line: String) = beQuietDuring(codeParser.tokens(line)) + // def parsesAsExpr(line: String) = { + // import codeParser._ + // (opt expr line).isDefined + // } - // TODO: integrate these into a CodeHandler[Type]. + def symbolOfLine(code: String): Symbol = { + def asExpr(): Symbol = { + val name = freshInternalVarName() + // Typing it with a lazy val would give us the right type, but runs + // into compiler bugs with things like existentials, so we compile it + // behind a def and strip the NullaryMethodType which wraps the expr. + val line = "def " + name + " = {\n" + code + "\n}" + + interpretSynthetic(line) match { + case IR.Success => + val sym0 = symbolOfTerm(name) + // drop NullaryMethodType + val sym = sym0.cloneSymbol setInfo afterTyper(sym0.info.finalResultType) + if (sym.info.typeSymbol eq UnitClass) NoSymbol + else sym + case _ => NoSymbol + } + } + def asDefn(): Symbol = { + val old = repl.definedSymbolList.toSet + + interpretSynthetic(code) match { + case IR.Success => + repl.definedSymbolList filterNot old match { + case Nil => NoSymbol + case sym :: Nil => sym + case syms => NoSymbol.newOverloaded(NoPrefix, syms) + } + case _ => NoSymbol + } + } + beQuietDuring(asExpr()) orElse beQuietDuring(asDefn()) + } - // XXX literals. - // 1) Identifiers defined in the repl. - // 2) A path loadable via getModule. - // 3) Try interpreting it as an expression. private var typeOfExpressionDepth = 0 def typeOfExpression(expr: String, silent: Boolean = true): Type = { - repltrace("typeOfExpression(" + expr + ")") if (typeOfExpressionDepth > 2) { repldbg("Terminating typeOfExpression recursion for expression: " + expr) return NoType } - - def asQualifiedImport: Type = { - val name = expr.takeWhile(_ != '.') - typeOfExpression(importedTermNamed(name).fullName + expr.drop(name.length), true) - } - def asModule: Type = getModuleIfDefined(expr).tpe - def asExpr: Type = { - val lhs = freshInternalVarName() - val line = "lazy val " + lhs + " =\n" + expr - - interpret(line, true) match { - case IR.Success => typeOfExpression(lhs, true) - case _ => NoType - } - } - - def evaluate(): Type = { - typeOfExpressionDepth += 1 - try typeOfTerm(expr) orElse asModule orElse asExpr orElse asQualifiedImport - finally typeOfExpressionDepth -= 1 - } - + typeOfExpressionDepth += 1 // Don't presently have a good way to suppress undesirable success output // while letting errors through, so it is first trying it silently: if there // is an error, and errors are desired, then it re-evaluates non-silently // to induce the error message. - beSilentDuring(evaluate()) orElse beSilentDuring(typeOfDeclaration(expr)) orElse { - if (!silent) - evaluate() - - NoType + try beSilentDuring(symbolOfLine(expr).tpe) match { + case NoType if !silent => symbolOfLine(expr).tpe // generate error + case tpe => tpe } + finally typeOfExpressionDepth -= 1 } - // Since people will be giving us ":t def foo = 5" even though that is not an - // expression, we have a means of typing declarations too. - private def typeOfDeclaration(code: String): Type = { - repltrace("typeOfDeclaration(" + code + ")") - val obname = freshInternalVarName() - interpret("object " + obname + " {\n" + code + "\n}\n", true) match { - case IR.Success => - val sym = symbolOfTerm(obname) - if (sym == NoSymbol) NoType else { - // TODO: bitmap$n is not marked synthetic. - val decls = sym.tpe.decls.toList filterNot (x => x.isConstructor || x.isPrivate || (x.name.toString contains "$")) - repltrace("decls: " + decls) - if (decls.isEmpty) NoType - else cleanMemberDecl(sym, decls.last.name) - } - case _ => - NoType - } - } + def tokens(line: String) = beQuietDuring(codeParser.tokens(line)) + + // In the todo column + // // def compileAndTypeExpr(expr: String): Option[Typer] = { // class TyperRun extends Run { // override def stopPhase(name: String) = name == "superaccessors" // } - // } } diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala index bf386d8a12..9279d37464 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala @@ -49,12 +49,60 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) var settings: Settings = _ var intp: IMain = _ - override def echoCommandMessage(msg: String): Unit = - intp.reporter.printMessage(msg) + /** Having inherited the difficult "var-ness" of the repl instance, + * I'm trying to work around it by moving operations into a class from + * which it will appear a stable prefix. + */ + private def onIntp[T](f: IMain => T): T = f(intp) + + class IMainOps[T <: IMain](val intp: T) { + import intp._ + import global._ + + def printAfterTyper(msg: => String) = + intp.reporter printUntruncatedMessage afterTyper(msg) + + /** Strip NullaryMethodType artifacts. */ + private def replInfo(sym: Symbol) = { + sym.info match { + case NullaryMethodType(restpe) if sym.isAccessor => restpe + case info => info + } + } + def echoTypeStructure(sym: Symbol) = + printAfterTyper("" + deconstruct.show(replInfo(sym))) + + def echoTypeSignature(sym: Symbol, verbose: Boolean) = { + if (verbose) ILoop.this.echo("// Type signature") + printAfterTyper("" + replInfo(sym)) + + if (verbose) { + ILoop.this.echo("\n// Internal Type structure") + echoTypeStructure(sym) + } + } + } + implicit def stabilizeIMain(intp: IMain) = new IMainOps[intp.type](intp) + + /** TODO - + * -n normalize + * -l label with case class parameter names + * -c complete - leave nothing out + */ + private def typeCommandInternal(expr: String, verbose: Boolean): Result = { + onIntp { intp => + val sym = intp.symbolOfLine(expr) + if (sym.exists) intp.echoTypeSignature(sym, verbose) + else "" + } + } + + override def echoCommandMessage(msg: String) { + intp.reporter printUntruncatedMessage msg + } def isAsync = !settings.Yreplsync.value lazy val power = new Power(intp, new StdReplVals(this)) - lazy val NoType = intp.global.NoType // TODO // object opt extends AestheticSettings @@ -249,7 +297,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) // nullary("reset", "reset the interpreter, forgetting session values but retaining session types", replay), shCommand, nullary("silent", "disable/enable automatic printing of results", verbosity), - cmd("type", "<expr>", "display the type of an expression without evaluating it", typeCommand), + cmd("type", "[-v] <expr>", "display the type of an expression without evaluating it", typeCommand), nullary("warnings", "show the suppressed warnings from the most recent line which had any", warningsCommand) ) @@ -322,10 +370,9 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) } } - private def implicitsCommand(line: String): Result = { - val intp = ILoop.this.intp + private def implicitsCommand(line: String): Result = onIntp { intp => import intp._ - import global.{ Symbol, afterTyper } + import global._ def p(x: Any) = intp.reporter.printMessage("" + x) @@ -436,14 +483,14 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) private lazy val javap = substituteAndLog[Javap]("javap", NoJavap)(newJavap()) // Still todo: modules. - private def typeCommand(line: String): Result = { - if (line.trim == "") ":type <expression>" - else { - val tp = intp.typeOfExpression(line, false) - if (tp == NoType) "" // the error message was already printed - else intp.global.afterTyper(tp.toString) + private def typeCommand(line0: String): Result = { + line0.trim match { + case "" => ":type [-v] <expression>" + case s if s startsWith "-v " => typeCommandInternal(s stripPrefix "-v " trim, true) + case s => typeCommandInternal(s, false) } } + private def warningsCommand(): Result = { intp.lastWarnings foreach { case (pos, msg) => intp.reporter.warning(pos, msg) } } @@ -471,33 +518,34 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) } private def wrapCommand(line: String): Result = { def failMsg = "Argument to :wrap must be the name of a method with signature [T](=> T): T" - val intp = ILoop.this.intp - val g: intp.global.type = intp.global - import g._ - - words(line) match { - case Nil => - intp.executionWrapper match { - case "" => "No execution wrapper is set." - case s => "Current execution wrapper: " + s - } - case "clear" :: Nil => - intp.executionWrapper match { - case "" => "No execution wrapper is set." - case s => intp.clearExecutionWrapper() ; "Cleared execution wrapper." - } - case wrapper :: Nil => - intp.typeOfExpression(wrapper) match { - case PolyType(List(targ), MethodType(List(arg), restpe)) => - intp setExecutionWrapper intp.pathToTerm(wrapper) - "Set wrapper to '" + wrapper + "'" - case tp => - failMsg + ( - if (tp == g.NoType) "\nFound: <unknown>" - else "\nFound: <unknown>" - ) - } - case _ => failMsg + onIntp { intp => + import intp._ + import global._ + + words(line) match { + case Nil => + intp.executionWrapper match { + case "" => "No execution wrapper is set." + case s => "Current execution wrapper: " + s + } + case "clear" :: Nil => + intp.executionWrapper match { + case "" => "No execution wrapper is set." + case s => intp.clearExecutionWrapper() ; "Cleared execution wrapper." + } + case wrapper :: Nil => + intp.typeOfExpression(wrapper) match { + case PolyType(List(targ), MethodType(List(arg), restpe)) => + intp setExecutionWrapper intp.pathToTerm(wrapper) + "Set wrapper to '" + wrapper + "'" + case tp => + failMsg + ( + if (tp == NoType) "\nFound: <unknown>" + else "\nFound: <unknown>" + ) + } + case _ => failMsg + } } } @@ -798,6 +846,10 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) }) } + def runCompletion = + try in.completion execute code map (intp bindSyntheticValue _) + catch { case ex: Exception => None } + /** Here we place ourselves between the user and the interpreter and examine * the input they are ostensibly submitting. We intervene in several cases: * @@ -815,30 +867,28 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) else if (Completion.looksLikeInvocation(code) && intp.mostRecentVar != "") { interpretStartingWith(intp.mostRecentVar + code) } - else { - def runCompletion = - try in.completion execute code map (intp bindValue _) - catch { case ex: Exception => None } - - /** Due to my accidentally letting file completion execution sneak ahead - * of actual parsing this now operates in such a way that the scala - * interpretation always wins. However to avoid losing useful file - * completion I let it fail and then check the others. So if you - * type /tmp it will echo a failure and then give you a Directory object. - * It's not pretty: maybe I'll implement the silence bits I need to avoid - * echoing the failure. - */ - if (intp isParseable code) { - val (code, result) = reallyInterpret - if (power != null && code == IR.Error) - runCompletion - - result - } - else runCompletion match { - case Some(_) => None // completion hit: avoid the latent error - case _ => reallyInterpret._2 // trigger the latent error - } + else if (code.trim startsWith "//") { + // line comment, do nothing + None + } + /** Due to my accidentally letting file completion execution sneak ahead + * of actual parsing this now operates in such a way that the scala + * interpretation always wins. However to avoid losing useful file + * completion I let it fail and then check the others. So if you + * type /tmp it will echo a failure and then give you a Directory object. + * It's not pretty: maybe I'll implement the silence bits I need to avoid + * echoing the failure. + */ + else if (intp isParseable code) { + val (code, result) = reallyInterpret + if (power != null && code == IR.Error) + runCompletion + + result + } + else runCompletion match { + case Some(_) => None // completion hit: avoid the latent error + case _ => reallyInterpret._2 // trigger the latent error } } diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala index 43e01bebfd..13124e6afc 100644 --- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala +++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala @@ -24,6 +24,7 @@ import scala.collection.{ mutable, immutable } import scala.util.control.Exception.{ ultimately } import IMain._ import java.util.concurrent.Future +import typechecker.Analyzer import language.implicitConversions /** directory to save .class files to */ @@ -140,7 +141,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends lazy val formatting: Formatting = new Formatting { val prompt = Properties.shellPromptString } - lazy val reporter: ConsoleReporter = new ReplReporter(this) + lazy val reporter: ReplReporter = new ReplReporter(this) import formatting._ import reporter.{ printMessage, withoutTruncating } @@ -156,6 +157,8 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends } catch AbstractOrMissingHandler() } + private def tquoted(s: String) = "\"\"\"" + s + "\"\"\"" + // argument is a thunk to execute after init is done def initialize(postInitSignal: => Unit) { synchronized { @@ -226,6 +229,10 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends } import naming._ + object deconstruct extends { + val global: imain.global.type = imain.global + } with StructuredTypeStrings + // object dossiers extends { // val intp: imain.type = imain // } with Dossiers { } @@ -275,11 +282,10 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends protected def createLineManager(classLoader: ClassLoader): Line.Manager = new Line.Manager(classLoader) /** Instantiate a compiler. Overridable. */ - protected def newCompiler(settings: Settings, reporter: NscReporter) = { + protected def newCompiler(settings: Settings, reporter: NscReporter): ReplGlobal = { settings.outputDirs setSingleOutput virtualDirectory settings.exposeEmptyPackage.value = true - - Global(settings, reporter) + new Global(settings, reporter) with ReplGlobal } /** Parent classloader. Overridable. */ @@ -521,7 +527,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends trees.last match { case _:Assign => // we don't want to include assignments case _:TermTree | _:Ident | _:Select => // ... but do want other unnamed terms. - val varName = if (synthetic) freshInternalVarName() else ("" + freshUserTermName()) + val varName = if (synthetic) freshInternalVarName() else freshUserVarName() val rewrittenLine = ( // In theory this would come out the same without the 1-specific test, but // it's a cushion against any more sneaky parse-tree position vs. code mismatches: @@ -587,6 +593,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends * e.g. that there were no parse errors. */ def interpret(line: String): IR.Result = interpret(line, false) + def interpretSynthetic(line: String): IR.Result = interpret(line, true) def interpret(line: String, synthetic: Boolean): IR.Result = { def loadAndRunReq(req: Request) = { val (result, succeeded) = req.loadAndRun @@ -679,7 +686,8 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends def quietBind(p: NamedParam): IR.Result = beQuietDuring(bind(p)) def bind(p: NamedParam): IR.Result = bind(p.name, p.tpe, p.value) def bind[T: Manifest](name: String, value: T): IR.Result = bind((name, value)) - def bindValue(x: Any): IR.Result = bindValue("" + freshUserTermName(), x) + def bindSyntheticValue(x: Any): IR.Result = bindValue(freshInternalVarName(), x) + def bindValue(x: Any): IR.Result = bindValue(freshUserVarName(), x) def bindValue(name: String, x: Any): IR.Result = bind(name, TypeStrings.fromValue(x), x) /** Reset this interpreter, forgetting all user-specified requests. */ @@ -852,6 +860,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends /** handlers for each tree in this request */ val handlers: List[MemberHandler] = trees map (memberHandlers chooseHandler _) + def defHandlers = handlers collect { case x: MemberDefHandler => x } /** all (public) names defined by these statements */ val definedNames = handlers flatMap (_.definedNames) @@ -863,6 +872,10 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends def termNames = handlers flatMap (_.definesTerm) def typeNames = handlers flatMap (_.definesType) def definedOrImported = handlers flatMap (_.definedOrImported) + def definedSymbolList = defHandlers flatMap (_.definedSymbols) + + def definedTypeSymbol(name: String) = definedSymbols(newTypeName(name)) + def definedTermSymbol(name: String) = definedSymbols(newTermName(name)) /** Code to import bound names from previous lines - accessPath is code to * append to objectName to access anything bound by request. @@ -915,8 +928,6 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends val generate = (m: MemberHandler) => m extraCodeToEvaluate Request.this } - def tquoted(s: String) = "\"\"\"" + s + "\"\"\"" - private object ResultObjectSourceCode extends CodeAssembler[MemberHandler] { /** We only want to generate this code when the result * is a value which can be referred to as-is. @@ -970,6 +981,16 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends typeOf typesOfDefinedTerms + // Assign symbols to the original trees + // TODO - just use the new trees. + defHandlers foreach { dh => + val name = dh.member.name + definedSymbols get name foreach { sym => + dh.member setSymbol sym + repldbg("Set symbol of " + name + " to " + sym.defString) + } + } + // compile the result-extraction object beQuietDuring { savingSettings(_.nowarn.value = true) { @@ -991,17 +1012,17 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends } /** Types of variables defined by this request. */ - lazy val compilerTypeOf = typeMap[Type](x => x) + lazy val compilerTypeOf = typeMap[Type](x => x) withDefaultValue NoType /** String representations of same. */ lazy val typeOf = typeMap[String](tp => afterTyper(tp.toString)) // lazy val definedTypes: Map[Name, Type] = { // typeNames map (x => x -> afterTyper(resultSymbol.info.nonPrivateDecl(x).tpe)) toMap // } - lazy val definedSymbols: Map[Name, Symbol] = ( + lazy val definedSymbols = ( termNames.map(x => x -> applyToResultMember(x, x => x)) ++ - typeNames.map(x => x -> compilerTypeOf.get(x).map(_.typeSymbol).getOrElse(NoSymbol)) - ).toMap + typeNames.map(x => x -> compilerTypeOf(x).typeSymbol) + ).toMap[Name, Symbol] withDefaultValue NoSymbol lazy val typesOfDefinedTerms: Map[Name, Type] = termNames map (x => x -> applyToResultMember(x, _.tpe)) toMap @@ -1075,18 +1096,21 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends } def valueOfTerm(id: String): Option[AnyRef] = - requestForIdent(id) flatMap (_.getEval) + requestForName(newTermName(id)) flatMap (_.getEval) def classOfTerm(id: String): Option[JClass] = valueOfTerm(id) map (_.getClass) def typeOfTerm(id: String): Type = newTermName(id) match { case nme.ROOTPKG => definitions.RootClass.tpe - case name => requestForName(name) flatMap (_.compilerTypeOf get name) getOrElse NoType + case name => requestForName(name).fold(NoType: Type)(_ compilerTypeOf name) } + def symbolOfType(id: String): Symbol = + requestForName(newTypeName(id)).fold(NoSymbol: Symbol)(_ definedTypeSymbol id) + def symbolOfTerm(id: String): Symbol = - requestForIdent(id) flatMap (_.definedSymbols get newTermName(id)) getOrElse NoSymbol + requestForIdent(newTermName(id)).fold(NoSymbol: Symbol)(_ definedTermSymbol id) def runtimeClassAndTypeOfTerm(id: String): Option[(JClass, Type)] = { classOfTerm(id) flatMap { clazz => @@ -1120,11 +1144,15 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends val global: imain.global.type = imain.global } with ReplTokens { } - private object exprTyper extends { + object exprTyper extends { val repl: IMain.this.type = imain } with ExprTyper { } def parse(line: String): Option[List[Tree]] = exprTyper.parse(line) + + def symbolOfLine(code: String): Symbol = + exprTyper.symbolOfLine(code) + def typeOfExpression(expr: String, silent: Boolean = true): Type = exprTyper.typeOfExpression(expr, silent) @@ -1134,14 +1162,15 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends protected def onlyTerms(xs: List[Name]) = xs collect { case x: TermName => x } protected def onlyTypes(xs: List[Name]) = xs collect { case x: TypeName => x } - def definedTerms = onlyTerms(allDefinedNames) filterNot isInternalTermName - def definedTypes = onlyTypes(allDefinedNames) - def definedSymbols = prevRequests.toSet flatMap ((x: Request) => x.definedSymbols.values) + def definedTerms = onlyTerms(allDefinedNames) filterNot isInternalTermName + def definedTypes = onlyTypes(allDefinedNames) + def definedSymbols = prevRequestList.flatMap(_.definedSymbols.values).toSet[Symbol] + def definedSymbolList = prevRequestList flatMap (_.definedSymbolList) filterNot (s => isInternalTermName(s.name)) // Terms with user-given names (i.e. not res0 and not synthetic) def namedDefinedTerms = definedTerms filterNot (x => isUserVarName("" + x) || directlyBoundNames(x)) - private def findName(name: Name) = definedSymbols find (_.name == name) + private def findName(name: Name) = definedSymbols find (_.name == name) getOrElse NoSymbol /** Translate a repl-defined identifier into a Symbol. */ @@ -1150,16 +1179,19 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends def types(name: String): Symbol = { val tpname = newTypeName(name) - findName(tpname) getOrElse getClassIfDefined(tpname) + findName(tpname) orElse getClassIfDefined(tpname) } def terms(name: String): Symbol = { val termname = newTypeName(name) - findName(termname) getOrElse getModuleIfDefined(termname) + findName(termname) orElse getModuleIfDefined(termname) } def types[T: ClassManifest] : Symbol = types(classManifest[T].erasure.getName) def terms[T: ClassManifest] : Symbol = terms(classManifest[T].erasure.getName) def apply[T: ClassManifest] : Symbol = apply(classManifest[T].erasure.getName) + def classSymbols = allDefSymbols collect { case x: ClassSymbol => x } + def methodSymbols = allDefSymbols collect { case x: MethodSymbol => x } + /** the previous requests this interpreter has processed */ private var executingRequest: Request = _ private val prevRequests = mutable.ListBuffer[Request]() @@ -1167,7 +1199,10 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends private val definedNameMap = mutable.Map[Name, Request]() private val directlyBoundNames = mutable.Set[Name]() - private def allHandlers = prevRequestList flatMap (_.handlers) + def allHandlers = prevRequestList flatMap (_.handlers) + def allDefHandlers = allHandlers collect { case x: MemberDefHandler => x } + def allDefSymbols = allDefHandlers map (_.symbol) filter (_ ne NoSymbol) + def lastRequest = if (prevRequests.isEmpty) null else prevRequests.last def prevRequestList = prevRequests.toList def allSeenTypes = prevRequestList flatMap (_.typeOf.values.toList) distinct diff --git a/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala b/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala index a86462ad5f..795b2e3678 100644 --- a/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala +++ b/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala @@ -194,14 +194,7 @@ class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput // literal Ints, Strings, etc. object literals extends CompletionAware { - def simpleParse(code: String): Tree = { - val unit = new CompilationUnit(new util.BatchSourceFile("<console>", code)) - val scanner = new syntaxAnalyzer.UnitParser(unit) - val tss = scanner.templateStatSeq(false)._2 - - if (tss.size == 1) tss.head else EmptyTree - } - + def simpleParse(code: String): Tree = newUnitParser(code).templateStats().last def completions(verbosity: Int) = Nil override def follow(id: String) = simpleParse(id) match { diff --git a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala index 40993eb916..fcede04aaf 100644 --- a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala +++ b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala @@ -64,6 +64,7 @@ trait MemberHandlers { } sealed abstract class MemberDefHandler(override val member: MemberDef) extends MemberHandler(member) { + def symbol = if (member.symbol eq null) NoSymbol else member.symbol def name: Name = member.name def mods: Modifiers = member.mods def keyword = member.keyword @@ -72,6 +73,7 @@ trait MemberHandlers { override def definesImplicit = member.mods.isImplicit override def definesTerm: Option[TermName] = Some(name.toTermName) filter (_ => name.isTermName) override def definesType: Option[TypeName] = Some(name.toTypeName) filter (_ => name.isTypeName) + override def definedSymbols = if (symbol eq NoSymbol) Nil else List(symbol) } /** Class to handle one member among all the members included @@ -89,6 +91,7 @@ trait MemberHandlers { def importedNames = List[Name]() def definedNames = definesTerm.toList ++ definesType.toList def definedOrImported = definedNames ++ importedNames + def definedSymbols = List[Symbol]() def extraCodeToEvaluate(req: Request): String = "" def resultExtractionCode(req: Request): String = "" diff --git a/src/compiler/scala/tools/nsc/interpreter/Power.scala b/src/compiler/scala/tools/nsc/interpreter/Power.scala index ecf9dd219b..2cb034f7ab 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Power.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Power.scala @@ -410,8 +410,8 @@ class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: Repl lazy val phased: Phased = new { val global: intp.global.type = intp.global } with Phased { } def context(code: String) = analyzer.rootContext(unit(code)) - def source(code: String) = new BatchSourceFile("<console>", code) - def unit(code: String) = new CompilationUnit(source(code)) + def source(code: String) = newSourceFile(code) + def unit(code: String) = newCompilationUnit(code) def trees(code: String) = parse(code) getOrElse Nil def typeOf(id: String) = intp.typeOfExpression(id) diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplGlobal.scala b/src/compiler/scala/tools/nsc/interpreter/ReplGlobal.scala new file mode 100644 index 0000000000..05321dd7e6 --- /dev/null +++ b/src/compiler/scala/tools/nsc/interpreter/ReplGlobal.scala @@ -0,0 +1,57 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools.nsc +package interpreter + +import reporters._ +import typechecker.Analyzer + +/** A layer on top of Global so I can guarantee some extra + * functionality for the repl. It doesn't do much yet. + */ +trait ReplGlobal extends Global { + // This exists mostly because using the reporter too early leads to deadlock. + private def echo(msg: String) { Console println msg } + + override def abort(msg: String): Nothing = { + echo("ReplGlobal.abort: " + msg) + super.abort(msg) + } + + override lazy val analyzer = new { + val global: ReplGlobal.this.type = ReplGlobal.this + } with Analyzer { + override def newTyper(context: Context): Typer = new Typer(context) { + override def typed(tree: Tree, mode: Int, pt: Type): Tree = { + val res = super.typed(tree, mode, pt) + tree match { + case Ident(name) if !tree.symbol.hasPackageFlag && !name.toString.startsWith("$") => + repldbg("typed %s: %s".format(name, res.tpe)) + case _ => + } + res + } + } + } + + object replPhase extends SubComponent { + val global: ReplGlobal.this.type = ReplGlobal.this + val phaseName = "repl" + val runsAfter = List[String]("typer") + val runsRightAfter = None + def newPhase(_prev: Phase): StdPhase = new StdPhase(_prev) { + def apply(unit: CompilationUnit) { + repldbg("Running replPhase on " + unit.body) + // newNamer(rootContext(unit)).enterSym(unit.body) + } + } + } + + override protected def computePhaseDescriptors: List[SubComponent] = { + addToPhasesSet(replPhase, "repl") + super.computePhaseDescriptors + } +} diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala b/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala index 130af990ad..fb61dfb672 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala @@ -9,7 +9,11 @@ package interpreter import reporters._ import IMain._ +/** Like ReplGlobal, a layer for ensuring extra functionality. + */ class ReplReporter(intp: IMain) extends ConsoleReporter(intp.settings, Console.in, new ReplStrippingWriter(intp)) { + def printUntruncatedMessage(msg: String) = withoutTruncating(printMessage(msg)) + override def printMessage(msg: String) { // Avoiding deadlock if the compiler starts logging before // the lazy val is complete. diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala b/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala index 8b891e0010..4efab7e260 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala @@ -27,7 +27,8 @@ class StdReplVals(final val r: ILoop) extends ReplVals { final lazy val phased = power.phased final lazy val analyzer = global.analyzer - final lazy val treedsl = new { val global: intp.global.type = intp.global } with ast.TreeDSL { } + object treedsl extends { val global: intp.global.type = intp.global } with ast.TreeDSL { } + final lazy val typer = analyzer.newTyper( analyzer.rootContext( power.unit("").asInstanceOf[analyzer.global.CompilationUnit] @@ -35,13 +36,15 @@ class StdReplVals(final val r: ILoop) extends ReplVals { ) def lastRequest = intp.lastRequest - final lazy val replImplicits = new power.Implicits2 { + class ReplImplicits extends power.Implicits2 { import intp.global._ private val manifestFn = ReplVals.mkManifestToType[intp.global.type](global) implicit def mkManifestToType(sym: Symbol) = manifestFn(sym) } + final lazy val replImplicits = new ReplImplicits + def typed[T <: analyzer.global.Tree](tree: T): T = typer.typed(tree).asInstanceOf[T] } diff --git a/src/compiler/scala/tools/nsc/interpreter/RichClass.scala b/src/compiler/scala/tools/nsc/interpreter/RichClass.scala index 59a7b9b5d2..b1bee6ce93 100644 --- a/src/compiler/scala/tools/nsc/interpreter/RichClass.scala +++ b/src/compiler/scala/tools/nsc/interpreter/RichClass.scala @@ -13,7 +13,10 @@ class RichClass[T](val clazz: Class[T]) { // Sadly isAnonymousClass does not return true for scala anonymous // classes because our naming scheme is not doing well against the // jvm's many assumptions. - def isScalaAnonymous = clazz.isAnonymousClass || (clazz.getName contains "$anon$") + def isScalaAnonymous = ( + try clazz.isAnonymousClass || (clazz.getName contains "$anon$") + catch { case _: java.lang.InternalError => false } // good ol' "Malformed class name" + ) /** It's not easy... to be... me... */ def supermans: List[Manifest[_]] = supers map (_.toManifest) diff --git a/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala b/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala index 872ac00bfd..9ba75d9166 100644 --- a/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala +++ b/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala @@ -11,6 +11,142 @@ import r.TypeVariable import scala.reflect.NameTransformer import NameTransformer._ import scala.reflect.{mirror => rm} +import typechecker.DestructureTypes +import scala.tools.util.StringOps.ojoin + +/** A more principled system for turning types into strings. + */ +trait StructuredTypeStrings extends DestructureTypes { + val global: Global + import global._ + import definitions._ + + case class LabelAndType(label: String, typeName: String) { } + object LabelAndType { + val empty = LabelAndType("", "") + } + case class Grouping(ldelim: String, mdelim: String, rdelim: String, labels: Boolean) { + def join(elems: String*): String = ( + if (elems.isEmpty) "" + else elems.mkString(ldelim, mdelim, rdelim) + ) + } + val NoGrouping = Grouping("", "", "", false) + val ListGrouping = Grouping("(", ", ", ")", false) + val ProductGrouping = Grouping("(", ", ", ")", true) + val ParamGrouping = Grouping("(", ", ", ")", true) + val BlockGrouping = Grouping(" { ", "; ", "}", false) + + private implicit def lowerName(n: Name): String = "" + n + private def str(level: Int)(body: => String): String = " " * level + body + private def block(level: Int, grouping: Grouping)(name: String, nodes: List[TypeNode]): String = { + val l1 = str(level)(name + grouping.ldelim) + val l2 = nodes.map(_ show level + 1) + val l3 = str(level)(grouping.rdelim) + + l1 +: l2 :+ l3 mkString "\n" + } + private def maybeBlock(level: Int, grouping: Grouping)(name: String, nodes: List[TypeNode]): String = { + import grouping._ + val threshold = 70 + + val try1 = str(level)(name + grouping.join(nodes map (_.show(0, grouping.labels)): _*)) + if (try1.length < threshold) try1 + else block(level, grouping)(name, nodes) + } + private def shortClass(x: Any) = { + if (opt.debug) { + val name = (x.getClass.getName split '.').last + val isAnon = name.reverse takeWhile (_ != '$') forall (_.isDigit) + val str = if (isAnon) name else (name split '$').last + + " // " + str + } + else "" + } + + sealed abstract class TypeNode { + def grouping: Grouping + def nodes: List[TypeNode] + + def show(indent: Int, showLabel: Boolean): String = maybeBlock(indent, grouping)(mkPrefix(showLabel), nodes) + def show(indent: Int): String = show(indent, true) + def show(): String = show(0) + + def withLabel(l: String): this.type = modifyNameInfo(_.copy(label = l)) + def withType(t: String): this.type = modifyNameInfo(_.copy(typeName = t)) + + def label = nameInfo.label + def typeName = nameInfo.typeName + + protected def mkPrefix(showLabel: Boolean) = { + val pre = if (showLabel && label != "") label + " = " else "" + pre + typeName + } + override def toString = show() // + "(toString)" + private var nameInfo: LabelAndType = LabelAndType.empty + private def modifyNameInfo(f: LabelAndType => LabelAndType): this.type = { + nameInfo = f(nameInfo) + this + } + } + case class TypeAtom[T](atom: T) extends TypeNode { + def grouping = NoGrouping + def nodes = Nil + override protected def mkPrefix(showLabel: Boolean) = + super.mkPrefix(showLabel) + atom + shortClass(atom) + } + case class TypeProduct(nodes: List[TypeNode]) extends TypeNode { + def grouping: Grouping = ProductGrouping + def emptyTypeName = "" + override def typeName = if (nodes.isEmpty) emptyTypeName else super.typeName + } + + /** For a NullaryMethod, in = TypeEmpty; for MethodType(Nil, _) in = TypeNil */ + class NullaryFunction(out: TypeNode) extends TypeProduct(List(out)) { + override def typeName = "NullaryMethodType" + } + class MonoFunction(in: TypeNode, out: TypeNode) extends TypeProduct(List(in, out)) { + override def typeName = "MethodType" + } + class PolyFunction(in: TypeNode, out: TypeNode) extends TypeProduct(List(in, out)) { + override def typeName = "PolyType" + } + + class TypeList(nodes: List[TypeNode]) extends TypeProduct(nodes) { + override def grouping = ListGrouping + override def emptyTypeName = "Nil" + override def typeName = "List" + } + class TypeScope(nodes: List[TypeNode]) extends TypeProduct(nodes) { + override def grouping = BlockGrouping + override def typeName = "Scope" + override def emptyTypeName = "EmptyScope" + } + + object TypeEmpty extends TypeNode { + override def grouping = NoGrouping + override def nodes = Nil + override def label = "" + override def typeName = "" + override def show(indent: Int, showLabel: Boolean) = "" + } + + object intoNodes extends DestructureType[TypeNode] { + def withLabel(node: TypeNode, label: String): TypeNode = node withLabel label + def withType(node: TypeNode, typeName: String): TypeNode = node withType typeName + + def wrapEmpty = TypeEmpty + def wrapSequence(nodes: List[TypeNode]) = new TypeList(nodes) + def wrapProduct(nodes: List[TypeNode]) = new TypeProduct(nodes) + def wrapPoly(in: TypeNode, out: TypeNode) = new PolyFunction(in, out) + def wrapMono(in: TypeNode, out: TypeNode) = if (in == wrapEmpty) new NullaryFunction(out) else new MonoFunction(in, out) + def wrapAtom[U](value: U) = new TypeAtom(value) + } + + def show(tp: Type): String = intoNodes(tp).show +} + /** Logic for turning a type into a String. The goal is to be * able to take some arbitrary object 'x' and obtain the most precise diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index e9c3bef737..8d26f66370 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -129,11 +129,11 @@ trait ContextErrors { val retyped = typed (tree.duplicate setType null) val foundDecls = retyped.tpe.decls filter (sym => !sym.isConstructor && !sym.isSynthetic) - if (foundDecls.isEmpty) found + if (foundDecls.isEmpty || (found.typeSymbol eq NoSymbol)) found else { // The members arrive marked private, presumably because there was no // expected type and so they're considered members of an anon class. - foundDecls foreach (_ resetFlag (PRIVATE | PROTECTED)) + foundDecls foreach (_.makePublic) // TODO: if any of the found parents match up with required parents after normalization, // print the error so that they match. The major beneficiary there would be // java.lang.Object vs. AnyRef. diff --git a/src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala b/src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala new file mode 100644 index 0000000000..0b414801d6 --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala @@ -0,0 +1,206 @@ +/* NSC -- new Scala compiler +* Copyright 2005-2012 LAMP/EPFL +* @author Paul Phillips +*/ + +package scala.tools.nsc +package typechecker + +/** A generic means of breaking down types into their subcomponents. + * Types are decomposed top down, and recognizable substructure is + * dispatched via self-apparently named methods. Those methods can + * be overridden for custom behavior, but only the abstract methods + * require implementations, each of which must create some unknown + * "Node" type from its inputs. + * + * - wrapProduct create Node from a product of Nodes + * - wrapSequence create Node from a sequence of Nodes + * - wrapAtom create Node from an arbitrary value + * + * This is a work in progress. + */ +trait DestructureTypes { + val global: Global + import global._ + import definitions.{ NothingClass, AnyClass } + + trait DestructureType[Node] extends (Type => Node) { + def withLabel(node: Node, label: String): Node + def withType(node: Node, typeName: String): Node + + def wrapEmpty: Node + def wrapPoly(in: Node, out: Node): Node + def wrapMono(in: Node, out: Node): Node + def wrapProduct(nodes: List[Node]): Node + def wrapSequence(nodes: List[Node]): Node + def wrapAtom[U](value: U): Node + + private implicit def liftToTerm(name: String): TermName = newTermName(name) + + private val openSymbols = collection.mutable.Set[Symbol]() + + private def nodeList[T](elems: List[T], mkNode: T => Node): Node = + if (elems.isEmpty) wrapEmpty else list(elems map mkNode) + + private def scopeMemberList(elems: List[Symbol]): Node = nodeList(elems, wrapAtom) + private def typeList(elems: List[Type]): Node = nodeList(elems, this) + private def symbolList(elems: List[Symbol]): Node = nodeList(elems, wrapSymbolInfo) + private def treeList(elems: List[Tree]): Node = nodeList(elems, wrapTree) + private def annotationList(annots: List[AnnotationInfo]): Node = nodeList(annots, annotation) + + private def assocsNode(ann: AnnotationInfo): Node = { + val (names, args) = ann.assocs.toIndexedSeq.unzip + if (names.isEmpty) wrapEmpty + else node("assocs", nodeList(names.indices.toList, (i: Int) => atom(names(i).toString, args(i)))) + } + private def typeTypeName(tp: Type) = tp match { + case mt @ MethodType(_, _) if mt.isImplicit => "ImplicitMethodType" + case TypeRef(_, sym, _) => typeRefType(sym) + case _ => tp.kind + } + + def wrapTree(tree: Tree): Node = withType( + tree match { + case x: NameTree => atom(x.name.toString, x) + case _ => wrapAtom(tree) + }, + tree.printingPrefix + ) + def wrapSymbol(label: String, sym: Symbol): Node = { + if (sym eq NoSymbol) wrapEmpty + else atom(label, sym) + } + def wrapInfo(sym: Symbol) = sym.info match { + case TypeBounds(lo, hi) => typeBounds(lo, hi) + case PolyType(tparams, restpe) => polyFunction(tparams, restpe) + case _ => wrapEmpty + } + def wrapSymbolInfo(sym: Symbol): Node = { + if ((sym eq NoSymbol) || openSymbols(sym)) wrapEmpty + else { + openSymbols += sym + try product(symbolType(sym), wrapAtom(sym.defString)) + finally openSymbols -= sym + } + } + + def list(nodes: List[Node]): Node = wrapSequence(nodes) + def product(tp: Type, nodes: Node*): Node = product(typeTypeName(tp), nodes: _*) + def product(typeName: String, nodes: Node*): Node = ( + nodes.toList filterNot (_ == wrapEmpty) match { + case Nil => wrapEmpty + case xs => withType(wrapProduct(xs), typeName) + } + ) + + def atom[U](label: String, value: U): Node = node(label, wrapAtom(value)) + def constant(label: String, const: Constant): Node = atom(label, const) + + def scope(decls: Scope): Node = node("decls", scopeMemberList(decls.toList)) + def const[T](named: (String, T)): Node = constant(named._1, Constant(named._2)) + + def resultType(restpe: Type): Node = this("resultType", restpe) + def typeParams(tps: List[Symbol]): Node = node("typeParams", symbolList(tps)) + def valueParams(params: List[Symbol]): Node = node("params", symbolList(params)) + def typeArgs(tps: List[Type]): Node = node("args", typeList(tps)) + def parentList(tps: List[Type]): Node = node("parents", typeList(tps)) + + def polyFunction(tparams: List[Symbol], restpe: Type): Node = wrapPoly(typeParams(tparams), resultType(restpe)) + def monoFunction(params: List[Symbol], restpe: Type): Node = wrapMono(valueParams(params), resultType(restpe)) + def nullaryFunction(restpe: Type): Node = wrapMono(wrapEmpty, this(restpe)) + + def prefix(pre: Type): Node = pre match { + case NoPrefix => wrapEmpty + case _ => this("pre", pre) + } + def typeBounds(lo0: Type, hi0: Type): Node = { + val lo = if ((lo0 eq WildcardType) || (lo0.typeSymbol eq NothingClass)) wrapEmpty else this("lo", lo0) + val hi = if ((hi0 eq WildcardType) || (hi0.typeSymbol eq AnyClass)) wrapEmpty else this("hi", hi0) + + product("TypeBounds", lo, hi) + } + + def annotation(ann: AnnotationInfo): Node = product( + "AnnotationInfo", + this("atp", ann.atp), + node("args", treeList(ann.args)), + assocsNode(ann) + ) + def typeConstraint(constr: TypeConstraint): Node = product( + "TypeConstraint", + node("lo", typeList(constr.loBounds)), + node("hi", typeList(constr.hiBounds)), + this("inst", constr.inst) + ) + def annotatedType(annotations: List[AnnotationInfo], underlying: Type) = product( + "AnnotatedType", + node("annotations", annotationList(annotations)), + this("underlying", underlying) + ) + + /** This imposes additional structure beyond that which is visible in + * the case class hierarchy. In particular, (too) many different constructs + * are encoded in TypeRefs; here they are partitioned somewhat before + * being dispatched. + * + * For example, a typical type parameter is encoded as TypeRef(NoPrefix, sym, Nil) + * with its upper and lower bounds stored in the info of the symbol. Viewing the + * TypeRef naively we are treated to both too much information (useless prefix, usually + * empty args) and too little (bounds hidden behind indirection.) So drop the prefix + * and promote the bounds. + */ + def typeRef(tp: TypeRef) = { + val TypeRef(pre, sym, args) = tp + // Filtered down to elements with "interesting" content + product( + tp, + if (sym.isDefinedInPackage) wrapEmpty else prefix(pre), + wrapSymbolInfo(sym), + typeArgs(args), + if (tp ne tp.normalize) this("normalize", tp.normalize) else wrapEmpty + ) + } + + def symbolType(sym: Symbol) = ( + if (sym.isRefinementClass) "Refinement" + else if (sym.isAliasType) "Alias" + else if (sym.isTypeSkolem) "TypeSkolem" + else if (sym.isTypeParameter) "TypeParam" + else if (sym.isAbstractType) "AbstractType" + else if (sym.isType) "TypeSymbol" + else "TermSymbol" + ) + def typeRefType(sym: Symbol) = ( + if (sym.isRefinementClass) "RefinementTypeRef" + else if (sym.isAliasType) "AliasTypeRef" + else if (sym.isTypeSkolem) "SkolemTypeRef" + else if (sym.isTypeParameter) "TypeParamTypeRef" + else if (sym.isAbstractType) "AbstractTypeRef" + else "TypeRef" + ) + ( if (sym.isFBounded) "(F-Bounded)" else "" ) + + def node(label: String, node: Node): Node = withLabel(node, label) + def apply(label: String, tp: Type): Node = withLabel(this(tp), label) + + def apply(tp: Type): Node = tp match { + case AntiPolyType(pre, targs) => product(tp, prefix(pre), typeArgs(targs)) + case ClassInfoType(parents, decls, clazz) => product(tp, parentList(parents), scope(decls), wrapAtom(clazz)) + case ConstantType(const) => product(tp, constant("value", const)) + case DeBruijnIndex(level, index, args) => product(tp, const("level" -> level), const("index" -> index), typeArgs(args)) + case OverloadedType(pre, alts) => product(tp, prefix(pre), node("alts", typeList(alts map pre.memberType))) + case RefinedType(parents, decls) => product(tp, parentList(parents), scope(decls)) + case SingleType(pre, sym) => product(tp, prefix(pre), wrapAtom(sym)) + case SuperType(thistp, supertp) => product(tp, this("this", thistp), this("super", supertp)) + case ThisType(clazz) => product(tp, wrapAtom(clazz)) + case TypeVar(inst, constr) => product(tp, this("inst", inst), typeConstraint(constr)) + case AnnotatedType(annotations, underlying, _) => annotatedType(annotations, underlying) + case ExistentialType(tparams, underlying) => polyFunction(tparams, underlying) + case PolyType(tparams, restpe) => polyFunction(tparams, restpe) + case MethodType(params, restpe) => monoFunction(params, restpe) + case NullaryMethodType(restpe) => nullaryFunction(restpe) + case TypeBounds(lo, hi) => typeBounds(lo, hi) + case tr @ TypeRef(pre, sym, args) => typeRef(tr) + case _ => wrapAtom(tp) // XXX see what this is + } + } +} diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index b7e0eaef2b..62a0e08aad 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -185,16 +185,7 @@ trait Macros { self: Analyzer => import typer.context if (macroDebug) println("typechecking macro def %s at %s".format(ddef.symbol, ddef.pos)) - typer.checkFeature(ddef.pos, MacrosFeature) - - // [Eugene to Martin] todo. copy/pasted this from checkFeature, because don't know a better way - // this is necessary to prevent macros from typechecking/expanding when they are not enabled - // `checkFeature` call alone is not enough, because it merely posts validation callback to unit.toCheck - def hasImport = inferImplicit(EmptyTree: Tree, MacrosFeature.tpe, true, false, typer.context) != SearchFailure - val nestedOwners = MacrosFeature.owner.ownerChain.takeWhile(_ != languageFeatureModule.moduleClass).reverse - val featureName = (nestedOwners map (_.name + ".")).mkString + MacrosFeature.name - def hasOption = settings.language.value contains featureName - if (!hasImport && !hasOption) { + if (!typer.checkFeature(ddef.pos, MacrosFeature, immediate = true)) { ddef.symbol setFlag IS_ERROR return EmptyTree } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index ff54c11af8..8f5b3fb519 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -733,15 +733,27 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } } - def checkFeature(pos: Position, featureTrait: Symbol, construct: => String = "") = - if (!isPastTyper) { + /** Check whether feature given by `featureTrait` is enabled. + * If it is not, issue an error or a warning depending on whether the feature is required. + * @param construct A string expression that is substituted for "#" in the feature description string + * @param immediate When set, feature check is run immediately, otherwise it is run + * at the end of the typechecking run for the enclosing unit. This + * is done to avoid potential cyclic reference errors by implicits + * that are forced too early. + * @return if feature check is run immediately: true if feature is enabled, false otherwise + * if feature check is delayed or suppressed because we are past typer: true + */ + def checkFeature(pos: Position, featureTrait: Symbol, construct: => String = "", immediate: Boolean = false): Boolean = + if (isPastTyper) true + else { val nestedOwners = featureTrait.owner.ownerChain.takeWhile(_ != languageFeatureModule.moduleClass).reverse val featureName = (nestedOwners map (_.name + ".")).mkString + featureTrait.name - unit.toCheck += { () => + def action(): Boolean = { def hasImport = inferImplicit(EmptyTree: Tree, featureTrait.tpe, true, false, context) != SearchFailure def hasOption = settings.language.value contains featureName - if (!hasImport && !hasOption) { + val OK = hasImport || hasOption + if (!OK) { val Some(AnnotationInfo(_, List(Literal(Constant(featureDesc: String)), Literal(Constant(required: Boolean))), _)) = featureTrait getAnnotation LanguageFeatureAnnot val req = if (required) "needs to" else "should" @@ -757,6 +769,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (required) unit.error(pos, msg) else currentRun.featureWarnings.warn(pos, msg) } + OK + } + if (immediate) { + action() + } else { + unit.toCheck += action + true } } diff --git a/src/compiler/scala/tools/nsc/util/Position.scala b/src/compiler/scala/tools/nsc/util/Position.scala index 573f7bc7b2..208cd5703a 100644 --- a/src/compiler/scala/tools/nsc/util/Position.scala +++ b/src/compiler/scala/tools/nsc/util/Position.scala @@ -40,7 +40,15 @@ trait Position extends scala.reflect.api.Position with scala.reflect.api.Attachm /** A bit weird method that is necessary to safely update positions without destroying custom attachments */ // necessary for conformance with Attachment - def withPos(pos: scala.reflect.api.Position) = pos + def withPos(newPos: scala.reflect.api.Position): scala.reflect.api.Attachment = newPos + + /** Exposes itself as payload of Attachment */ + // necessary for conformance with Attachment + def payload: Position = this + + /** A bit weird method that is necessary to safely update positions without destroying custom attachments */ + // necessary for conformance with Attachment + def withPayload(newPos: Any): scala.reflect.api.Attachment = newPos.asInstanceOf[Position] /** Java file corresponding to the source file of this position. */ diff --git a/src/forkjoin/scala/concurrent/forkjoin/ForkJoinPool.java b/src/forkjoin/scala/concurrent/forkjoin/ForkJoinPool.java index e9389e9acb..4b5b3382f5 100644 --- a/src/forkjoin/scala/concurrent/forkjoin/ForkJoinPool.java +++ b/src/forkjoin/scala/concurrent/forkjoin/ForkJoinPool.java @@ -1,5 +1,4 @@ /* - * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at * http://creativecommons.org/publicdomain/zero/1.0/ @@ -12,22 +11,18 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Random; -//import java.util.concurrent.AbstractExecutorService; +import java.util.concurrent.AbstractExecutorService; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; -//import java.util.concurrent.RunnableFuture; +import java.util.concurrent.RunnableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.AbstractQueuedSynchronizer; import java.util.concurrent.locks.Condition; -interface RunnableFuture<T> extends Runnable { - //TR placeholder for java.util.concurrent.RunnableFuture -} - /** * An {@link ExecutorService} for running {@link ForkJoinTask}s. * A {@code ForkJoinPool} provides the entry point for submissions @@ -127,7 +122,7 @@ interface RunnableFuture<T> extends Runnable { * @since 1.7 * @author Doug Lea */ -public class ForkJoinPool /*extends AbstractExecutorService*/ { +public class ForkJoinPool extends AbstractExecutorService { /* * Implementation Overview @@ -634,7 +629,7 @@ public class ForkJoinPool /*extends AbstractExecutorService*/ { final ForkJoinPool pool; // the containing pool (may be null) final ForkJoinWorkerThread owner; // owning thread or null if shared volatile Thread parker; // == owner during call to park; else null - ForkJoinTask<?> currentJoin; // task being joined in awaitJoin + volatile ForkJoinTask<?> currentJoin; // task being joined in awaitJoin ForkJoinTask<?> currentSteal; // current non-local task being executed // Heuristic padding to ameliorate unfortunate memory placements Object p00, p01, p02, p03, p04, p05, p06, p07; @@ -726,12 +721,11 @@ public class ForkJoinPool /*extends AbstractExecutorService*/ { * version of this method because it is never needed.) */ final ForkJoinTask<?> pop() { - ForkJoinTask<?> t; int m; - ForkJoinTask<?>[] a = array; - if (a != null && (m = a.length - 1) >= 0) { + ForkJoinTask<?>[] a; ForkJoinTask<?> t; int m; + if ((a = array) != null && (m = a.length - 1) >= 0) { for (int s; (s = top - 1) - base >= 0;) { - int j = ((m & s) << ASHIFT) + ABASE; - if ((t = (ForkJoinTask<?>)U.getObjectVolatile(a, j)) == null) + long j = ((m & s) << ASHIFT) + ABASE; + if ((t = (ForkJoinTask<?>)U.getObject(a, j)) == null) break; if (U.compareAndSwapObject(a, j, t, null)) { top = s; @@ -835,54 +829,6 @@ public class ForkJoinPool /*extends AbstractExecutorService*/ { } /** - * If present, removes from queue and executes the given task, or - * any other cancelled task. Returns (true) immediately on any CAS - * or consistency check failure so caller can retry. - * - * @return false if no progress can be made - */ - final boolean tryRemoveAndExec(ForkJoinTask<?> task) { - boolean removed = false, empty = true, progress = true; - ForkJoinTask<?>[] a; int m, s, b, n; - if ((a = array) != null && (m = a.length - 1) >= 0 && - (n = (s = top) - (b = base)) > 0) { - for (ForkJoinTask<?> t;;) { // traverse from s to b - int j = ((--s & m) << ASHIFT) + ABASE; - t = (ForkJoinTask<?>)U.getObjectVolatile(a, j); - if (t == null) // inconsistent length - break; - else if (t == task) { - if (s + 1 == top) { // pop - if (!U.compareAndSwapObject(a, j, task, null)) - break; - top = s; - removed = true; - } - else if (base == b) // replace with proxy - removed = U.compareAndSwapObject(a, j, task, - new EmptyTask()); - break; - } - else if (t.status >= 0) - empty = false; - else if (s + 1 == top) { // pop and throw away - if (U.compareAndSwapObject(a, j, t, null)) - top = s; - break; - } - if (--n == 0) { - if (!empty && base == b) - progress = false; - break; - } - } - } - if (removed) - task.doExec(); - return progress; - } - - /** * Initializes or doubles the capacity of array. Call either * by owner or with lock held -- it is OK for base, but not * top, to move while resizings are in progress. @@ -944,69 +890,98 @@ public class ForkJoinPool /*extends AbstractExecutorService*/ { // Execution methods /** - * Removes and runs tasks until empty, using local mode - * ordering. Normally called only after checking for apparent - * non-emptiness. + * Pops and runs tasks until empty. */ - final void runLocalTasks() { - // hoist checks from repeated pop/poll - ForkJoinTask<?>[] a; int m; - if ((a = array) != null && (m = a.length - 1) >= 0) { - if (mode == 0) { - for (int s; (s = top - 1) - base >= 0;) { - int j = ((m & s) << ASHIFT) + ABASE; - ForkJoinTask<?> t = - (ForkJoinTask<?>)U.getObjectVolatile(a, j); - if (t != null) { - if (U.compareAndSwapObject(a, j, t, null)) { - top = s; - t.doExec(); - } - } - else - break; - } + private void popAndExecAll() { + // A bit faster than repeated pop calls + ForkJoinTask<?>[] a; int m, s; long j; ForkJoinTask<?> t; + while ((a = array) != null && (m = a.length - 1) >= 0 && + (s = top - 1) - base >= 0 && + (t = ((ForkJoinTask<?>) + U.getObject(a, j = ((m & s) << ASHIFT) + ABASE))) + != null) { + if (U.compareAndSwapObject(a, j, t, null)) { + top = s; + t.doExec(); } - else { - for (int b; (b = base) - top < 0;) { - int j = ((m & b) << ASHIFT) + ABASE; - ForkJoinTask<?> t = - (ForkJoinTask<?>)U.getObjectVolatile(a, j); - if (t != null) { - if (base == b && - U.compareAndSwapObject(a, j, t, null)) { - base = b + 1; - t.doExec(); - } - } else if (base == b) { - if (b + 1 == top) + } + } + + /** + * Polls and runs tasks until empty. + */ + private void pollAndExecAll() { + for (ForkJoinTask<?> t; (t = poll()) != null;) + t.doExec(); + } + + /** + * If present, removes from queue and executes the given task, or + * any other cancelled task. Returns (true) immediately on any CAS + * or consistency check failure so caller can retry. + * + * @return 0 if no progress can be made, else positive + * (this unusual convention simplifies use with tryHelpStealer.) + */ + final int tryRemoveAndExec(ForkJoinTask<?> task) { + int stat = 1; + boolean removed = false, empty = true; + ForkJoinTask<?>[] a; int m, s, b, n; + if ((a = array) != null && (m = a.length - 1) >= 0 && + (n = (s = top) - (b = base)) > 0) { + for (ForkJoinTask<?> t;;) { // traverse from s to b + int j = ((--s & m) << ASHIFT) + ABASE; + t = (ForkJoinTask<?>)U.getObjectVolatile(a, j); + if (t == null) // inconsistent length + break; + else if (t == task) { + if (s + 1 == top) { // pop + if (!U.compareAndSwapObject(a, j, task, null)) break; - Thread.yield(); // wait for lagging update + top = s; + removed = true; } + else if (base == b) // replace with proxy + removed = U.compareAndSwapObject(a, j, task, + new EmptyTask()); + break; + } + else if (t.status >= 0) + empty = false; + else if (s + 1 == top) { // pop and throw away + if (U.compareAndSwapObject(a, j, t, null)) + top = s; + break; + } + if (--n == 0) { + if (!empty && base == b) + stat = 0; + break; } } } + if (removed) + task.doExec(); + return stat; } /** * Executes a top-level task and any local tasks remaining * after execution. - * - * @return true unless terminating */ - final boolean runTask(ForkJoinTask<?> t) { - boolean alive = true; + final void runTask(ForkJoinTask<?> t) { if (t != null) { currentSteal = t; t.doExec(); - if (top != base) // conservative guard - runLocalTasks(); + if (top != base) { // process remaining local tasks + if (mode == 0) + popAndExecAll(); + else + pollAndExecAll(); + } ++nsteals; currentSteal = null; } - else if (runState < 0) // terminating - alive = false; - return alive; } /** @@ -1072,7 +1047,6 @@ public class ForkJoinPool /*extends AbstractExecutorService*/ { ASHIFT = 31 - Integer.numberOfLeadingZeros(s); } } - /** * Per-thread records for threads that submit to pools. Currently * holds only pseudo-random seed / index that is used to choose @@ -1165,7 +1139,7 @@ public class ForkJoinPool /*extends AbstractExecutorService*/ { * traversal parameters at the expense of sometimes blocking when * we could be helping. */ - private static final int MAX_HELP = 32; + private static final int MAX_HELP = 64; /** * Secondary time-based bound (in nanosecs) for helping attempts @@ -1175,7 +1149,7 @@ public class ForkJoinPool /*extends AbstractExecutorService*/ { * value should roughly approximate the time required to create * and/or activate a worker thread. */ - private static final long COMPENSATION_DELAY = 100L * 1000L; // 0.1 millisec + private static final long COMPENSATION_DELAY = 1L << 18; // ~0.25 millisec /** * Increment for seed generators. See class ThreadLocal for @@ -1326,22 +1300,28 @@ public class ForkJoinPool /*extends AbstractExecutorService*/ { * * @param w the worker's queue */ + final void registerWorker(WorkQueue w) { Mutex lock = this.lock; lock.lock(); try { WorkQueue[] ws = workQueues; if (w != null && ws != null) { // skip on shutdown/failure - int rs, n; - while ((n = ws.length) < // ensure can hold total - (parallelism + (short)(ctl >>> TC_SHIFT) << 1)) - workQueues = ws = Arrays.copyOf(ws, n << 1); - int m = n - 1; + int rs, n = ws.length, m = n - 1; int s = nextSeed += SEED_INCREMENT; // rarely-colliding sequence w.seed = (s == 0) ? 1 : s; // ensure non-zero seed int r = (s << 1) | 1; // use odd-numbered indices - while (ws[r &= m] != null) // step by approx half size - r += ((n >>> 1) & SQMASK) + 2; + if (ws[r &= m] != null) { // collision + int probes = 0; // step by approx half size + int step = (n <= 4) ? 2 : ((n >>> 1) & SQMASK) + 2; + while (ws[r = (r + step) & m] != null) { + if (++probes >= n) { + workQueues = ws = Arrays.copyOf(ws, n <<= 1); + m = n - 1; + probes = 0; + } + } + } w.eventCount = w.poolIndex = r; // establish before recording ws[r] = w; // also update seq runState = ((rs = runState) & SHUTDOWN) | ((rs + 2) & ~SHUTDOWN); @@ -1488,7 +1468,6 @@ public class ForkJoinPool /*extends AbstractExecutorService*/ { } } - // Scanning for tasks /** @@ -1496,7 +1475,7 @@ public class ForkJoinPool /*extends AbstractExecutorService*/ { */ final void runWorker(WorkQueue w) { w.growArray(false); // initialize queue array in this thread - do {} while (w.runTask(scan(w))); + do { w.runTask(scan(w)); } while (w.runState >= 0); } /** @@ -1559,8 +1538,7 @@ public class ForkJoinPool /*extends AbstractExecutorService*/ { q.base = b + 1; // specialization of pollAt return t; } - else if ((t != null || b + 1 != q.top) && - (ec < 0 || j <= m)) { + else if (ec < 0 || j <= m) { rs = 0; // mark scan as imcomplete break; // caller can retry after release } @@ -1568,6 +1546,7 @@ public class ForkJoinPool /*extends AbstractExecutorService*/ { if (--j < 0) break; } + long c = ctl; int e = (int)c, a = (int)(c >> AC_SHIFT), nr, ns; if (e < 0) // decode ctl on empty scan w.runState = -1; // pool is terminating @@ -1635,7 +1614,7 @@ public class ForkJoinPool /*extends AbstractExecutorService*/ { */ private void idleAwaitWork(WorkQueue w, long currentCtl, long prevCtl) { if (w.eventCount < 0 && !tryTerminate(false, false) && - (int)prevCtl != 0 && ctl == currentCtl) { + (int)prevCtl != 0 && !hasQueuedSubmissions() && ctl == currentCtl) { Thread wt = Thread.currentThread(); Thread.yield(); // yield before block while (ctl == currentCtl) { @@ -1670,70 +1649,79 @@ public class ForkJoinPool /*extends AbstractExecutorService*/ { * leaves hints in workers to speed up subsequent calls. The * implementation is very branchy to cope with potential * inconsistencies or loops encountering chains that are stale, - * unknown, or so long that they are likely cyclic. All of these - * cases are dealt with by just retrying by caller. + * unknown, or so long that they are likely cyclic. * * @param joiner the joining worker * @param task the task to join - * @return true if found or ran a task (and so is immediately retryable) - */ - private boolean tryHelpStealer(WorkQueue joiner, ForkJoinTask<?> task) { - WorkQueue[] ws; - int m, depth = MAX_HELP; // remaining chain depth - boolean progress = false; - if ((ws = workQueues) != null && (m = ws.length - 1) > 0 && - task.status >= 0) { - ForkJoinTask<?> subtask = task; // current target - outer: for (WorkQueue j = joiner;;) { - WorkQueue stealer = null; // find stealer of subtask - WorkQueue v = ws[j.stealHint & m]; // try hint - if (v != null && v.currentSteal == subtask) - stealer = v; - else { // scan - for (int i = 1; i <= m; i += 2) { - if ((v = ws[i]) != null && v.currentSteal == subtask && - v != joiner) { - stealer = v; - j.stealHint = i; // save hint - break; - } + * @return 0 if no progress can be made, negative if task + * known complete, else positive + */ + private int tryHelpStealer(WorkQueue joiner, ForkJoinTask<?> task) { + int stat = 0, steps = 0; // bound to avoid cycles + if (joiner != null && task != null) { // hoist null checks + restart: for (;;) { + ForkJoinTask<?> subtask = task; // current target + for (WorkQueue j = joiner, v;;) { // v is stealer of subtask + WorkQueue[] ws; int m, s, h; + if ((s = task.status) < 0) { + stat = s; + break restart; } - if (stealer == null) - break; - } - - for (WorkQueue q = stealer;;) { // try to help stealer - ForkJoinTask[] a; ForkJoinTask<?> t; int b; - if (task.status < 0) - break outer; - if ((b = q.base) - q.top < 0 && (a = q.array) != null) { - progress = true; - int i = (((a.length - 1) & b) << ASHIFT) + ABASE; - t = (ForkJoinTask<?>)U.getObjectVolatile(a, i); - if (subtask.status < 0) // must recheck before taking - break outer; - if (t != null && - q.base == b && - U.compareAndSwapObject(a, i, t, null)) { - q.base = b + 1; - joiner.runSubtask(t); + if ((ws = workQueues) == null || (m = ws.length - 1) <= 0) + break restart; // shutting down + if ((v = ws[h = (j.stealHint | 1) & m]) == null || + v.currentSteal != subtask) { + for (int origin = h;;) { // find stealer + if (((h = (h + 2) & m) & 15) == 1 && + (subtask.status < 0 || j.currentJoin != subtask)) + continue restart; // occasional staleness check + if ((v = ws[h]) != null && + v.currentSteal == subtask) { + j.stealHint = h; // save hint + break; + } + if (h == origin) + break restart; // cannot find stealer } - else if (q.base == b) - break outer; // possibly stalled } - else { // descend - ForkJoinTask<?> next = stealer.currentJoin; - if (--depth <= 0 || subtask.status < 0 || - next == null || next == subtask) - break outer; // stale, dead-end, or cyclic - subtask = next; - j = stealer; - break; + for (;;) { // help stealer or descend to its stealer + ForkJoinTask[] a; int b; + if (subtask.status < 0) // surround probes with + continue restart; // consistency checks + if ((b = v.base) - v.top < 0 && (a = v.array) != null) { + int i = (((a.length - 1) & b) << ASHIFT) + ABASE; + ForkJoinTask<?> t = + (ForkJoinTask<?>)U.getObjectVolatile(a, i); + if (subtask.status < 0 || j.currentJoin != subtask || + v.currentSteal != subtask) + continue restart; // stale + stat = 1; // apparent progress + if (t != null && v.base == b && + U.compareAndSwapObject(a, i, t, null)) { + v.base = b + 1; // help stealer + joiner.runSubtask(t); + } + else if (v.base == b && ++steps == MAX_HELP) + break restart; // v apparently stalled + } + else { // empty -- try to descend + ForkJoinTask<?> next = v.currentJoin; + if (subtask.status < 0 || j.currentJoin != subtask || + v.currentSteal != subtask) + continue restart; // stale + else if (next == null || ++steps == MAX_HELP) + break restart; // dead-end or maybe cyclic + else { + subtask = next; + j = v; + break; + } + } } } } } - return progress; + return stat; } /** @@ -1833,44 +1821,50 @@ public class ForkJoinPool /*extends AbstractExecutorService*/ { * @return task status on exit */ final int awaitJoin(WorkQueue joiner, ForkJoinTask<?> task) { - ForkJoinTask<?> prevJoin = joiner.currentJoin; - joiner.currentJoin = task; - long startTime = 0L; - for (int k = 0, s; ; ++k) { - if ((joiner.isEmpty() ? // try to help - !tryHelpStealer(joiner, task) : - !joiner.tryRemoveAndExec(task))) { - if (k == 0) { - startTime = System.nanoTime(); - tryPollForAndExec(joiner, task); // check uncommon case - } - else if ((k & (MAX_HELP - 1)) == 0 && - System.nanoTime() - startTime >= COMPENSATION_DELAY && - tryCompensate(task, null)) { - if (task.trySetSignal() && task.status >= 0) { - synchronized (task) { - if (task.status >= 0) { - try { // see ForkJoinTask - task.wait(); // for explanation - } catch (InterruptedException ie) { + int s; + if ((s = task.status) >= 0) { + ForkJoinTask<?> prevJoin = joiner.currentJoin; + joiner.currentJoin = task; + long startTime = 0L; + for (int k = 0;;) { + if ((s = (joiner.isEmpty() ? // try to help + tryHelpStealer(joiner, task) : + joiner.tryRemoveAndExec(task))) == 0 && + (s = task.status) >= 0) { + if (k == 0) { + startTime = System.nanoTime(); + tryPollForAndExec(joiner, task); // check uncommon case + } + else if ((k & (MAX_HELP - 1)) == 0 && + System.nanoTime() - startTime >= + COMPENSATION_DELAY && + tryCompensate(task, null)) { + if (task.trySetSignal()) { + synchronized (task) { + if (task.status >= 0) { + try { // see ForkJoinTask + task.wait(); // for explanation + } catch (InterruptedException ie) { + } } + else + task.notifyAll(); } - else - task.notifyAll(); } + long c; // re-activate + do {} while (!U.compareAndSwapLong + (this, CTL, c = ctl, c + AC_UNIT)); } - long c; // re-activate - do {} while (!U.compareAndSwapLong - (this, CTL, c = ctl, c + AC_UNIT)); } + if (s < 0 || (s = task.status) < 0) { + joiner.currentJoin = prevJoin; + break; + } + else if ((k++ & (MAX_HELP - 1)) == MAX_HELP >>> 1) + Thread.yield(); // for politeness } - if ((s = task.status) < 0) { - joiner.currentJoin = prevJoin; - return s; - } - else if ((k & (MAX_HELP - 1)) == MAX_HELP >>> 1) - Thread.yield(); // for politeness } + return s; } /** @@ -1887,7 +1881,7 @@ public class ForkJoinPool /*extends AbstractExecutorService*/ { while ((s = task.status) >= 0 && (joiner.isEmpty() ? tryHelpStealer(joiner, task) : - joiner.tryRemoveAndExec(task))) + joiner.tryRemoveAndExec(task)) != 0) ; return s; } @@ -1919,6 +1913,7 @@ public class ForkJoinPool /*extends AbstractExecutorService*/ { } } + /** * Runs tasks until {@code isQuiescent()}. We piggyback on * active count ctl maintenance, but rather than blocking @@ -1927,8 +1922,9 @@ public class ForkJoinPool /*extends AbstractExecutorService*/ { */ final void helpQuiescePool(WorkQueue w) { for (boolean active = true;;) { - if (w.base - w.top < 0) - w.runLocalTasks(); // exhaust local queue + ForkJoinTask<?> localTask; // exhaust local queue + while ((localTask = w.nextLocalTask()) != null) + localTask.doExec(); WorkQueue q = findNonEmptyStealQueue(w); if (q != null) { ForkJoinTask<?> t; int b; diff --git a/src/forkjoin/scala/concurrent/forkjoin/ForkJoinTask.java b/src/forkjoin/scala/concurrent/forkjoin/ForkJoinTask.java index 344f6887a6..2ba146c4da 100644 --- a/src/forkjoin/scala/concurrent/forkjoin/ForkJoinTask.java +++ b/src/forkjoin/scala/concurrent/forkjoin/ForkJoinTask.java @@ -16,7 +16,7 @@ import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; -//import java.util.concurrent.RunnableFuture; +import java.util.concurrent.RunnableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.ReentrantLock; @@ -115,18 +115,19 @@ import java.lang.reflect.Constructor; * <p>The ForkJoinTask class is not usually directly subclassed. * Instead, you subclass one of the abstract classes that support a * particular style of fork/join processing, typically {@link - * RecursiveAction} for computations that do not return results, or - * {@link RecursiveTask} for those that do. Normally, a concrete - * ForkJoinTask subclass declares fields comprising its parameters, - * established in a constructor, and then defines a {@code compute} - * method that somehow uses the control methods supplied by this base - * class. While these methods have {@code public} access (to allow - * instances of different task subclasses to call each other's - * methods), some of them may only be called from within other - * ForkJoinTasks (as may be determined using method {@link - * #inForkJoinPool}). Attempts to invoke them in other contexts - * result in exceptions or errors, possibly including - * {@code ClassCastException}. + * RecursiveAction} for most computations that do not return results, + * {@link RecursiveTask} for those that do, and {@link + * CountedCompleter} for those in which completed actions trigger + * other actions. Normally, a concrete ForkJoinTask subclass declares + * fields comprising its parameters, established in a constructor, and + * then defines a {@code compute} method that somehow uses the control + * methods supplied by this base class. While these methods have + * {@code public} access (to allow instances of different task + * subclasses to call each other's methods), some of them may only be + * called from within other ForkJoinTasks (as may be determined using + * method {@link #inForkJoinPool}). Attempts to invoke them in other + * contexts result in exceptions or errors, possibly including {@code + * ClassCastException}. * * <p>Method {@link #join} and its variants are appropriate for use * only when completion dependencies are acyclic; that is, the @@ -137,17 +138,17 @@ import java.lang.reflect.Constructor; * {@link Phaser}, {@link #helpQuiesce}, and {@link #complete}) that * may be of use in constructing custom subclasses for problems that * are not statically structured as DAGs. To support such usages a - * ForkJoinTask may be atomically <em>marked</em> using {@link - * #markForkJoinTask} and checked for marking using {@link - * #isMarkedForkJoinTask}. The ForkJoinTask implementation does not - * use these {@code protected} methods or marks for any purpose, but + * ForkJoinTask may be atomically <em>tagged</em> with a {@code + * short} value using {@link #setForkJoinTaskTag} or {@link + * #compareAndSetForkJoinTaskTag} and checked using {@link + * #getForkJoinTaskTag}. The ForkJoinTask implementation does not + * use these {@code protected} methods or tags for any purpose, but * they may be of use in the construction of specialized subclasses. * For example, parallel graph traversals can use the supplied methods * to avoid revisiting nodes/tasks that have already been processed. - * Also, completion based designs can use them to record that one - * subtask has completed. (Method names for marking are bulky in part - * to encourage definition of methods that reflect their usage - * patterns.) + * Also, completion based designs can use them to record that subtasks + * have completed. (Method names for tagging are bulky in part to + * encourage definition of methods that reflect their usage patterns.) * * <p>Most base support methods are {@code final}, to prevent * overriding of implementations that are intrinsically tied to the @@ -213,6 +214,10 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { * thin-lock techniques, so use some odd coding idioms that tend * to avoid them, mainly by arranging that every synchronized * block performs a wait, notifyAll or both. + * + * These control bits occupy only (some of) the upper half (16 + * bits) of status field. The lower bits are used for user-defined + * tags. */ /** The run status of this task */ @@ -221,13 +226,12 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { static final int NORMAL = 0xf0000000; // must be negative static final int CANCELLED = 0xc0000000; // must be < NORMAL static final int EXCEPTIONAL = 0x80000000; // must be < CANCELLED - static final int SIGNAL = 0x00000001; - static final int MARKED = 0x00000002; + static final int SIGNAL = 0x00010000; // must be >= 1 << 16 + static final int SMASK = 0x0000ffff; // short bits for tags /** * Marks completion and wakes up threads waiting to join this - * task. A specialization for NORMAL completion is in method - * doExec. + * task. * * @param completion one of NORMAL, CANCELLED, EXCEPTIONAL * @return completion status on exit @@ -237,7 +241,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { if ((s = status) < 0) return s; if (U.compareAndSwapInt(this, STATUS, s, s | completion)) { - if ((s & SIGNAL) != 0) + if ((s >>> 16) != 0) synchronized (this) { notifyAll(); } return completion; } @@ -259,26 +263,22 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { } catch (Throwable rex) { return setExceptionalCompletion(rex); } - while ((s = status) >= 0 && completed) { - if (U.compareAndSwapInt(this, STATUS, s, s | NORMAL)) { - if ((s & SIGNAL) != 0) - synchronized (this) { notifyAll(); } - return NORMAL; - } - } + if (completed) + s = setCompletion(NORMAL); } return s; } /** - * Tries to set SIGNAL status. Used by ForkJoinPool. Other - * variants are directly incorporated into externalAwaitDone etc. + * Tries to set SIGNAL status unless already completed. Used by + * ForkJoinPool. Other variants are directly incorporated into + * externalAwaitDone etc. * * @return true if successful */ final boolean trySetSignal() { - int s; - return U.compareAndSwapInt(this, STATUS, s = status, s | SIGNAL); + int s = status; + return s >= 0 && U.compareAndSwapInt(this, STATUS, s, s | SIGNAL); } /** @@ -328,7 +328,6 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { return s; } - /** * Implementation for join, get, quietlyJoin. Directly handles * only cases of already-completed, external wait, and @@ -417,25 +416,39 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { * @return status on exit */ private int setExceptionalCompletion(Throwable ex) { - int h = System.identityHashCode(this); - final ReentrantLock lock = exceptionTableLock; - lock.lock(); - try { - expungeStaleExceptions(); - ExceptionNode[] t = exceptionTable; - int i = h & (t.length - 1); - for (ExceptionNode e = t[i]; ; e = e.next) { - if (e == null) { - t[i] = new ExceptionNode(this, ex, t[i]); - break; + int s; + if ((s = status) >= 0) { + int h = System.identityHashCode(this); + final ReentrantLock lock = exceptionTableLock; + lock.lock(); + try { + expungeStaleExceptions(); + ExceptionNode[] t = exceptionTable; + int i = h & (t.length - 1); + for (ExceptionNode e = t[i]; ; e = e.next) { + if (e == null) { + t[i] = new ExceptionNode(this, ex, t[i]); + break; + } + if (e.get() == this) // already present + break; } - if (e.get() == this) // already present - break; + } finally { + lock.unlock(); } - } finally { - lock.unlock(); + s = setCompletion(EXCEPTIONAL); } - return setCompletion(EXCEPTIONAL); + ForkJoinTask<?> p = internalGetCompleter(); // propagate + if (p != null && p.status >= 0) + p.setExceptionalCompletion(ex); + return s; + } + + /** + * Exception propagation support for tasks with completers. + */ + ForkJoinTask<?> internalGetCompleter() { + return null; } /** @@ -517,7 +530,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { Throwable ex; if (e == null || (ex = e.ex) == null) return null; - if (e.thrower != Thread.currentThread().getId()) { + if (false && e.thrower != Thread.currentThread().getId()) { Class<? extends Throwable> ec = ex.getClass(); try { Constructor<?> noArgCtor = null; @@ -907,6 +920,18 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { } /** + * Completes this task normally without setting a value. The most + * recent value established by {@link #setRawResult} (or {@code + * null} by default) will be returned as the result of subsequent + * invocations of {@code join} and related operations. + * + * @since 1.8 + */ + public final void quietlyComplete() { + setCompletion(NORMAL); + } + + /** * Waits if necessary for the computation to complete, and then * retrieves its result. * @@ -1225,15 +1250,18 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { protected abstract void setRawResult(V value); /** - * Immediately performs the base action of this task. This method - * is designed to support extensions, and should not in general be - * called otherwise. The return value controls whether this task - * is considered to be done normally. It may return false in + * Immediately performs the base action of this task and returns + * true if, upon return from this method, this task is guaranteed + * to have completed normally. This method may return false + * otherwise, to indicate that this task is not necessarily + * complete (or is not known to be complete), for example in * asynchronous actions that require explicit invocations of - * {@link #complete} to become joinable. It may also throw an - * (unchecked) exception to indicate abnormal exit. + * completion methods. This method may also throw an (unchecked) + * exception to indicate abnormal exit. This method is designed to + * support extensions, and should not in general be called + * otherwise. * - * @return {@code true} if completed normally + * @return {@code true} if this task is known to have completed normally */ protected abstract boolean exec(); @@ -1302,44 +1330,53 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { return wt.pool.nextTaskFor(wt.workQueue); } - // Mark-bit operations + // tag operations /** - * Returns true if this task is marked. + * Returns the tag for this task. * - * @return true if this task is marked + * @return the tag for this task * @since 1.8 */ - public final boolean isMarkedForkJoinTask() { - return (status & MARKED) != 0; + public final short getForkJoinTaskTag() { + return (short)status; } /** - * Atomically sets the mark on this task. + * Atomically sets the tag value for this task. * - * @return true if this task was previously unmarked + * @param tag the tag value + * @return the previous value of the tag * @since 1.8 */ - public final boolean markForkJoinTask() { + public final short setForkJoinTaskTag(short tag) { for (int s;;) { - if (((s = status) & MARKED) != 0) - return false; - if (U.compareAndSwapInt(this, STATUS, s, s | MARKED)) - return true; + if (U.compareAndSwapInt(this, STATUS, s = status, + (s & ~SMASK) | (tag & SMASK))) + return (short)s; } } /** - * Atomically clears the mark on this task. + * Atomically conditionally sets the tag value for this task. + * Among other applications, tags can be used as visit markers + * in tasks operating on graphs, as in methods that check: {@code + * if (task.compareAndSetForkJoinTaskTag((short)0, (short)1))} + * before processing, otherwise exiting because the node has + * already been visited. * - * @return true if this task was previously marked + * @param e the expected tag value + * @param tag the new tag value + * @return true if successful; i.e., the current value was + * equal to e and is now tag. * @since 1.8 */ - public final boolean unmarkForkJoinTask() { + public final boolean compareAndSetForkJoinTaskTag(short e, short tag) { for (int s;;) { - if (((s = status) & MARKED) == 0) + if ((short)(s = status) != e) return false; - if (U.compareAndSwapInt(this, STATUS, s, s & ~MARKED)) + if (U.compareAndSwapInt(this, STATUS, s, + (s & ~SMASK) | (tag & SMASK))) return true; } } diff --git a/src/library/scala/collection/SeqExtractors.scala b/src/library/scala/collection/SeqExtractors.scala index cb3cb27f18..cbb09a0a90 100644 --- a/src/library/scala/collection/SeqExtractors.scala +++ b/src/library/scala/collection/SeqExtractors.scala @@ -19,3 +19,6 @@ object :+ { if(t.isEmpty) None else Some(t.init -> t.last) } + +// Dummy to fool ant +private abstract class SeqExtractors
\ No newline at end of file diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 0d76c23c25..9aaf05dbd6 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -18,6 +18,7 @@ import java.{ lang => jl } import java.util.concurrent.atomic.{ AtomicReferenceFieldUpdater, AtomicInteger, AtomicBoolean } import scala.concurrent.util.Duration +import scala.concurrent.impl.NonFatal import scala.Option import scala.annotation.tailrec @@ -117,7 +118,7 @@ trait Future[+T] extends Awaitable[T] { case Right(v) => // do nothing } - /** When this future is completed, either through an exception, a timeout, or a value, + /** When this future is completed, either through an exception, or a value, * apply the provided function. * * If the future has already been completed, @@ -204,7 +205,7 @@ trait Future[+T] extends Awaitable[T] { case Right(v) => try p success f(v) catch { - case t => p complete resolver(t) + case NonFatal(t) => p complete resolver(t) } } @@ -230,7 +231,7 @@ trait Future[+T] extends Awaitable[T] { case Right(v) => p success v } } catch { - case t: Throwable => p complete resolver(t) + case NonFatal(t) => p complete resolver(t) } } @@ -242,7 +243,7 @@ trait Future[+T] extends Awaitable[T] { * If the current future contains a value which satisfies the predicate, the new future will also hold that value. * Otherwise, the resulting future will fail with a `NoSuchElementException`. * - * If the current future fails or times out, the resulting future also fails or times out, respectively. + * If the current future fails, then the resulting future also fails. * * Example: * {{{ @@ -263,7 +264,7 @@ trait Future[+T] extends Awaitable[T] { if (pred(v)) p success v else p failure new NoSuchElementException("Future.filter predicate is not satisfied by: " + v) } catch { - case t: Throwable => p complete resolver(t) + case NonFatal(t) => p complete resolver(t) } } @@ -282,12 +283,12 @@ trait Future[+T] extends Awaitable[T] { // def withFilter(q: S => Boolean): FutureWithFilter[S] = new FutureWithFilter[S](self, x => p(x) && q(x)) // } - /** Creates a new future by mapping the value of the current future if the given partial function is defined at that value. + /** Creates a new future by mapping the value of the current future, if the given partial function is defined at that value. * * If the current future contains a value for which the partial function is defined, the new future will also hold that value. * Otherwise, the resulting future will fail with a `NoSuchElementException`. * - * If the current future fails or times out, the resulting future also fails or times out, respectively. + * If the current future fails, then the resulting future also fails. * * Example: * {{{ @@ -312,7 +313,7 @@ trait Future[+T] extends Awaitable[T] { if (pf.isDefinedAt(v)) p success pf(v) else p failure new NoSuchElementException("Future.collect partial function is not defined at: " + v) } catch { - case t: Throwable => p complete resolver(t) + case NonFatal(t) => p complete resolver(t) } } @@ -337,7 +338,7 @@ trait Future[+T] extends Awaitable[T] { onComplete { case Left(t) if pf isDefinedAt t => try { p success pf(t) } - catch { case t: Throwable => p complete resolver(t) } + catch { case NonFatal(t) => p complete resolver(t) } case otherwise => p complete otherwise } @@ -365,7 +366,7 @@ trait Future[+T] extends Awaitable[T] { try { p completeWith pf(t) } catch { - case t: Throwable => p complete resolver(t) + case NonFatal(t) => p complete resolver(t) } case otherwise => p complete otherwise } @@ -425,13 +426,30 @@ trait Future[+T] extends Awaitable[T] { * that conforms to `S`'s erased type or a `ClassCastException` otherwise. */ def mapTo[S](implicit m: Manifest[S]): Future[S] = { + import java.{ lang => jl } + val toBoxed = Map[Class[_], Class[_]]( + classOf[Boolean] -> classOf[jl.Boolean], + classOf[Byte] -> classOf[jl.Byte], + classOf[Char] -> classOf[jl.Character], + classOf[Short] -> classOf[jl.Short], + classOf[Int] -> classOf[jl.Integer], + classOf[Long] -> classOf[jl.Long], + classOf[Float] -> classOf[jl.Float], + classOf[Double] -> classOf[jl.Double], + classOf[Unit] -> classOf[scala.runtime.BoxedUnit] + ) + + def boxedType(c: Class[_]): Class[_] = { + if (c.isPrimitive) toBoxed(c) else c + } + val p = newPromise[S] onComplete { case l: Left[Throwable, _] => p complete l.asInstanceOf[Either[Throwable, S]] case Right(t) => p complete (try { - Right(impl.Future.boxedType(m.erasure).cast(t).asInstanceOf[S]) + Right(boxedType(m.erasure).cast(t).asInstanceOf[S]) } catch { case e: ClassCastException => Left(e) }) @@ -512,17 +530,16 @@ trait Future[+T] extends Awaitable[T] { * Note: using this method yields nondeterministic dataflow programs. */ object Future { - - /** Starts an asynchronous computation and returns a `Future` object with the result of that computation. - * - * The result becomes available once the asynchronous computation is completed. - * - * @tparam T the type of the result - * @param body the asychronous computation - * @param execctx the execution context on which the future is run - * @return the `Future` holding the result of the computation - */ - def apply[T](body: =>T)(implicit executor: ExecutionContext): Future[T] = impl.Future(body) + /** Starts an asynchronous computation and returns a `Future` object with the result of that computation. + * + * The result becomes available once the asynchronous computation is completed. + * + * @tparam T the type of the result + * @param body the asychronous computation + * @param execctx the execution context on which the future is run + * @return the `Future` holding the result of the computation + */ + def apply[T](body: =>T)(implicit execctx: ExecutionContext): Future[T] = impl.Future(body) import scala.collection.mutable.Builder import scala.collection.generic.CanBuildFrom @@ -615,4 +632,3 @@ object Future { - diff --git a/src/library/scala/concurrent/ManagedBlocker.scala b/src/library/scala/concurrent/ManagedBlocker.scala index 0b6d82e76f..9c6f4d51d6 100644 --- a/src/library/scala/concurrent/ManagedBlocker.scala +++ b/src/library/scala/concurrent/ManagedBlocker.scala @@ -12,7 +12,6 @@ package scala.concurrent * * @author Philipp Haller */ -@deprecated("Not used.", "2.10.0") trait ManagedBlocker { /** diff --git a/src/library/scala/concurrent/impl/AbstractPromise.java b/src/library/scala/concurrent/impl/AbstractPromise.java index 5280d67854..8aac5de042 100644 --- a/src/library/scala/concurrent/impl/AbstractPromise.java +++ b/src/library/scala/concurrent/impl/AbstractPromise.java @@ -15,7 +15,7 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; abstract class AbstractPromise { - private volatile Object _ref = null; + private volatile Object _ref; protected final static AtomicReferenceFieldUpdater<AbstractPromise, Object> updater = - AtomicReferenceFieldUpdater.newUpdater(AbstractPromise.class, Object.class, "_ref"); -} + AtomicReferenceFieldUpdater.newUpdater(AbstractPromise.class, Object.class, "_ref"); +}
\ No newline at end of file diff --git a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala index c308a59297..ad98331241 100644 --- a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala +++ b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala @@ -93,7 +93,9 @@ private[scala] class ExecutionContextImpl(es: AnyRef) extends ExecutionContext w } def reportFailure(t: Throwable) = t match { - case e: Error => throw e // rethrow serious errors + // `Error`s are currently wrapped by `resolver`. + // Also, re-throwing `Error`s here causes an exception handling test to fail. + //case e: Error => throw e case t => t.printStackTrace() } diff --git a/src/library/scala/concurrent/impl/Future.scala b/src/library/scala/concurrent/impl/Future.scala index a3c8ed3095..548524c9fe 100644 --- a/src/library/scala/concurrent/impl/Future.scala +++ b/src/library/scala/concurrent/impl/Future.scala @@ -28,7 +28,7 @@ private[concurrent] trait Future[+T] extends scala.concurrent.Future[T] with Awa /** Tests whether this Future has been completed. */ - final def isCompleted: Boolean = value.isDefined + def isCompleted: Boolean /** The contained value of this Future. Before this Future is completed * the value will be None. After completion the value will be Some(Right(t)) @@ -42,20 +42,6 @@ private[concurrent] trait Future[+T] extends scala.concurrent.Future[T] with Awa } object Future { - import java.{ lang => jl } - - private val toBoxed = Map[Class[_], Class[_]]( - classOf[Boolean] -> classOf[jl.Boolean], - classOf[Byte] -> classOf[jl.Byte], - classOf[Char] -> classOf[jl.Character], - classOf[Short] -> classOf[jl.Short], - classOf[Int] -> classOf[jl.Integer], - classOf[Long] -> classOf[jl.Long], - classOf[Float] -> classOf[jl.Float], - classOf[Double] -> classOf[jl.Double], - classOf[Unit] -> classOf[scala.runtime.BoxedUnit] - ) - /** Wraps a block of code into an awaitable object. */ private[concurrent] def body2awaitable[T](body: =>T) = new Awaitable[T] { def ready(atMost: Duration)(implicit permit: CanAwait) = { @@ -65,19 +51,20 @@ object Future { def result(atMost: Duration)(implicit permit: CanAwait) = body } - def boxedType(c: Class[_]): Class[_] = { - if (c.isPrimitive) toBoxed(c) else c - } - def apply[T](body: =>T)(implicit executor: ExecutionContext): Future[T] = { val promise = new Promise.DefaultPromise[T]() + + //TODO: use `dispatchFuture`? executor.execute(new Runnable { def run = { promise complete { try { Right(body) } catch { - case e => scala.concurrent.resolver(e) + case NonFatal(e) => + // Commenting out reporting for now, since it produces too much output in the tests + //executor.reportFailure(e) + scala.concurrent.resolver(e) } } } @@ -107,7 +94,7 @@ object Future { private[impl] def dispatchFuture(executor: ExecutionContext, task: () => Unit, force: Boolean = false): Unit = _taskStack.get match { - case stack if (stack ne null) && !force => stack push task + case stack if (stack ne null) && !force => stack push task // FIXME we can't mix tasks aimed for different ExecutionContexts see: https://github.com/akka/akka/blob/v2.0.1/akka-actor/src/main/scala/akka/dispatch/Future.scala#L373 case _ => executor.execute(new Runnable { def run() { try { @@ -115,13 +102,7 @@ object Future { _taskStack set taskStack while (taskStack.nonEmpty) { val next = taskStack.pop() - try { - next.apply() - } catch { - case e => - // TODO catching all and continue isn't good for OOME - executor.reportFailure(e) - } + try next() catch { case NonFatal(e) => executor reportFailure e } } } finally { _taskStack.remove() diff --git a/src/library/scala/concurrent/impl/NonFatal.scala b/src/library/scala/concurrent/impl/NonFatal.scala new file mode 100644 index 0000000000..bc509e664c --- /dev/null +++ b/src/library/scala/concurrent/impl/NonFatal.scala @@ -0,0 +1,37 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.concurrent +package impl + +/** + * Extractor of non-fatal Throwables. Will not match fatal errors + * like VirtualMachineError (OutOfMemoryError) + * ThreadDeath, LinkageError and InterruptedException. + * StackOverflowError is matched, i.e. considered non-fatal. + * + * Usage to catch all harmless throwables: + * {{{ + * try { + * // dangerous stuff + * } catch { + * case NonFatal(e) => log.error(e, "Something not that bad") + * } + * }}} + */ +private[concurrent] object NonFatal { + + def unapply(t: Throwable): Option[Throwable] = t match { + case e: StackOverflowError ⇒ Some(e) // StackOverflowError ok even though it is a VirtualMachineError + // VirtualMachineError includes OutOfMemoryError and other fatal errors + case _: VirtualMachineError | _: ThreadDeath | _: InterruptedException | _: LinkageError ⇒ None + case e ⇒ Some(e) + } + +} + diff --git a/src/library/scala/concurrent/impl/Promise.scala b/src/library/scala/concurrent/impl/Promise.scala index 07b6d1f278..ee1841aaff 100644 --- a/src/library/scala/concurrent/impl/Promise.scala +++ b/src/library/scala/concurrent/impl/Promise.scala @@ -74,37 +74,10 @@ private[concurrent] trait Promise[T] extends scala.concurrent.Promise[T] with Fu object Promise { - - def dur2long(dur: Duration): Long = if (dur.isFinite) dur.toNanos else Long.MaxValue - - def EmptyPending[T](): FState[T] = emptyPendingValue.asInstanceOf[FState[T]] - - /** Represents the internal state. - * - * [adriaan] it's unsound to make FState covariant (tryComplete won't type check) - */ - sealed trait FState[T] { def value: Option[Either[Throwable, T]] } - - case class Pending[T](listeners: List[Either[Throwable, T] => Any] = Nil) extends FState[T] { - def value: Option[Either[Throwable, T]] = None - } - - case class Success[T](value: Option[Either[Throwable, T]] = None) extends FState[T] { - def result: T = value.get.right.get - } - - case class Failure[T](value: Option[Either[Throwable, T]] = None) extends FState[T] { - def exception: Throwable = value.get.left.get - } - - private val emptyPendingValue = Pending[Nothing](Nil) - /** Default promise implementation. */ - class DefaultPromise[T](implicit val executor: ExecutionContext) extends AbstractPromise with Promise[T] { - self => - - updater.set(this, Promise.EmptyPending()) + class DefaultPromise[T](implicit val executor: ExecutionContext) extends AbstractPromise with Promise[T] { self => + updater.set(this, Nil) // Start at "No callbacks" //FIXME switch to Unsafe instead of ARFU protected final def tryAwait(atMost: Duration): Boolean = { @tailrec @@ -115,7 +88,7 @@ object Promise { val start = System.nanoTime() try { synchronized { - while (value.isEmpty) wait(ms, ns) + while (!isCompleted) wait(ms, ns) } } catch { case e: InterruptedException => @@ -123,93 +96,91 @@ object Promise { awaitUnsafe(waitTimeNanos - (System.nanoTime() - start)) } else - value.isDefined + isCompleted } - - blocking(Future.body2awaitable(awaitUnsafe(dur2long(atMost))), atMost) + //FIXME do not do this if there'll be no waiting + blocking(Future.body2awaitable(awaitUnsafe(if (atMost.isFinite) atMost.toNanos else Long.MaxValue)), atMost) } + @throws(classOf[TimeoutException]) def ready(atMost: Duration)(implicit permit: CanAwait): this.type = - if (value.isDefined || tryAwait(atMost)) this + if (isCompleted || tryAwait(atMost)) this else throw new TimeoutException("Futures timed out after [" + atMost.toMillis + "] milliseconds") + @throws(classOf[Exception]) def result(atMost: Duration)(implicit permit: CanAwait): T = ready(atMost).value.get match { case Left(e) => throw e case Right(r) => r } - def value: Option[Either[Throwable, T]] = getState.value + def value: Option[Either[Throwable, T]] = getState match { + case c: Either[_, _] => Some(c.asInstanceOf[Either[Throwable, T]]) + case _ => None + } + + override def isCompleted(): Boolean = getState match { // Cheaper than boxing result into Option due to "def value" + case _: Either[_, _] => true + case _ => false + } @inline - private[this] final def updater = AbstractPromise.updater.asInstanceOf[AtomicReferenceFieldUpdater[AbstractPromise, FState[T]]] + private[this] final def updater = AbstractPromise.updater.asInstanceOf[AtomicReferenceFieldUpdater[AbstractPromise, AnyRef]] @inline - protected final def updateState(oldState: FState[T], newState: FState[T]): Boolean = updater.compareAndSet(this, oldState, newState) + protected final def updateState(oldState: AnyRef, newState: AnyRef): Boolean = updater.compareAndSet(this, oldState, newState) @inline - protected final def getState: FState[T] = updater.get(this) + protected final def getState: AnyRef = updater.get(this) def tryComplete(value: Either[Throwable, T]): Boolean = { - val callbacks: List[Either[Throwable, T] => Any] = { + val callbacks: List[Either[Throwable, T] => Unit] = { try { @tailrec - def tryComplete(v: Either[Throwable, T]): List[Either[Throwable, T] => Any] = { + def tryComplete(v: Either[Throwable, T]): List[Either[Throwable, T] => Unit] = { getState match { - case cur @ Pending(listeners) => - val newState = - if (v.isLeft) Failure(Some(v.asInstanceOf[Left[Throwable, T]])) - else Success(Some(v.asInstanceOf[Right[Throwable, T]])) - - if (updateState(cur, newState)) listeners - else tryComplete(v) + case raw: List[_] => + val cur = raw.asInstanceOf[List[Either[Throwable, T] => Unit]] + if (updateState(cur, v)) cur else tryComplete(v) case _ => null } } tryComplete(resolveEither(value)) } finally { - synchronized { notifyAll() } // notify any blockers from `tryAwait` + synchronized { notifyAll() } //Notify any evil blockers } } callbacks match { case null => false case cs if cs.isEmpty => true - case cs => - Future.dispatchFuture(executor, { - () => cs.foreach(f => notifyCompleted(f, value)) - }) - true + case cs => Future.dispatchFuture(executor, () => cs.foreach(f => notifyCompleted(f, value))); true } } def onComplete[U](func: Either[Throwable, T] => U): this.type = { - @tailrec // Returns whether the future has already been completed or not - def tryAddCallback(): Boolean = { + @tailrec //Returns the future's results if it has already been completed, or null otherwise. + def tryAddCallback(): Either[Throwable, T] = { val cur = getState cur match { - case _: Success[_] | _: Failure[_] => true - case p: Pending[_] => - val pt = p.asInstanceOf[Pending[T]] - if (updateState(pt, pt.copy(listeners = func :: pt.listeners))) false else tryAddCallback() + case r: Either[_, _] => r.asInstanceOf[Either[Throwable, T]] + case listeners: List[_] => if (updateState(listeners, func :: listeners)) null else tryAddCallback() } } - if (tryAddCallback()) { - val result = value.get - Future.dispatchFuture(executor, { - () => notifyCompleted(func, result) - }) + tryAddCallback() match { + case null => this + case completed => + Future.dispatchFuture(executor, () => notifyCompleted(func, completed)) + this } - - this } private final def notifyCompleted(func: Either[Throwable, T] => Any, result: Either[Throwable, T]) { try { func(result) } catch { - case e => executor.reportFailure(e) + case NonFatal(e) => executor reportFailure e } } } @@ -222,13 +193,13 @@ object Promise { val value = Some(resolveEither(suppliedValue)) + override def isCompleted(): Boolean = true + def tryComplete(value: Either[Throwable, T]): Boolean = false def onComplete[U](func: Either[Throwable, T] => U): this.type = { val completedAs = value.get - Future.dispatchFuture(executor, { - () => func(completedAs) - }) + Future.dispatchFuture(executor, () => func(completedAs)) this } @@ -241,19 +212,3 @@ object Promise { } } - - - - - - - - - - - - - - - - diff --git a/src/library/scala/concurrent/impl/Unsafe.java b/src/library/scala/concurrent/impl/Unsafe.java new file mode 100644 index 0000000000..21f7e638e5 --- /dev/null +++ b/src/library/scala/concurrent/impl/Unsafe.java @@ -0,0 +1,32 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + + +package scala.concurrent; + +import java.lang.reflect.Field; + +final class Unsafe { + public final static sun.misc.Unsafe instance; + static { + try { + sun.misc.Unsafe found = null; + for(Field field : sun.misc.Unsafe.class.getDeclaredFields()) { + if (field.getType() == sun.misc.Unsafe.class) { + field.setAccessible(true); + found = (sun.misc.Unsafe) field.get(null); + break; + } + } + if (found == null) throw new IllegalStateException("Can't find instance of sun.misc.Unsafe"); + else instance = found; + } catch(Throwable t) { + throw new ExceptionInInitializerError(t); + } + } +} diff --git a/src/library/scala/concurrent/util/Duration.scala b/src/library/scala/concurrent/util/Duration.scala index 15a546de10..c4e5fa491a 100644 --- a/src/library/scala/concurrent/util/Duration.scala +++ b/src/library/scala/concurrent/util/Duration.scala @@ -297,7 +297,7 @@ class FiniteDuration(val length: Long, val unit: TimeUnit) extends Duration { def toMinutes = unit.toMinutes(length) def toHours = unit.toHours(length) def toDays = unit.toDays(length) - def toUnit(u: TimeUnit) = long2double(toNanos) / NANOSECONDS.convert(1, u) + def toUnit(u: TimeUnit) = toNanos.toDouble / NANOSECONDS.convert(1, u) override def toString = this match { case Duration(1, DAYS) ⇒ "1 day" @@ -341,11 +341,11 @@ class FiniteDuration(val length: Long, val unit: TimeUnit) extends Duration { } } - def *(factor: Double) = fromNanos(long2double(toNanos) * factor) + def *(factor: Double) = fromNanos(toNanos.toDouble * factor) - def /(factor: Double) = fromNanos(long2double(toNanos) / factor) + def /(factor: Double) = fromNanos(toNanos.toDouble / factor) - def /(other: Duration) = if (other.finite_?) long2double(toNanos) / other.toNanos else 0 + def /(other: Duration) = if (other.finite_?) toNanos.toDouble / other.toNanos else 0 def unary_- = Duration(-length, unit) diff --git a/src/library/scala/concurrent/util/duration/package.scala b/src/library/scala/concurrent/util/duration/package.scala index 25625054ee..e3cf229c61 100644 --- a/src/library/scala/concurrent/util/duration/package.scala +++ b/src/library/scala/concurrent/util/duration/package.scala @@ -1,6 +1,7 @@ package scala.concurrent.util import java.util.concurrent.TimeUnit +import language.implicitConversions package object duration { @@ -27,4 +28,4 @@ package object duration { implicit def intMult(i: Int) = new IntMult(i) implicit def longMult(l: Long) = new LongMult(l) implicit def doubleMult(f: Double) = new DoubleMult(f) -}
\ No newline at end of file +} diff --git a/src/library/scala/language.scala b/src/library/scala/language.scala index 907adb5f72..2837187d48 100644 --- a/src/library/scala/language.scala +++ b/src/library/scala/language.scala @@ -4,22 +4,121 @@ object language { import languageFeature._ + /** Where enabled, direct or indirect subclasses of trait scala.Dynamic can + * be defined. Unless dynamics is enabled, a definition of a class, trait, + * or object that has Dynamic as a base trait is rejected. Dynamic member + * selection of existing subclasses of trait Dynamic are unaffected; + * they can be used anywhere. + * + * _Why introduce the feature?_ To enable flexible DSLs and convenient interfacing + * with dynamic languages. + * + * _Why control it?_ Dynamic member selection can undermine static checkability + * of programs. Furthermore, dynamic member selection often relies on reflection, + * which is not available on all platforms. + */ implicit val dynamics: dynamics = ??? + /** Only where enabled, postfix operator notation `(expr op)` will be allowed. + * + * _Why keep the feature?_ Several DSLs written in Scala need the notation. + * + * _Why control it?_ Postfix operators interact poorly with semicolon inference. + * Most programmers avoid them for this reason. + */ implicit val postfixOps: postfixOps = ??? + /** Only where enabled, accesses to members of structural types that need + * reflection are supported. Reminder: A structural type is a type of the form + * `Parents { Decls }` where `Decls` contains declarations of new members that do + * not override any member in `Parents`. To access one of these members, a + * reflective call is needed. + * + * _Why keep the feature?_ Structural types provide great flexibility because + * they avoid the need to define inheritance hierarchies a priori. Besides, + * their definition falls out quite naturally from Scala’s concept of type refinement. + * + * _Why control it?+ Reflection is not available on all platforms. Popular tools + * such as ProGuard have problems dealing with it. Even where reflection is available, + * reflective dispatch can lead to surprising performance degradations. + */ implicit val reflectiveCalls: reflectiveCalls = ??? + /** Only where enabled, definitions of implicit conversions are allowed. An + * implicit conversion is an implicit value of unary function type `A => B`, + * or an implicit method that has in its first parameter section a single, + * non-implicit parameter. Examples: + * + * implicit def stringToInt(s: String): Int = s.length + * implicit val conv = (s: String) => s.length + * implicit def listToX(xs: List[T])(implicit f: T => X): X = … + * + * Implicit values of other types are not affected, and neither are implicit + * classes. + * + * _Why keep the feature?_ Implicit conversions are central to many aspects + * of Scala’s core libraries. + * + * _Why control it?_ Implicit conversions are known to cause many pitfalls + * if over-used. And there is a tendency to over-use them because they look + * very powerful and their effects seem to be easy to understand. Also, in + * most situations using implicit parameters leads to a better design than + * implicit conversions. + */ implicit val implicitConversions: implicitConversions = ??? + /** Only where this flag is enabled, higher-kinded types can be written. + * + * _Why keep the feature?_ Higher-kinded types enable the definition of very general + * abstractions such as functor, monad, or arrow. A significant set of advanced + * libraries relies on them. Higher-kinded types are also at the core of the + * scala-virtualized effort to produce high-performance parallel DSLs through staging. + * + * _Why control it?_ Higher kinded types in Scala lead to a Turing-complete + * type system, where compiler termination is no longer guaranteed. They tend + * to be useful mostly for type-level computation and for highly generic design + * patterns. The level of abstraction implied by these design patterns is often + * a barrier to understanding for newcomers to a Scala codebase. Some syntactic + * aspects of higher-kinded types are hard to understand for the uninitiated and + * type inference is less effective for them than for normal types. Because we are + * not completely happy with them yet, it is possible that some aspects of + * higher-kinded types will change in future versions of Scala. So an explicit + * enabling also serves as a warning that code involving higher-kinded types + * might have to be slightly revised in the future. + */ implicit val higherKinds: higherKinds = ??? + /** Only where enabled, existential types that cannot be expressed as wildcard + * types can be written and are allowed in inferred types of values or return + * types of methods. Existential types with wildcard type syntax such as `List[_]`, + * or `Map[String, _]` are not affected. + * + * _Why keep the feature?_ Existential types are needed to make sense of Java’s wildcard + * types and raw types and the erased types of run-time values. + * + * Why control it? Having complex existential types in a code base usually makes + * application code very brittle, with a tendency to produce type errors with + * obscure error messages. Therefore, going overboard with existential types + * is generally perceived not to be a good idea. Also, complicated existential types + * might be no longer supported in a future simplification of the language. + */ implicit val existentials: existentials = ??? object experimental { import languageFeature.experimental._ + /** Where enabled, macro definitions are allowed. Macro implementations and + * macro applications are unaffected; they can be used anywhere. + * + * _Why introduce the feature?_ Macros promise to make the language more regular, + * replacing ad-hoc language constructs with a general powerful abstraction + * capability that can express them. Macros are also a more disciplined and + * powerful replacement for compiler plugins. + * + * _Why control it?_ For their very power, macros can lead to code that is hard + * to debug and understand. + */ implicit val macros: macros = ??? } } diff --git a/src/library/scala/reflect/ReflectionUtils.scala b/src/library/scala/reflect/ReflectionUtils.scala index 1be46eac55..79a42f6ec4 100644 --- a/src/library/scala/reflect/ReflectionUtils.scala +++ b/src/library/scala/reflect/ReflectionUtils.scala @@ -27,6 +27,28 @@ object ReflectionUtils { case ex if pf isDefinedAt unwrapThrowable(ex) => pf(unwrapThrowable(ex)) } + private def systemProperties: Iterator[(String, String)] = { + import scala.collection.JavaConverters._ + System.getProperties.asScala.iterator + } + + private def searchForBootClasspath = ( + systemProperties find (_._1 endsWith ".boot.class.path") map (_._2) getOrElse "" + ) + + def show(cl: ClassLoader) = { + def inferClasspath(cl: ClassLoader) = cl match { + case cl: java.net.URLClassLoader => "[" + (cl.getURLs mkString ",") + "]" + case _ => "<unknown>" + } + cl match { + case cl if cl != null => + "%s of type %s with classpath %s".format(cl, cl.getClass, inferClasspath(cl)) + case null => + "primordial classloader with boot classpath [%s]".format(searchForBootClasspath) + } + } + def defaultReflectionClassLoader() = { // say no to non-determinism of mirror classloaders // default classloader will be instantiated using current system classloader diff --git a/src/library/scala/reflect/api/Attachment.scala b/src/library/scala/reflect/api/Attachment.scala index dfd362ebe0..9fa5ceb0fb 100644 --- a/src/library/scala/reflect/api/Attachment.scala +++ b/src/library/scala/reflect/api/Attachment.scala @@ -7,10 +7,18 @@ package api * Attachments have to carry positions, because we don't want to introduce even a single additional field in Tree * imposing an unnecessary memory tax because of something that will not be used in most cases. */ +// [Eugene] with the introduction of `attach` and `payload[T]` users don't need to create custom attachments anymore +// however, we cannot move attachments to scala.reflect.internal, because they are used in Trees, which are implemented completely in scala.reflect.api trait Attachment { /** Gets the underlying position */ def pos: Position /** Creates a copy of this attachment with its position updated */ - def withPos(pos: Position): Attachment + def withPos(newPos: Position): Attachment + + /** Gets the underlying payload */ + def payload: Any + + /** Creates a copy of this attachment with its payload updated */ + def withPayload(newPayload: Any): Attachment } diff --git a/src/library/scala/reflect/api/StandardNames.scala b/src/library/scala/reflect/api/StandardNames.scala index d2110ede75..d39d44dd86 100644 --- a/src/library/scala/reflect/api/StandardNames.scala +++ b/src/library/scala/reflect/api/StandardNames.scala @@ -44,6 +44,7 @@ trait StandardNames { self: Universe => val MIRROR_FREE_PREFIX: TermName val MIRROR_FREE_THIS_SUFFIX: TermName val MIRROR_FREE_VALUE_SUFFIX: TermName + val MIRROR_SYMDEF_PREFIX: TermName val MIXIN_CONSTRUCTOR: TermName val MODULE_INSTANCE_FIELD: TermName val OUTER: TermName diff --git a/src/library/scala/reflect/api/Symbols.scala b/src/library/scala/reflect/api/Symbols.scala index a154e5f7a0..e47bc7216e 100755 --- a/src/library/scala/reflect/api/Symbols.scala +++ b/src/library/scala/reflect/api/Symbols.scala @@ -173,6 +173,11 @@ trait Symbols { self: Universe => */ def isSkolem : Boolean + /** Does this symbol represent an existentially bound type? + * If yes, `isType` is also guaranteed to be true. + */ + def isExistential : Boolean + /** Does this symbol represent a free type captured by reification? */ // needed for ones who wish to inspect reified trees diff --git a/src/library/scala/reflect/api/Trees.scala b/src/library/scala/reflect/api/Trees.scala index d659a9e9eb..7548a6bdc0 100644 --- a/src/library/scala/reflect/api/Trees.scala +++ b/src/library/scala/reflect/api/Trees.scala @@ -85,10 +85,18 @@ trait Trees { self: Universe => def pos_=(pos: Position): Unit = rawatt = (rawatt withPos pos) // the "withPos" part is crucial to robustness def setPos(newpos: Position): this.type = { pos = newpos; this } - private[this] var rawatt: Attachment = NoPosition - def attachment: Attachment = rawatt - def attachment_=(att: Attachment): Unit = rawatt = att - def setAttachment(att: Attachment): this.type = { rawatt = att; this } + private var rawatt: Attachment = NoPosition + private case class NontrivialAttachment(pos: api.Position, payload: Any) extends Attachment { + def withPos(newPos: api.Position) = copy(pos = newPos, payload = payload) + def withPayload(newPayload: Any) = copy(pos = pos, payload = newPayload) + } + // todo. annotate T with ClassTag and make pattern matcher use it + // todo. support multiple attachments, and remove the assignment. only leave attach/detach +// def attachment[T]: T = rawatt.payload.asInstanceOf[T] +// def attachmentOpt[T]: Option[T] = try { Some(rawatt.payload.asInstanceOf[T]) } catch { case _: Throwable => None } + def attachment: Any = rawatt.payload + def attachment_=(att: Any): Unit = rawatt = NontrivialAttachment(pos, att) + def setAttachment(att: Any): this.type = { attachment = att; this } private[this] var rawtpe: Type = _ @@ -238,7 +246,7 @@ trait Trees { self: Universe => duplicateTree(this).asInstanceOf[this.type] private[scala] def copyAttrs(tree: Tree): this.type = { - attachment = tree.attachment + rawatt = tree.rawatt tpe = tree.tpe if (hasSymbol) symbol = tree.symbol this diff --git a/src/library/scala/reflect/api/TypeTags.scala b/src/library/scala/reflect/api/TypeTags.scala index 4ffabe1c36..59a7c87f44 100644 --- a/src/library/scala/reflect/api/TypeTags.scala +++ b/src/library/scala/reflect/api/TypeTags.scala @@ -61,9 +61,8 @@ trait TypeTags { self: Universe => // assert(tpe != null) def sym = tpe.typeSymbol - - def isConcrete = !isNotConcrete - def isNotConcrete = tpe exists (_.typeSymbol.isAbstractType) + def isConcrete = tpe.isConcrete + def notConcrete = !isConcrete def toConcrete: ConcreteTypeTag[T] = ConcreteTypeTag[T](tpe) override def toString = { @@ -124,7 +123,7 @@ trait TypeTags { self: Universe => // it's unsafe to use assert here, because we might run into deadlocks with Predef // also see comments in ClassTags.scala //assert(isConcrete, tpe) - if (isNotConcrete) throw new Error("%s (%s) is not concrete and cannot be used to construct a concrete type tag".format(tpe, tpe.kind)) + if (notConcrete) throw new Error("%s (%s) is not concrete and cannot be used to construct a concrete type tag".format(tpe, tpe.kind)) override def productPrefix = "ConcreteTypeTag" } diff --git a/src/library/scala/reflect/api/Types.scala b/src/library/scala/reflect/api/Types.scala index 12aad453b1..5e1c1af2fe 100755 --- a/src/library/scala/reflect/api/Types.scala +++ b/src/library/scala/reflect/api/Types.scala @@ -57,6 +57,10 @@ trait Types { self: Universe => */ def isHigherKinded: Boolean // !!! This should be called "isTypeConstructor", no? + /** Does this type refer to abstract types or is an abstract type? + */ + def isConcrete: Boolean + /** * Expands type aliases and converts higher-kinded TypeRefs to PolyTypes. * Functions on types are also implemented as PolyTypes. diff --git a/src/library/scala/reflect/package.scala b/src/library/scala/reflect/package.scala index 1738642932..6f40a3ac3e 100644 --- a/src/library/scala/reflect/package.scala +++ b/src/library/scala/reflect/package.scala @@ -3,6 +3,7 @@ package scala package object reflect { import ReflectionUtils._ + import scala.compat.Platform.EOL // !!! This was a val; we can't throw exceptions that aggressively without breaking // non-standard environments, e.g. google app engine. I made it a lazy val, but @@ -12,15 +13,69 @@ package object reflect { // todo. default mirror (a static object) might become a source for memory leaks (because it holds a strong reference to a classloader)! lazy val mirror: api.Mirror = mkMirror(defaultReflectionClassLoader) + private def mirrorDiagnostics(cl: ClassLoader): String = """ + | + | This error has happened because `scala.reflect.runtime.package` located in + | scala-compiler.jar cannot be loaded. Classloader you are using is: + | %s. + | + | In Scala 2.10.0 M3, scala-compiler.jar is required to be on the classpath + | for manifests and type tags to function. This will change in the final release, + | but for now you need to adjust your scripts or build system to proceed. + | Here are the instructions for some of the situations that might be relevant. + | + | If you compile your application directly from the command line + | or a hand-rolled script, this is a bug. Please, report it. + | + | If you compile your application with Maven using the maven-scala plugin, + | set its "fork" configuration entry to "false: + | + | <plugin> + | <groupId>org.scala-tools</groupId> + | <artifactId>maven-scala-plugin</artifactId> + | <version>2.15.0</version> + | <executions> + | <execution> + | <goals> + | ... + | </goals> + | <configuration> + | <fork>false</fork> + | ... + | </configuration> + | </execution> + | </executions> + | </plugin> + | + | If you compile your application with SBT, + | <to be implemented: release SBT for 2.10.0 M3> + | + | If you compile your application in Scala IDE, + | <to be implemented: release Scala IDE for 2.10.0 M3>. + | + | If you launch your application directly from the command line + | or a hand-rolled script, add `scala-compiler.jar` to the classpath: + | + | scalac HelloWorld.scala + | scala HelloWorld -cp path/to/scala-compiler.jar + | + | If you launch your application with Maven using the maven-scala plugin, + | set its "fork" configuration entry to "false as shown above. + | + | If you launch your application with SBT, make sure that you use + | <to be implemented: release SBT for 2.10.0 M3> + | + | If you launch your application in Scala IDE, make sure that both scala-library.jar and scala-compiler.jar + | are in bootstrap entries on the classpath of your launch configuration. + """.stripMargin('|').format(show(cl)) + def mkMirror(classLoader: ClassLoader): api.Mirror = { - // we use (Java) reflection here so that we can keep reflect.runtime and reflect.internals in a seperate jar - // note that we must instantiate the mirror with current classloader, otherwise we won't be able to cast it to api.Mirror - // that's not a problem, though, because mirror can service classes from arbitrary classloaders - val instance = invokeFactoryOpt(getClass.getClassLoader, "scala.reflect.runtime.package", "mkMirror", classLoader) + val coreClassLoader = getClass.getClassLoader + val instance = invokeFactoryOpt(coreClassLoader, "scala.reflect.runtime.package", "mkMirror", classLoader) instance match { case Some(x: api.Mirror) => x - case Some(_) => throw new UnsupportedOperationException("Available scala reflection implementation is incompatible with this interface") - case None => throw new UnsupportedOperationException("Scala reflection not available on this platform") + case Some(_) => throw new UnsupportedOperationException("Available scala reflection implementation is incompatible with this interface." + mirrorDiagnostics(coreClassLoader)) + case None => throw new UnsupportedOperationException("Scala reflection not available on this platform." + mirrorDiagnostics(coreClassLoader)) } } diff --git a/src/library/scala/xml/factory/LoggedNodeFactory.scala b/src/library/scala/xml/factory/LoggedNodeFactory.scala index abf8f97f03..45ba9530e1 100644 --- a/src/library/scala/xml/factory/LoggedNodeFactory.scala +++ b/src/library/scala/xml/factory/LoggedNodeFactory.scala @@ -12,7 +12,7 @@ package factory /** This class logs what the nodefactory is actually doing. * If you want to see what happens during loading, use it like this: {{{ -object testLogged extends Application { +object testLogged extends App { val x = new scala.xml.parsing.NoBindingFactoryAdapter with scala.xml.factory.LoggedNodeFactory[scala.xml.Elem] with scala.util.logging.ConsoleLogger diff --git a/test/disabled/neg/macro-keyword-bind.check b/test/disabled/neg/macro-keyword-bind.check deleted file mode 100644 index 1f74cfe5cd..0000000000 --- a/test/disabled/neg/macro-keyword-bind.check +++ /dev/null @@ -1,7 +0,0 @@ -macro-keyword-bind.scala:2: error: illegal start of simple pattern
- val Some(macro) = Some(42)
- ^
-macro-keyword-bind.scala:6: error: ')' expected but '}' found.
-}
-^
-two errors found
diff --git a/test/disabled/neg/macro-keyword-bind.flags b/test/disabled/neg/macro-keyword-bind.flags deleted file mode 100644 index cd66464f2f..0000000000 --- a/test/disabled/neg/macro-keyword-bind.flags +++ /dev/null @@ -1 +0,0 @@ --language:experimental.macros
\ No newline at end of file diff --git a/test/disabled/neg/macro-keyword-bind.scala b/test/disabled/neg/macro-keyword-bind.scala deleted file mode 100644 index a3b1553348..0000000000 --- a/test/disabled/neg/macro-keyword-bind.scala +++ /dev/null @@ -1,6 +0,0 @@ -object Test12 { - val Some(macro) = Some(42) - macro match { - case macro => println(macro) - } -}
\ No newline at end of file diff --git a/test/disabled/neg/macro-keyword-class1.check b/test/disabled/neg/macro-keyword-class1.check deleted file mode 100644 index d8983180ef..0000000000 --- a/test/disabled/neg/macro-keyword-class1.check +++ /dev/null @@ -1,4 +0,0 @@ -macro-keyword-class1.scala:3: error: identifier expected but 'macro' found.
-class macro
- ^
-one error found
diff --git a/test/disabled/neg/macro-keyword-class1.flags b/test/disabled/neg/macro-keyword-class1.flags deleted file mode 100644 index cd66464f2f..0000000000 --- a/test/disabled/neg/macro-keyword-class1.flags +++ /dev/null @@ -1 +0,0 @@ --language:experimental.macros
\ No newline at end of file diff --git a/test/disabled/neg/macro-keyword-class1.scala b/test/disabled/neg/macro-keyword-class1.scala deleted file mode 100644 index 8635d1f4f6..0000000000 --- a/test/disabled/neg/macro-keyword-class1.scala +++ /dev/null @@ -1,3 +0,0 @@ -package test4 - -class macro diff --git a/test/disabled/neg/macro-keyword-class2.check b/test/disabled/neg/macro-keyword-class2.check deleted file mode 100644 index 0e4d11bcc4..0000000000 --- a/test/disabled/neg/macro-keyword-class2.check +++ /dev/null @@ -1,4 +0,0 @@ -macro-keyword-class2.scala:2: error: identifier expected but 'macro' found.
- class macro
- ^
-one error found
diff --git a/test/disabled/neg/macro-keyword-class2.flags b/test/disabled/neg/macro-keyword-class2.flags deleted file mode 100644 index cd66464f2f..0000000000 --- a/test/disabled/neg/macro-keyword-class2.flags +++ /dev/null @@ -1 +0,0 @@ --language:experimental.macros
\ No newline at end of file diff --git a/test/disabled/neg/macro-keyword-class2.scala b/test/disabled/neg/macro-keyword-class2.scala deleted file mode 100644 index af24a489d0..0000000000 --- a/test/disabled/neg/macro-keyword-class2.scala +++ /dev/null @@ -1,3 +0,0 @@ -object Test5 { - class macro -} diff --git a/test/disabled/neg/macro-keyword-object1.check b/test/disabled/neg/macro-keyword-object1.check deleted file mode 100644 index cfbd06ffd6..0000000000 --- a/test/disabled/neg/macro-keyword-object1.check +++ /dev/null @@ -1,4 +0,0 @@ -macro-keyword-object1.scala:3: error: identifier expected but 'macro' found.
-object macro
- ^
-one error found
diff --git a/test/disabled/neg/macro-keyword-object1.flags b/test/disabled/neg/macro-keyword-object1.flags deleted file mode 100644 index cd66464f2f..0000000000 --- a/test/disabled/neg/macro-keyword-object1.flags +++ /dev/null @@ -1 +0,0 @@ --language:experimental.macros
\ No newline at end of file diff --git a/test/disabled/neg/macro-keyword-object1.scala b/test/disabled/neg/macro-keyword-object1.scala deleted file mode 100644 index 66eb494e6b..0000000000 --- a/test/disabled/neg/macro-keyword-object1.scala +++ /dev/null @@ -1,3 +0,0 @@ -package test6 - -object macro diff --git a/test/disabled/neg/macro-keyword-object2.check b/test/disabled/neg/macro-keyword-object2.check deleted file mode 100644 index ede31f13e5..0000000000 --- a/test/disabled/neg/macro-keyword-object2.check +++ /dev/null @@ -1,4 +0,0 @@ -macro-keyword-object2.scala:2: error: identifier expected but 'macro' found.
- object macro
- ^
-one error found
diff --git a/test/disabled/neg/macro-keyword-object2.flags b/test/disabled/neg/macro-keyword-object2.flags deleted file mode 100644 index cd66464f2f..0000000000 --- a/test/disabled/neg/macro-keyword-object2.flags +++ /dev/null @@ -1 +0,0 @@ --language:experimental.macros
\ No newline at end of file diff --git a/test/disabled/neg/macro-keyword-object2.scala b/test/disabled/neg/macro-keyword-object2.scala deleted file mode 100644 index 6f5b9ceacd..0000000000 --- a/test/disabled/neg/macro-keyword-object2.scala +++ /dev/null @@ -1,3 +0,0 @@ -object Test7 { - object macro -} diff --git a/test/disabled/neg/macro-keyword-package1.check b/test/disabled/neg/macro-keyword-package1.check deleted file mode 100644 index 22c1e11ded..0000000000 --- a/test/disabled/neg/macro-keyword-package1.check +++ /dev/null @@ -1,4 +0,0 @@ -macro-keyword-package1.scala:1: error: identifier expected but 'macro' found.
-package macro
- ^
-one error found
diff --git a/test/disabled/neg/macro-keyword-package1.flags b/test/disabled/neg/macro-keyword-package1.flags deleted file mode 100644 index cd66464f2f..0000000000 --- a/test/disabled/neg/macro-keyword-package1.flags +++ /dev/null @@ -1 +0,0 @@ --language:experimental.macros
\ No newline at end of file diff --git a/test/disabled/neg/macro-keyword-package1.scala b/test/disabled/neg/macro-keyword-package1.scala deleted file mode 100644 index 52d3fbabf6..0000000000 --- a/test/disabled/neg/macro-keyword-package1.scala +++ /dev/null @@ -1,3 +0,0 @@ -package macro - -package macro.bar
\ No newline at end of file diff --git a/test/disabled/neg/macro-keyword-package2.check b/test/disabled/neg/macro-keyword-package2.check deleted file mode 100644 index 0cb542a85d..0000000000 --- a/test/disabled/neg/macro-keyword-package2.check +++ /dev/null @@ -1,4 +0,0 @@ -macro-keyword-package2.scala:3: error: identifier expected but 'macro' found.
-package macro.foo
- ^
-one error found
diff --git a/test/disabled/neg/macro-keyword-package2.flags b/test/disabled/neg/macro-keyword-package2.flags deleted file mode 100644 index cd66464f2f..0000000000 --- a/test/disabled/neg/macro-keyword-package2.flags +++ /dev/null @@ -1 +0,0 @@ --language:experimental.macros
\ No newline at end of file diff --git a/test/disabled/neg/macro-keyword-package2.scala b/test/disabled/neg/macro-keyword-package2.scala deleted file mode 100644 index a68ebd935f..0000000000 --- a/test/disabled/neg/macro-keyword-package2.scala +++ /dev/null @@ -1,3 +0,0 @@ -package foo - -package macro.foo diff --git a/test/disabled/neg/macro-keyword-trait1.check b/test/disabled/neg/macro-keyword-trait1.check deleted file mode 100644 index 9586a62e08..0000000000 --- a/test/disabled/neg/macro-keyword-trait1.check +++ /dev/null @@ -1,4 +0,0 @@ -macro-keyword-trait1.scala:3: error: identifier expected but 'macro' found.
-trait macro
- ^
-one error found
diff --git a/test/disabled/neg/macro-keyword-trait1.flags b/test/disabled/neg/macro-keyword-trait1.flags deleted file mode 100644 index cd66464f2f..0000000000 --- a/test/disabled/neg/macro-keyword-trait1.flags +++ /dev/null @@ -1 +0,0 @@ --language:experimental.macros
\ No newline at end of file diff --git a/test/disabled/neg/macro-keyword-trait1.scala b/test/disabled/neg/macro-keyword-trait1.scala deleted file mode 100644 index e32d4c1385..0000000000 --- a/test/disabled/neg/macro-keyword-trait1.scala +++ /dev/null @@ -1,3 +0,0 @@ -package test8 - -trait macro diff --git a/test/disabled/neg/macro-keyword-trait2.check b/test/disabled/neg/macro-keyword-trait2.check deleted file mode 100644 index 40aa764378..0000000000 --- a/test/disabled/neg/macro-keyword-trait2.check +++ /dev/null @@ -1,4 +0,0 @@ -macro-keyword-trait2.scala:2: error: identifier expected but 'macro' found.
- trait macro
- ^
-one error found
diff --git a/test/disabled/neg/macro-keyword-trait2.flags b/test/disabled/neg/macro-keyword-trait2.flags deleted file mode 100644 index cd66464f2f..0000000000 --- a/test/disabled/neg/macro-keyword-trait2.flags +++ /dev/null @@ -1 +0,0 @@ --language:experimental.macros
\ No newline at end of file diff --git a/test/disabled/neg/macro-keyword-trait2.scala b/test/disabled/neg/macro-keyword-trait2.scala deleted file mode 100644 index 243a54abe6..0000000000 --- a/test/disabled/neg/macro-keyword-trait2.scala +++ /dev/null @@ -1,3 +0,0 @@ -object Test9 { - trait macro -} diff --git a/test/disabled/neg/macro-keyword-type.check b/test/disabled/neg/macro-keyword-type.check deleted file mode 100644 index 4a7481114c..0000000000 --- a/test/disabled/neg/macro-keyword-type.check +++ /dev/null @@ -1,4 +0,0 @@ -macro-keyword-type.scala:2: error: identifier expected but 'macro' found.
- type macro = Int
- ^
-one error found
diff --git a/test/disabled/neg/macro-keyword-type.flags b/test/disabled/neg/macro-keyword-type.flags deleted file mode 100644 index cd66464f2f..0000000000 --- a/test/disabled/neg/macro-keyword-type.flags +++ /dev/null @@ -1 +0,0 @@ --language:experimental.macros
\ No newline at end of file diff --git a/test/disabled/neg/macro-keyword-type.scala b/test/disabled/neg/macro-keyword-type.scala deleted file mode 100644 index 30e523bcaf..0000000000 --- a/test/disabled/neg/macro-keyword-type.scala +++ /dev/null @@ -1,3 +0,0 @@ -object Test3 { - type macro = Int -}
\ No newline at end of file diff --git a/test/disabled/neg/macro-keyword-val.check b/test/disabled/neg/macro-keyword-val.check deleted file mode 100644 index 0dc4c030a9..0000000000 --- a/test/disabled/neg/macro-keyword-val.check +++ /dev/null @@ -1,7 +0,0 @@ -macro-keyword-val.scala:2: error: illegal start of simple pattern
- val macro = ???
- ^
-macro-keyword-val.scala:3: error: '=' expected but '}' found.
-}
-^
-two errors found
diff --git a/test/disabled/neg/macro-keyword-val.flags b/test/disabled/neg/macro-keyword-val.flags deleted file mode 100644 index cd66464f2f..0000000000 --- a/test/disabled/neg/macro-keyword-val.flags +++ /dev/null @@ -1 +0,0 @@ --language:experimental.macros
\ No newline at end of file diff --git a/test/disabled/neg/macro-keyword-val.scala b/test/disabled/neg/macro-keyword-val.scala deleted file mode 100644 index 96f57acb30..0000000000 --- a/test/disabled/neg/macro-keyword-val.scala +++ /dev/null @@ -1,3 +0,0 @@ -object Test1 { - val macro = ??? -}
\ No newline at end of file diff --git a/test/disabled/neg/macro-keyword-var.check b/test/disabled/neg/macro-keyword-var.check deleted file mode 100644 index 96d02e0052..0000000000 --- a/test/disabled/neg/macro-keyword-var.check +++ /dev/null @@ -1,7 +0,0 @@ -macro-keyword-var.scala:2: error: illegal start of simple pattern
- var macro = ???
- ^
-macro-keyword-var.scala:3: error: '=' expected but '}' found.
-}
-^
-two errors found
diff --git a/test/disabled/neg/macro-keyword-var.flags b/test/disabled/neg/macro-keyword-var.flags deleted file mode 100644 index cd66464f2f..0000000000 --- a/test/disabled/neg/macro-keyword-var.flags +++ /dev/null @@ -1 +0,0 @@ --language:experimental.macros
\ No newline at end of file diff --git a/test/disabled/neg/macro-keyword-var.scala b/test/disabled/neg/macro-keyword-var.scala deleted file mode 100644 index a79dda6dc2..0000000000 --- a/test/disabled/neg/macro-keyword-var.scala +++ /dev/null @@ -1,3 +0,0 @@ -object Test2 { - var macro = ??? -}
\ No newline at end of file diff --git a/test/files/jvm/interpreter.check b/test/files/jvm/interpreter.check index b9ff6afa2b..292d2fc4cb 100644 --- a/test/files/jvm/interpreter.check +++ b/test/files/jvm/interpreter.check @@ -316,9 +316,8 @@ scala> /* */ */ -scala> -scala> +You typed two blank lines. Starting a new command. scala> // multi-line string @@ -326,7 +325,7 @@ scala> """ hello there """ -res9: String = +res12: String = " hello there diff --git a/test/files/jvm/manifests.check b/test/files/jvm/manifests.check new file mode 100644 index 0000000000..be8ec2bb5b --- /dev/null +++ b/test/files/jvm/manifests.check @@ -0,0 +1,56 @@ +x=(), m=ConcreteTypeTag[Unit], k=TypeRef, s=class Unit
+x=true, m=ConcreteTypeTag[Boolean], k=TypeRef, s=class Boolean
+x=a, m=ConcreteTypeTag[Char], k=TypeRef, s=class Char
+x=1, m=ConcreteTypeTag[Int], k=TypeRef, s=class Int
+x=abc, m=ConcreteTypeTag[String], k=TypeRef, s=class String
+x='abc, m=ConcreteTypeTag[Symbol], k=TypeRef, s=class Symbol
+
+x=List(()), m=ConcreteTypeTag[List[Unit]], k=TypeRef, s=class List
+x=List(true), m=ConcreteTypeTag[List[Boolean]], k=TypeRef, s=class List
+x=List(1), m=ConcreteTypeTag[List[Int]], k=TypeRef, s=class List
+x=List(abc), m=ConcreteTypeTag[List[String]], k=TypeRef, s=class List
+x=List('abc), m=ConcreteTypeTag[List[Symbol]], k=TypeRef, s=class List
+
+x=[Z, m=ConcreteTypeTag[Array[Boolean]], k=TypeRef, s=class Array
+x=[C, m=ConcreteTypeTag[Array[Char]], k=TypeRef, s=class Array
+x=[I, m=ConcreteTypeTag[Array[Int]], k=TypeRef, s=class Array
+x=[Ljava.lang.String;, m=ConcreteTypeTag[Array[String]], k=TypeRef, s=class Array
+x=[Lscala.Symbol;, m=ConcreteTypeTag[Array[Symbol]], k=TypeRef, s=class Array
+
+x=((),()), m=ConcreteTypeTag[(Unit, Unit)], k=TypeRef, s=class Tuple2
+x=(true,false), m=ConcreteTypeTag[(Boolean, Boolean)], k=TypeRef, s=class Tuple2
+x=(1,2), m=ConcreteTypeTag[(Int, Int)], k=TypeRef, s=class Tuple2
+x=(abc,xyz), m=ConcreteTypeTag[(String, String)], k=TypeRef, s=class Tuple2
+x=('abc,'xyz), m=ConcreteTypeTag[(Symbol, Symbol)], k=TypeRef, s=class Tuple2
+
+
+x=Foo, m=ConcreteTypeTag[Foo[Int]], k=TypeRef, s=class Foo
+x=Foo, m=ConcreteTypeTag[Foo[List[Int]]], k=TypeRef, s=class Foo
+x=Foo, m=ConcreteTypeTag[Foo[Foo[Int]]], k=TypeRef, s=class Foo
+x=Foo, m=ConcreteTypeTag[Foo[List[Foo[Int]]]], k=TypeRef, s=class Foo
+
+x=Test1$$anon$1, m=ConcreteTypeTag[Bar[String]], k=RefinedType, s=<local Test1>
+x=Test1$$anon$2, m=ConcreteTypeTag[Bar[String]], k=RefinedType, s=<local Test1>
+
+()=()
+true=true
+a=a
+1=1
+'abc='abc
+
+List(())=List(())
+List(true)=List(true)
+List('abc)=List('abc)
+
+Array()=Array()
+Array(true)=Array(true)
+Array(a)=Array(a)
+Array(1)=Array(1)
+
+((),())=((),())
+(true,false)=(true,false)
+
+List(List(1), List(2))=List(List(1), List(2))
+
+Array(Array(1), Array(2))=Array(Array(1), Array(2))
+
diff --git a/test/files/jvm/manifests.check.temporarily.disabled b/test/files/jvm/manifests.check.temporarily.disabled deleted file mode 100644 index 54f504b929..0000000000 --- a/test/files/jvm/manifests.check.temporarily.disabled +++ /dev/null @@ -1,55 +0,0 @@ -x=(), m=Unit -x=true, m=Boolean -x=a, m=Char -x=1, m=Int -x=abc, m=java.lang.String -x='abc, m=scala.Symbol - -x=List(()), m=scala.collection.immutable.List[Unit] -x=List(true), m=scala.collection.immutable.List[Boolean] -x=List(1), m=scala.collection.immutable.List[Int] -x=List(abc), m=scala.collection.immutable.List[java.lang.String] -x=List('abc), m=scala.collection.immutable.List[scala.Symbol] - -x=[Z, m=Array[Boolean] -x=[C, m=Array[Char] -x=[I, m=Array[Int] -x=[Ljava.lang.String;, m=Array[java.lang.String] -x=[Lscala.Symbol;, m=Array[scala.Symbol] - -x=((),()), m=scala.Tuple2[Unit, Unit] -x=(true,false), m=scala.Tuple2[Boolean, Boolean] -x=(1,2), m=scala.Tuple2[Int, Int] -x=(abc,xyz), m=scala.Tuple2[java.lang.String, java.lang.String] -x=('abc,'xyz), m=scala.Tuple2[scala.Symbol, scala.Symbol] - - -x=Foo, m=Foo[Int] -x=Foo, m=Foo[scala.collection.immutable.List[Int]] -x=Foo, m=Foo[Foo[Int]] -x=Foo, m=Foo[scala.collection.immutable.List[Foo[Int]]] - -x=Test1$$anon$1, m=Object with Bar[java.lang.String] - -()=() -true=true -a=a -1=1 -'abc='abc - -List(())=List(()) -List(true)=List(true) -List('abc)=List('abc) - -Array()=Array() -Array(true)=Array(true) -Array(a)=Array(a) -Array(1)=Array(1) - -((),())=((),()) -(true,false)=(true,false) - -List(List(1), List(2))=List(List(1), List(2)) - -Array(Array(1), Array(2))=Array(Array(1), Array(2)) - diff --git a/test/files/jvm/manifests.scala.temporarily.disabled b/test/files/jvm/manifests.scala index 241966fd9d..935427f5d4 100644 --- a/test/files/jvm/manifests.scala.temporarily.disabled +++ b/test/files/jvm/manifests.scala @@ -50,7 +50,8 @@ object Test1 extends TestUtil { print(new Foo(List(new Foo(2)))) println() - print(new Bar[String] { def f = "abc" }) + print(new Bar[String] { def f = "abc" }); + {print(new Bar[String] { def f = "abc" })} println() } @@ -102,8 +103,10 @@ trait TestUtil { } import scala.reflect._ def print[T](x: T)(implicit m: Manifest[T]) { - val m1: Manifest[T] = read(write(m)) + // manifests are no longer serializable +// val m1: Manifest[T] = read(write(m)) + val m1: Manifest[T] = m val x1 = x.toString.replaceAll("@[0-9a-z]+$", "") - println("x="+x1+", m="+m1) + println("x="+x1+", m="+m1+", k="+m1.tpe.kind+", s="+m1.sym.toString) } } diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala index f0ca438774..fce1a25bb6 100644 --- a/test/files/jvm/scala-concurrent-tck.scala +++ b/test/files/jvm/scala-concurrent-tck.scala @@ -64,7 +64,7 @@ trait FutureCallbacks extends TestBase { } } } - + def testOnSuccessWhenFailed(): Unit = once { done => val f = future[Unit] { @@ -94,7 +94,7 @@ trait FutureCallbacks extends TestBase { assert(x == 1) } } - + def testOnFailureWhenSpecialThrowable(num: Int, cause: Throwable): Unit = once { done => val f = future[Unit] { @@ -139,7 +139,8 @@ trait FutureCallbacks extends TestBase { testOnFailure() testOnFailureWhenSpecialThrowable(5, new Error) testOnFailureWhenSpecialThrowable(6, new scala.util.control.ControlThrowable { }) - testOnFailureWhenSpecialThrowable(7, new InterruptedException) + //TODO: this test is currently problematic, because NonFatal does not match InterruptedException + //testOnFailureWhenSpecialThrowable(7, new InterruptedException) testOnFailureWhenTimeoutException() } @@ -289,6 +290,9 @@ trait FutureCombinators extends TestBase { } } + /* TODO: Test for NonFatal in collect (more of a regression test at this point). + */ + def testForeachSuccess(): Unit = once { done => val p = promise[Int]() @@ -473,8 +477,8 @@ trait FutureCombinators extends TestBase { def testFallbackToFailure(): Unit = once { done => val cause = new Exception - val f = future { throw cause } - val g = future { sys.error("failed") } + val f = future { sys.error("failed") } + val g = future { throw cause } val h = f fallbackTo g h onSuccess { diff --git a/test/files/neg/t3507.check b/test/files/neg/t3507.check deleted file mode 100644 index 71bf295039..0000000000 --- a/test/files/neg/t3507.check +++ /dev/null @@ -1,4 +0,0 @@ -t3507.scala:13: error: No ConcreteTypeTag available for _1.b.c.type
- mani/*[object _1.b.c]*/(c) // kaboom in manifestOfType / TreeGen.mkAttributedQualifier
- ^
-one error found
diff --git a/test/files/neg/t5666.check b/test/files/neg/t5666.check index 1be51d0138..cc31390dc7 100644 --- a/test/files/neg/t5666.check +++ b/test/files/neg/t5666.check @@ -1,37 +1,10 @@ t5666.scala:2: error: class Any is abstract; cannot be instantiated new Any ^ -t5666.scala:3: error: trait AnyVal is abstract; cannot be instantiated +t5666.scala:3: error: class AnyVal is abstract; cannot be instantiated new AnyVal ^ -t5666.scala:4: error: Double does not have a constructor - new Double - ^ -t5666.scala:5: error: Float does not have a constructor - new Float - ^ -t5666.scala:6: error: Long does not have a constructor - new Long - ^ -t5666.scala:7: error: Int does not have a constructor - new Int - ^ -t5666.scala:8: error: Char does not have a constructor - new Char - ^ -t5666.scala:9: error: Short does not have a constructor - new Short - ^ -t5666.scala:10: error: Byte does not have a constructor - new Byte - ^ -t5666.scala:11: error: Boolean does not have a constructor - new Boolean - ^ -t5666.scala:12: error: Unit does not have a constructor - new Unit - ^ t5666.scala:13: error: trait Nothing is abstract; cannot be instantiated new Nothing ^ -12 errors found +three errors found diff --git a/test/files/pos/implicits.scala.temporarily.disabled b/test/files/pos/implicits.scala index 2c01dd0ba8..2c01dd0ba8 100644 --- a/test/files/pos/implicits.scala.temporarily.disabled +++ b/test/files/pos/implicits.scala diff --git a/test/files/pos/manifest1.scala.temporarily.disabled b/test/files/pos/manifest1.scala index 8901aa7437..8901aa7437 100644 --- a/test/files/pos/manifest1.scala.temporarily.disabled +++ b/test/files/pos/manifest1.scala diff --git a/test/files/run/existentials3.check b/test/files/run/existentials3.check new file mode 100644 index 0000000000..8227d77909 --- /dev/null +++ b/test/files/run/existentials3.check @@ -0,0 +1,24 @@ +ConcreteTypeTag[Bar.type], t=TypeRef, s= <: scala.runtime.AbstractFunction0[Bar] with Serializable{case def unapply(x$0: Bar): Boolean} with Singleton
+ConcreteTypeTag[Bar], t=TypeRef, s= <: Test.ToS with Product with Serializable{def copy(): Bar}
+ConcreteTypeTag[Test.ToS], t=RefinedType, s=f3
+ConcreteTypeTag[Test.ToS], t=RefinedType, s=f4
+ConcreteTypeTag[Test.ToS], t=RefinedType, s=f5
+ConcreteTypeTag[() => Test.ToS], t=TypeRef, s=class Function0
+ConcreteTypeTag[() => Test.ToS], t=TypeRef, s=class Function0
+ConcreteTypeTag[$anon], t=TypeRef, s= <: B with Test.ToS
+ConcreteTypeTag[$anon], t=TypeRef, s= <: B with A with Test.ToS
+ConcreteTypeTag[List[Object{type T1}#T1]], t=TypeRef, s=class List
+ConcreteTypeTag[List[Seq[Int]]], t=TypeRef, s=class List
+ConcreteTypeTag[List[Seq[U forSome { type U <: Int }]]], t=TypeRef, s=class List
+ConcreteTypeTag[Bar.type], t=TypeRef, s= <: scala.runtime.AbstractFunction0[Bar] with Serializable{case def unapply(x$0: Bar): Boolean} with Singleton
+ConcreteTypeTag[Bar], t=TypeRef, s= <: Test.ToS with Product with Serializable{def copy(): Bar}
+ConcreteTypeTag[Test.ToS], t=RefinedType, s=g3
+ConcreteTypeTag[Test.ToS], t=RefinedType, s=g4
+ConcreteTypeTag[Test.ToS], t=RefinedType, s=g5
+ConcreteTypeTag[() => Test.ToS], t=TypeRef, s=class Function0
+ConcreteTypeTag[() => Test.ToS], t=TypeRef, s=class Function0
+ConcreteTypeTag[$anon], t=TypeRef, s= <: B with Test.ToS
+ConcreteTypeTag[$anon], t=TypeRef, s= <: B with A with Test.ToS
+ConcreteTypeTag[List[Object{type T1}#T1]], t=TypeRef, s=class List
+ConcreteTypeTag[List[Seq[Int]]], t=TypeRef, s=class List
+ConcreteTypeTag[List[Seq[U forSome { type U <: Int }]]], t=TypeRef, s=class List
diff --git a/test/files/run/existentials3.check.temporarily.disabled b/test/files/run/existentials3.check.temporarily.disabled deleted file mode 100644 index 36a458dacc..0000000000 --- a/test/files/run/existentials3.check.temporarily.disabled +++ /dev/null @@ -1,22 +0,0 @@ -_ <: scala.runtime.AbstractFunction0[_ <: Object with Test$ToS with scala.Product with scala.Serializable] with scala.Serializable with java.lang.Object -_ <: Object with Test$ToS with scala.Product with scala.Serializable -Object with Test$ToS -Object with Test$ToS -Object with Test$ToS -scala.Function0[Object with Test$ToS] -scala.Function0[Object with Test$ToS] -_ <: Object with _ <: Object with Object with Test$ToS -_ <: Object with _ <: Object with _ <: Object with Test$ToS -scala.collection.immutable.List[Object with scala.collection.Seq[Int]] -scala.collection.immutable.List[Object with scala.collection.Seq[_ <: Int]] -_ <: scala.runtime.AbstractFunction0[_ <: Object with Test$ToS with scala.Product with scala.Serializable] with scala.Serializable with java.lang.Object -_ <: Object with Test$ToS with scala.Product with scala.Serializable -Object with Test$ToS -Object with Test$ToS -Object with Test$ToS -scala.Function0[Object with Test$ToS] -scala.Function0[Object with Test$ToS] -_ <: Object with _ <: Object with Object with Test$ToS -_ <: Object with _ <: Object with _ <: Object with Test$ToS -scala.collection.immutable.List[Object with scala.collection.Seq[Int]] -scala.collection.immutable.List[Object with scala.collection.Seq[_ <: Int]] diff --git a/test/files/run/existentials3.scala.temporarily.disabled b/test/files/run/existentials3.scala index bb80d366cc..d6d5612687 100644 --- a/test/files/run/existentials3.scala.temporarily.disabled +++ b/test/files/run/existentials3.scala @@ -11,11 +11,11 @@ object Test { def f8 = { trait A ; trait B extends A ; class C extends B with ToS; new C { } } def f9 = { trait A ; trait B ; class C extends B with A with ToS; new C { } } - + def f10 = { class A { type T1 } ; List[A#T1]() } def f11 = { abstract class A extends Seq[Int] ; List[A]() } def f12 = { abstract class A extends Seq[U forSome { type U <: Int }] ; List[A]() } - + val g1 = { case class Bar() extends ToS; Bar } val g2 = { case class Bar() extends ToS; Bar() } val g3 = { class Bar() extends ToS; object Bar extends ToS; Bar } @@ -30,10 +30,16 @@ object Test { val g10 = { class A { type T1 } ; List[A#T1]() } val g11 = { abstract class A extends Seq[Int] ; List[A]() } val g12 = { abstract class A extends Seq[U forSome { type U <: Int }] ; List[A]() } - - def m[T: Manifest](x: T) = println(manifest[T]) - + + def printTag(t: TypeTag[_]) = { + val s = if (t.sym.isFreeType) t.sym.typeSignature.toString else t.sym.toString + println("%s, t=%s, s=%s".format(t, t.tpe.kind, s)) + } + def m[T: ConcreteTypeTag](x: T) = printTag(concreteTypeTag[T]) + def m2[T: TypeTag](x: T) = printTag(typeTag[T]) + // manifests don't work for f10/g10 + // oh, they do now :) def main(args: Array[String]): Unit = { m(f1) m(f2) @@ -44,7 +50,7 @@ object Test { m(f7) m(f8) m(f9) - // m(f10) + m2(f10) m(f11) m(f12) m(g1) @@ -56,7 +62,7 @@ object Test { m(g7) m(g8) m(g9) - // m(g10) + m2(g10) m(g11) m(g12) } diff --git a/test/files/run/repl-colon-type.check b/test/files/run/repl-colon-type.check index 66c2fcc77f..cb0b9a6c8b 100644 --- a/test/files/run/repl-colon-type.check +++ b/test/files/run/repl-colon-type.check @@ -7,6 +7,12 @@ scala> :type List[1, 2, 3] <console>:2: error: identifier expected but integer literal found. List[1, 2, 3] ^ +<console>:3: error: ']' expected but '}' found. + } + ^ +<console>:1: error: identifier expected but integer literal found. + List[1, 2, 3] + ^ scala> :type List(1, 2, 3) @@ -25,7 +31,7 @@ scala> :type def f[T >: Null, U <: String](x: T, y: U) = Set(x, y) [T >: Null, U <: String](x: T, y: U)scala.collection.immutable.Set[Any] scala> :type def x = 1 ; def bar[T >: Null <: AnyRef](xyz: T) = 5 -[T >: Null <: AnyRef](xyz: T)Int +=> Int <and> [T >: Null <: AnyRef](xyz: T)Int scala> @@ -39,10 +45,19 @@ scala> :type lazy val f = 5 Int scala> :type protected lazy val f = 5 -Int +<console>:2: error: illegal start of statement (no modifiers allowed here) + protected lazy val f = 5 + ^ +<console>:5: error: lazy value f cannot be accessed in object $iw + Access to protected value f not permitted because + enclosing object $eval in package $line19 is not a subclass of + object $iw where target is defined + lazy val $result = `f` + ^ + scala> :type def f = 5 -Int +=> Int scala> :type def f() = 5 ()Int @@ -54,4 +69,156 @@ scala> :type def g[T](xs: Set[_ <: T]) = Some(xs.head) scala> +scala> // verbose! + +scala> :type -v List(1,2,3) filter _ +// Type signature +(Int => Boolean) => List[Int] + +// Internal Type structure +TypeRef( + TypeSymbol(abstract trait Function1[-T1, +R] extends Object) + args = List( + TypeRef( + TypeSymbol(abstract trait Function1[-T1, +R] extends Object) + args = List( + TypeRef(TypeSymbol(final class Int extends AnyVal)) + TypeRef(TypeSymbol(final class Boolean extends AnyVal)) + ) + ) + TypeRef( + TypeSymbol( + sealed abstract class List[+A] extends AbstractSeq[A] with LinearSeq[A] with Product with GenericTraversableTemplate[A,List] with LinearSeqOptimized[A,List[A]] + + ) + args = List( + TypeRef(TypeSymbol(final class Int extends AnyVal)) + ) + ) + ) +) + +scala> :type -v def f[T >: Null, U <: String](x: T, y: U) = Set(x, y) +// Type signature +[T >: Null, U <: String](x: T, y: U)scala.collection.immutable.Set[Any] + +// Internal Type structure +PolyType( + typeParams = List(TypeParam(T >: Null), TypeParam(U <: String)) + resultType = MethodType( + params = List(TermSymbol(x: T), TermSymbol(y: U)) + resultType = TypeRef( + TypeSymbol( + abstract trait Set[A] extends Iterable[A] with Set[A] with GenericSetTemplate[A,scala.collection.immutable.Set] with SetLike[A,scala.collection.immutable.Set[A]] with Parallelizable[A,scala.collection.parallel.immutable.ParSet[A]] + + ) + args = List(TypeRef(TypeSymbol(abstract class Any extends ))) + ) + ) +) + +scala> :type -v def x = 1 ; def bar[T >: Null <: AnyRef](xyz: T) = 5 +// Type signature +=> Int <and> [T >: Null <: AnyRef](xyz: T)Int + +// Internal Type structure +OverloadedType( + alts = List( + NullaryMethodType( + TypeRef(TypeSymbol(final class Int extends AnyVal)) + ) + PolyType( + typeParams = List(TypeParam(T >: Null <: AnyRef)) + resultType = MethodType( + params = List(TermSymbol(xyz: T)) + resultType = TypeRef( + TypeSymbol(final class Int extends AnyVal) + ) + ) + ) + ) +) + +scala> :type -v Nil.combinations _ +// Type signature +Int => Iterator[List[Nothing]] + +// Internal Type structure +TypeRef( + TypeSymbol(abstract trait Function1[-T1, +R] extends Object) + args = List( + TypeRef(TypeSymbol(final class Int extends AnyVal)) + TypeRef( + TypeSymbol( + abstract trait Iterator[+A] extends TraversableOnce[A] + ) + args = List( + TypeRef( + TypeSymbol( + sealed abstract class List[+A] extends AbstractSeq[A] with LinearSeq[A] with Product with GenericTraversableTemplate[A,List] with LinearSeqOptimized[A,List[A]] + + ) + args = List( + TypeRef( + TypeSymbol(final abstract class Nothing extends Any) + ) + ) + ) + ) + ) + ) +) + +scala> :type -v def f[T <: AnyVal] = List[T]().combinations _ +// Type signature +[T <: AnyVal]=> Int => Iterator[List[T]] + +// Internal Type structure +PolyType( + typeParams = List(TypeParam(T <: AnyVal)) + resultType = NullaryMethodType( + TypeRef( + TypeSymbol(abstract trait Function1[-T1, +R] extends Object) + args = List( + TypeRef(TypeSymbol(final class Int extends AnyVal)) + TypeRef( + TypeSymbol( + abstract trait Iterator[+A] extends TraversableOnce[A] + ) + args = List( + TypeRef( + TypeSymbol( + sealed abstract class List[+A] extends AbstractSeq[A] with LinearSeq[A] with Product with GenericTraversableTemplate[A,List] with LinearSeqOptimized[A,List[A]] + + ) + args = List(TypeParamTypeRef(TypeParam(T <: AnyVal))) + ) + ) + ) + ) + ) + ) +) + +scala> :type -v def f[T, U >: T](x: T, y: List[U]) = x :: y +// Type signature +[T, U >: T](x: T, y: List[U])List[U] + +// Internal Type structure +PolyType( + typeParams = List(TypeParam(T), TypeParam(U >: T)) + resultType = MethodType( + params = List(TermSymbol(x: T), TermSymbol(y: List[U])) + resultType = TypeRef( + TypeSymbol( + sealed abstract class List[+A] extends AbstractSeq[A] with LinearSeq[A] with Product with GenericTraversableTemplate[A,List] with LinearSeqOptimized[A,List[A]] + + ) + args = List(TypeParamTypeRef(TypeParam(U >: T))) + ) + ) +) + +scala> + scala> diff --git a/test/files/run/repl-colon-type.scala b/test/files/run/repl-colon-type.scala index 39ab580d2a..c055b215c2 100644 --- a/test/files/run/repl-colon-type.scala +++ b/test/files/run/repl-colon-type.scala @@ -18,6 +18,14 @@ object Test extends ReplTest { |:type def f() = 5 | |:type def g[T](xs: Set[_ <: T]) = Some(xs.head) + | + |// verbose! + |:type -v List(1,2,3) filter _ + |:type -v def f[T >: Null, U <: String](x: T, y: U) = Set(x, y) + |:type -v def x = 1 ; def bar[T >: Null <: AnyRef](xyz: T) = 5 + |:type -v Nil.combinations _ + |:type -v def f[T <: AnyVal] = List[T]().combinations _ + |:type -v def f[T, U >: T](x: T, y: List[U]) = x :: y """.stripMargin } diff --git a/test/files/run/repl-power.check b/test/files/run/repl-power.check index e439a2a7f4..1e7b6f0cd8 100644 --- a/test/files/run/repl-power.check +++ b/test/files/run/repl-power.check @@ -14,7 +14,6 @@ scala> global.emptyValDef // "it is imported twice in the same scope by ..." res0: $r.global.emptyValDef.type = private val _ = _ scala> val tp = ArrayClass[scala.util.Random] // magic with manifests -warning: there were 2 feature warnings; re-run with -feature for details tp: $r.global.Type = Array[scala.util.Random] scala> tp.memberType(Array_apply) // evidence diff --git a/test/files/run/repl-type-verbose.check b/test/files/run/repl-type-verbose.check new file mode 100644 index 0000000000..103ac3e64d --- /dev/null +++ b/test/files/run/repl-type-verbose.check @@ -0,0 +1,186 @@ +Type in expressions to have them evaluated. +Type :help for more information. + +scala> + +scala> // verbose! + +scala> :type -v def f = 5 +// Type signature +=> Int + +// Internal Type structure +NullaryMethodType( + TypeRef(TypeSymbol(final class Int extends AnyVal)) +) + +scala> :type -v def f() = 5 +// Type signature +()Int + +// Internal Type structure +NullaryMethodType( + resultType = TypeRef(TypeSymbol(final class Int extends AnyVal)) +) + +scala> :type -v def f[T] = 5 +// Type signature +[T]=> Int + +// Internal Type structure +PolyType( + typeParams = List(TypeParam(T)) + resultType = NullaryMethodType( + TypeRef(TypeSymbol(final class Int extends AnyVal)) + ) +) + +scala> :type -v def f[T >: Null] = 5 +// Type signature +[T >: Null]=> Int + +// Internal Type structure +PolyType( + typeParams = List(TypeParam(T >: Null)) + resultType = NullaryMethodType( + TypeRef(TypeSymbol(final class Int extends AnyVal)) + ) +) + +scala> :type -v def f[T <: String] = 5 +// Type signature +[T <: String]=> Int + +// Internal Type structure +PolyType( + typeParams = List(TypeParam(T <: String)) + resultType = NullaryMethodType( + TypeRef(TypeSymbol(final class Int extends AnyVal)) + ) +) + +scala> :type -v def f[T]() = 5 +// Type signature +[T]()Int + +// Internal Type structure +PolyType( + typeParams = List(TypeParam(T)) + resultType = NullaryMethodType( + resultType = TypeRef(TypeSymbol(final class Int extends AnyVal)) + ) +) + +scala> :type -v def f[T, U]() = 5 +// Type signature +[T, U]()Int + +// Internal Type structure +PolyType( + typeParams = List(TypeParam(T), TypeParam(U)) + resultType = NullaryMethodType( + resultType = TypeRef(TypeSymbol(final class Int extends AnyVal)) + ) +) + +scala> :type -v def f[T, U]()() = 5 +// Type signature +[T, U]()()Int + +// Internal Type structure +PolyType( + typeParams = List(TypeParam(T), TypeParam(U)) + resultType = NullaryMethodType( + resultType = NullaryMethodType( + resultType = TypeRef( + TypeSymbol(final class Int extends AnyVal) + ) + ) + ) +) + +scala> :type -v def f[T, U <: T] = 5 +// Type signature +[T, U <: T]=> Int + +// Internal Type structure +PolyType( + typeParams = List(TypeParam(T), TypeParam(U <: T)) + resultType = NullaryMethodType( + TypeRef(TypeSymbol(final class Int extends AnyVal)) + ) +) + +scala> :type -v def f[T, U <: T](x: T)(y: U) = 5 +// Type signature +[T, U <: T](x: T)(y: U)Int + +// Internal Type structure +PolyType( + typeParams = List(TypeParam(T), TypeParam(U <: T)) + resultType = MethodType( + params = List(TermSymbol(x: T)) + resultType = MethodType( + params = List(TermSymbol(y: U)) + resultType = TypeRef( + TypeSymbol(final class Int extends AnyVal) + ) + ) + ) +) + +scala> :type -v def f[T: Ordering] = 5 +// Type signature +[T](implicit evidence$1: Ordering[T])Int + +// Internal Type structure +PolyType( + typeParams = List(TypeParam(T)) + resultType = MethodType( + params = List(TermSymbol(implicit evidence$1: Ordering[T])) + resultType = TypeRef(TypeSymbol(final class Int extends AnyVal)) + ) +) + +scala> :type -v def f[T: Ordering] = implicitly[Ordering[T]] +// Type signature +[T](implicit evidence$1: Ordering[T])Ordering[T] + +// Internal Type structure +PolyType( + typeParams = List(TypeParam(T)) + resultType = MethodType( + params = List(TermSymbol(implicit evidence$1: Ordering[T])) + resultType = AliasTypeRef( + Alias(type Ordering[T] = scala.math.Ordering[T]) + args = List(TypeParamTypeRef(TypeParam(T))) + normalize = TypeRef( + TypeSymbol( + abstract trait Ordering[T] extends Comparator[T] with PartialOrdering[T] with Serializable + + ) + args = List(TypeParamTypeRef(TypeParam(T))) + ) + ) + ) +) + +scala> :type -v def f[T <: { type Bippy = List[Int] ; def g(): Bippy }] = 5 +// Type signature +[T <: AnyRef{type Bippy = List[Int]; def g(): this.Bippy}]=> Int + +// Internal Type structure +PolyType( + typeParams = List( + TypeParam( + T <: AnyRef{type Bippy = List[Int]; def g(): this.Bippy} + ) + ) + resultType = NullaryMethodType( + TypeRef(TypeSymbol(final class Int extends AnyVal)) + ) +) + +scala> + +scala> diff --git a/test/files/run/repl-type-verbose.scala b/test/files/run/repl-type-verbose.scala new file mode 100644 index 0000000000..10c390550a --- /dev/null +++ b/test/files/run/repl-type-verbose.scala @@ -0,0 +1,20 @@ +import scala.tools.partest.ReplTest + +object Test extends ReplTest { + def code = """ + |// verbose! + |:type -v def f = 5 + |:type -v def f() = 5 + |:type -v def f[T] = 5 + |:type -v def f[T >: Null] = 5 + |:type -v def f[T <: String] = 5 + |:type -v def f[T]() = 5 + |:type -v def f[T, U]() = 5 + |:type -v def f[T, U]()() = 5 + |:type -v def f[T, U <: T] = 5 + |:type -v def f[T, U <: T](x: T)(y: U) = 5 + |:type -v def f[T: Ordering] = 5 + |:type -v def f[T: Ordering] = implicitly[Ordering[T]] + |:type -v def f[T <: { type Bippy = List[Int] ; def g(): Bippy }] = 5 + """.stripMargin +} diff --git a/test/files/run/t1195.check b/test/files/run/t1195.check new file mode 100644 index 0000000000..554e3fd03d --- /dev/null +++ b/test/files/run/t1195.check @@ -0,0 +1,6 @@ +ConcreteTypeTag[Bar.type], underlying = <: scala.runtime.AbstractFunction1[Int,Bar] with Serializable{case def unapply(x$0: Bar): Option[Int]} with Singleton
+ConcreteTypeTag[Bar], underlying = <: Product with Serializable{val x: Int; def copy(x: Int): Bar; def copy$default$1: Int; def _1: Int}
+ConcreteTypeTag[Product with Serializable], underlying = Product with Serializable
+ConcreteTypeTag[Bar.type], underlying = <: scala.runtime.AbstractFunction1[Int,Bar] with Serializable{case def unapply(x$0: Bar): Option[Int]} with Singleton
+ConcreteTypeTag[Bar], underlying = <: Product with Serializable{val x: Int; def copy(x: Int): Bar; def copy$default$1: Int; def _1: Int}
+ConcreteTypeTag[Product with Serializable], underlying = Product with Serializable
diff --git a/test/files/run/t1195.check.temporarily.disabled b/test/files/run/t1195.check.temporarily.disabled deleted file mode 100644 index d023bc91f7..0000000000 --- a/test/files/run/t1195.check.temporarily.disabled +++ /dev/null @@ -1,6 +0,0 @@ -_ <: scala.runtime.AbstractFunction1[Int, _ <: Object with scala.Product with scala.Serializable] with scala.Serializable with java.lang.Object -_ <: Object with scala.Product with scala.Serializable -Object with scala.Product with scala.Serializable -_ <: scala.runtime.AbstractFunction1[Int, _ <: Object with scala.Product with scala.Serializable] with scala.Serializable with java.lang.Object -_ <: Object with scala.Product with scala.Serializable -Object with scala.Product with scala.Serializable diff --git a/test/files/run/t1195.scala.temporarily.disabled b/test/files/run/t1195.scala index 81ef5bdb0e..93b1dcbd07 100644 --- a/test/files/run/t1195.scala.temporarily.disabled +++ b/test/files/run/t1195.scala @@ -6,8 +6,8 @@ object Test { val f1 = f() val g1 = g() val h1 = h() - - def m[T: Manifest](x: T) = println(manifest[T]) + + def m[T: Manifest](x: T) = println(manifest[T] + ", underlying = " + manifest[T].sym.typeSignature) def main(args: Array[String]): Unit = { m(f) diff --git a/test/files/run/t3507.check b/test/files/run/t3507.check new file mode 100644 index 0000000000..50ab029592 --- /dev/null +++ b/test/files/run/t3507.check @@ -0,0 +1 @@ +ConcreteTypeTag[_1.type#b.c.type]
diff --git a/test/files/neg/t3507.scala b/test/files/run/t3507.scala index 32688d3934..3cdd40a881 100644 --- a/test/files/neg/t3507.scala +++ b/test/files/run/t3507.scala @@ -5,11 +5,11 @@ class A { def m = b.c } -object Test { +object Test extends App { var a: A = new A // mutable val c /*: object _1.b.c forSome { val _1: A } */ = a.m // widening using existential - - def mani[T: Manifest](x: T) = () + + def mani[T: Manifest](x: T) = println(manifest[T]) mani/*[object _1.b.c]*/(c) // kaboom in manifestOfType / TreeGen.mkAttributedQualifier // --> _1 is not in scope here }
\ No newline at end of file diff --git a/test/files/run/t4110.check b/test/files/run/t4110.check new file mode 100644 index 0000000000..28f220e1fe --- /dev/null +++ b/test/files/run/t4110.check @@ -0,0 +1,2 @@ +ConcreteTypeTag[Test.A with Test.B]
+ConcreteTypeTag[Test.A with Test.B]
diff --git a/test/files/run/t4110.check.temporarily.disabled b/test/files/run/t4110.check.temporarily.disabled deleted file mode 100644 index 8b005989de..0000000000 --- a/test/files/run/t4110.check.temporarily.disabled +++ /dev/null @@ -1,2 +0,0 @@ -Object with Test$A with Test$B -Object with Test$A with Test$B diff --git a/test/files/run/t4110.scala.temporarily.disabled b/test/files/run/t4110.scala index 4bd377b73e..4bd377b73e 100644 --- a/test/files/run/t4110.scala.temporarily.disabled +++ b/test/files/run/t4110.scala diff --git a/test/files/run/treePrint.check.temporarily.disabled b/test/files/run/treePrint.check index 3360815ac1..3360815ac1 100644 --- a/test/files/run/treePrint.check.temporarily.disabled +++ b/test/files/run/treePrint.check diff --git a/test/files/run/treePrint.scala.temporarily.disabled b/test/files/run/treePrint.scala index e0332a705f..4a80e2824d 100644 --- a/test/files/run/treePrint.scala.temporarily.disabled +++ b/test/files/run/treePrint.scala @@ -4,7 +4,7 @@ object Test { import scala.tools.nsc._ import interpreter._ import java.io.{ OutputStream, BufferedReader, StringReader, PrintWriter, Writer, OutputStreamWriter} - + val code = """ def foo = { var q: Boolean = false @@ -22,11 +22,11 @@ object Test { else 20 } else 30 - + (x == 5) || !q || true } - """ - + """ + class NullOutputStream extends OutputStream { def write(b: Int) { } } def main(args: Array[String]) { @@ -35,7 +35,8 @@ object Test { settings.Ycompacttrees.value = true val intp = new IMain(settings, new PrintWriter(new NullOutputStream)) - val power = new Power(intp, new ReplVals { }) + val vals = new ReplVals { } + val power = new Power(intp, vals) intp.interpret("""def initialize = "Have to interpret something or we get errors." """) power trees code foreach println } |