diff options
-rw-r--r-- | classpath.SAMPLE | 5 | ||||
-rw-r--r-- | src/compiler/scala/reflect/internal/SymbolTable.scala | 6 | ||||
-rw-r--r-- | src/compiler/scala/reflect/internal/Symbols.scala | 11 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala | 141 | ||||
-rw-r--r-- | src/library/scala/concurrent/Awaitable.scala | 9 | ||||
-rw-r--r-- | src/library/scala/concurrent/ConcurrentPackageObject.scala | 14 | ||||
-rw-r--r-- | src/library/scala/concurrent/ExecutionContext.scala | 91 | ||||
-rw-r--r-- | src/library/scala/concurrent/Future.scala | 128 | ||||
-rw-r--r-- | src/library/scala/concurrent/Promise.scala | 16 | ||||
-rw-r--r-- | src/library/scala/concurrent/impl/ExecutionContextImpl.scala | 84 | ||||
-rw-r--r-- | src/library/scala/concurrent/impl/Future.scala | 74 | ||||
-rw-r--r-- | src/library/scala/concurrent/impl/Promise.scala | 29 | ||||
-rw-r--r-- | src/library/scala/concurrent/package.scala | 28 | ||||
-rw-r--r-- | test/files/jvm/scala-concurrent-tck.scala | 2 | ||||
-rw-r--r-- | test/pending/pos/t1476.scala | 23 | ||||
-rw-r--r-- | test/pending/pos/t5626.scala | 12 | ||||
-rw-r--r-- | test/pending/run/t5629.check | 2 | ||||
-rw-r--r-- | test/pending/run/t5629.scala | 25 |
18 files changed, 391 insertions, 309 deletions
diff --git a/classpath.SAMPLE b/classpath.SAMPLE index 69c2baeba7..9e607a41d9 100644 --- a/classpath.SAMPLE +++ b/classpath.SAMPLE @@ -3,10 +3,9 @@ <classpathentry kind="src" path="src/compiler"/> <classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_CONTAINER"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> - <classpathentry exported="true" kind="lib" path="lib/msil.jar"/> + <classpathentry kind="lib" path="lib/msil.jar"/> <classpathentry kind="lib" path="lib/jline.jar"/> - <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> - <classpathentry exported="true" kind="lib" path="lib/fjbg.jar"/> + <classpathentry kind="lib" path="lib/fjbg.jar"/> <classpathentry kind="lib" path="lib/forkjoin.jar"/> <classpathentry kind="lib" path="lib/ant/ant.jar"/> <classpathentry kind="output" path="build/quick/classes/compiler"/> diff --git a/src/compiler/scala/reflect/internal/SymbolTable.scala b/src/compiler/scala/reflect/internal/SymbolTable.scala index b58a0ef7d5..bb11ca634a 100644 --- a/src/compiler/scala/reflect/internal/SymbolTable.scala +++ b/src/compiler/scala/reflect/internal/SymbolTable.scala @@ -55,6 +55,12 @@ abstract class SymbolTable extends api.Universe log(msg + ": " + result) result } + private[scala] def logResultIf[T](msg: String, cond: T => Boolean)(result: T): T = { + if (cond(result)) + log(msg + ": " + result) + + result + } /** Are we compiling for Java SE? */ // def forJVM: Boolean diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index f4039cf6d3..2019b92836 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -543,7 +543,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Conditions where we omit the prefix when printing a symbol, to avoid * unpleasantries like Predef.String, $iw.$iw.Foo and <empty>.Bippy. */ - final def isOmittablePrefix = !settings.debug.value && ( + final def isOmittablePrefix = /*!settings.debug.value &&*/ ( UnqualifiedOwners(skipPackageObject) || isEmptyPrefix ) @@ -1191,9 +1191,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => var ph = phase while (ph.prev.keepsTypeParams) ph = ph.prev - - if (ph ne phase) - debuglog("checking unsafeTypeParams(" + this + ") at: " + phase + " reading at: " + ph) + // + // if (ph ne phase) + // debuglog("checking unsafeTypeParams(" + this + ") at: " + phase + " reading at: " + ph) ph } @@ -2119,7 +2119,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => else "" def defaultFlagMask = - if (settings.debug.value) -1L + if (isAbstractType) ExplicitFlags + else if (settings.debug.value) -1L else if (owner.isRefinementClass) ExplicitFlags & ~OVERRIDE else ExplicitFlags diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 7b0f5254b6..185cf4f533 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -318,7 +318,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { else specializedTypeVars(sym).intersect(env.keySet) ) val (methparams, others) = tvars.toList sortBy ("" + _.name) partition (_.owner.isMethod) - debuglog("specName(" + sym + ") env: " + env + " tvars: " + tvars) + // debuglog("specName(" + sym + ") env: " + env + " tvars: " + tvars) specializedName(sym.name, methparams map env, others map env) } @@ -518,7 +518,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { // better evaluate it before creating the new class symbol val clazzName = specializedName(clazz, env0).toTypeName val bytecodeClazz = clazz.owner.info.decl(clazzName) - debuglog("Specializing " + clazz + " found " + bytecodeClazz + " already there") + // debuglog("Specializing " + clazz + ", but found " + bytecodeClazz + " already there") bytecodeClazz.info val sClass = clazz.owner.newClass(clazzName, clazz.pos, (clazz.flags | SPECIALIZED) & ~CASE) @@ -574,7 +574,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { parents = parents.head.parents.head :: parents val extraSpecializedMixins = specializedParents(clazz.info.parents map applyContext) if (extraSpecializedMixins.nonEmpty) - debuglog("specializeClass on " + clazz + " founds extra specialized mixins: " + extraSpecializedMixins.mkString(", ")) + debuglog("extra specialized mixins for %s: %s".format(clazz.name.decode, extraSpecializedMixins.mkString(", "))) // If the class being specialized has a self-type, the self type may // require specialization. First exclude classes whose self types have // the same type constructor as the class itself, since they will @@ -652,7 +652,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { val NormalizedMember(original) = info(m) if (nonConflicting(env ++ typeEnv(m))) { if (info(m).degenerate) { - debuglog("degenerate normalized member " + m + " info(m): " + info(m)) + debuglog("degenerate normalized member " + m.defString) val specMember = enterMember(cloneInSpecializedClass(m, _ & ~DEFERRED)) info(specMember) = Implementation(original) @@ -660,7 +660,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } else debuglog({ val om = forwardToOverload(m) - "normalizedMember " + m + " om: " + om + " typeEnv(om): " + typeEnv(om) + "normalizedMember " + m + " om: " + om + " " + pp(typeEnv(om)) }) } else @@ -668,7 +668,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } else if (m.isDeferred) { // abstract methods val specMember = enterMember(cloneInSpecializedClass(m, _ | DEFERRED)) - debuglog("deferred " + specMember.fullName + " remains abstract") + // debuglog("deferred " + specMember.fullName + " remains abstract") info(specMember) = new Abstract(specMember) // was: new Forward(specMember) { @@ -698,20 +698,21 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { enterMember(specVal) // create accessors - debuglog("m: " + m + " isLocal: " + nme.isLocalName(m.name) + " specVal: " + specVal.name + " isLocal: " + nme.isLocalName(specVal.name)) + // debuglog("m: " + m + " isLocal: " + nme.isLocalName(m.name) + " specVal: " + specVal.name + " isLocal: " + nme.isLocalName(specVal.name)) + if (nme.isLocalName(m.name)) { val specGetter = mkAccessor(specVal, nme.localToGetter(specVal.name)) setInfo MethodType(Nil, specVal.info) val origGetter = overrideIn(sClass, m.getter(clazz)) info(origGetter) = Forward(specGetter) enterMember(specGetter) enterMember(origGetter) - debuglog("created accessors: " + specGetter + " orig: " + origGetter) + debuglog("specialize accessor in %s: %s -> %s".format(sClass.name.decode, origGetter.name.decode, specGetter.name.decode)) clazz.caseFieldAccessors.find(_.name.startsWith(m.name)) foreach { cfa => val cfaGetter = overrideIn(sClass, cfa) info(cfaGetter) = SpecializedAccessor(specVal) enterMember(cfaGetter) - debuglog("found case field accessor for " + m + " added override " + cfaGetter); + debuglog("override case field accessor %s -> %s".format(m.name.decode, cfaGetter.name.decode)) } if (specVal.isVariable && m.setter(clazz) != NoSymbol) { @@ -724,11 +725,15 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { enterMember(specSetter) enterMember(origSetter) } - } else { // if there are no accessors, specialized methods will need to access this field in specialized subclasses + } + else { // if there are no accessors, specialized methods will need to access this field in specialized subclasses m.resetFlag(PRIVATE) specVal.resetFlag(PRIVATE) + debuglog("no accessors for %s/%s, specialized methods must access field in subclass".format( + m.name.decode, specVal.name.decode)) } - } else if (m.isClass) { + } + else if (m.isClass) { val specClass: Symbol = cloneInSpecializedClass(m, x => x) typeEnv(specClass) = fullEnv specClass.name = specializedName(specClass, fullEnv).toTypeName @@ -787,10 +792,10 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { * // etc. */ private def normalizeMember(owner: Symbol, sym: Symbol, outerEnv: TypeEnv): List[Symbol] = { - debuglog("normalizeMember: " + sym.fullName) sym :: ( if (!sym.isMethod || beforeTyper(sym.typeParams.isEmpty)) Nil else { + // debuglog("normalizeMember: " + sym.fullNameAsName('.').decode) var specializingOn = specializedParams(sym) val unusedStvars = specializingOn filterNot specializedTypeVars(sym.info) @@ -810,7 +815,8 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { val (keys, vals) = env.toList.unzip specMember.name = specializedName(sym, env) - debuglog("normalizing: " + sym + " to " + specMember + " with params " + tps) + // debuglog("%s normalizes to %s%s".format(sym, specMember, + // if (tps.isEmpty) "" else " with params " + tps.mkString(", "))) typeEnv(specMember) = outerEnv ++ env val tps1 = produceTypeParameters(tps, specMember, env) @@ -820,11 +826,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { val methodType = sym.info.resultType.instantiateTypeParams(keys ++ tps, vals ++ tps1.map(_.tpe)).cloneInfo(specMember) specMember setInfo GenPolyType(tps1, methodType) - debuglog("expanded member: " + sym + ": " + sym.info + - " -> " + specMember + - ": " + specMember.info + - " env: " + env - ) + debuglog("%s expands to %s in %s".format(sym, specMember.name.decode, pp(env))) info(specMember) = NormalizedMember(sym) overloads(sym) ::= Overload(specMember, env) specMember @@ -833,6 +835,17 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { ) } + // concise printing of type env + private def pp(env: TypeEnv): String = { + env.toList.sortBy(_._1.name.toString) map { + case (k, v) => + val vsym = v.typeSymbol + if (k == vsym) "" + k.name + else k.name + ":" + vsym.name + + } mkString ("env(", ", ", ")") + } + /** Specialize member `m` w.r.t. to the outer environment and the type * parameters of the innermost enclosing class. * @@ -841,37 +854,38 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { * Return a list of symbols that are specializations of 'sym', owned by 'owner'. */ private def specializeMember(owner: Symbol, sym: Symbol, outerEnv: TypeEnv, tps: List[Symbol]): List[Symbol] = { - def specializeOn(tparams: List[Symbol]): List[Symbol] = - for (spec0 <- specializations(tparams)) yield { - val spec = mapAnyRefsInOrigCls(spec0, owner) - if (sym.isPrivate/* || sym.isProtected*/) { - //sym.privateWithin = sym.enclosingPackage - sym.resetFlag(PRIVATE).setFlag(PROTECTED) - debuglog("-->d SETTING PRIVATE WITHIN TO " + sym.enclosingPackage + " for " + sym) - } + def specializeOn(tparams: List[Symbol]): List[Symbol] = specializations(tparams) map { spec0 => + val spec = mapAnyRefsInOrigCls(spec0, owner) + if (sym.isPrivate) { + sym.resetFlag(PRIVATE).setFlag(PROTECTED) + debuglog("Set %s to private[%s]".format(sym, sym.enclosingPackage)) + } - val specMember = subst(outerEnv)(specializedOverload(owner, sym, spec)) - typeEnv(specMember) = typeEnv(sym) ++ outerEnv ++ spec - wasSpecializedForTypeVars(specMember) ++= spec collect { case (s, tp) if s.tpe == tp => s } + val specMember = subst(outerEnv)(specializedOverload(owner, sym, spec)) + typeEnv(specMember) = typeEnv(sym) ++ outerEnv ++ spec + wasSpecializedForTypeVars(specMember) ++= spec collect { case (s, tp) if s.tpe == tp => s } - debuglog("sym " + specMember + " was specialized for type vars " + wasSpecializedForTypeVars(specMember)) - debuglog("added specialized overload: %s in env: %s".format(specMember, typeEnv(specMember))) + val wasSpec = wasSpecializedForTypeVars(specMember) + if (wasSpec.nonEmpty) + debuglog("specialized overload for %s in %s".format(specMember, pp(typeEnv(specMember)))) - overloads(sym) ::= Overload(specMember, spec) - specMember - } + overloads(sym) ::= Overload(specMember, spec) + info(specMember) = SpecialOverload(sym, typeEnv(specMember)) + + specMember + } if (sym.isMethod) { - debuglog("specializeMember %s with tps: %s stvars(sym): %s".format(sym, tps, specializedTypeVars(sym))) + val stvars = specializedTypeVars(sym) + if (stvars.nonEmpty) + debuglog("specialized %s on %s".format(sym.fullLocationString, stvars.map(_.name).mkString(", "))) val tps1 = if (sym.isConstructor) tps filter (sym.info.paramTypes contains _) else tps - val tps2 = tps1 intersect specializedTypeVars(sym).toList + val tps2 = tps1 filter stvars if (!sym.isDeferred) addConcreteSpecMethod(sym) - val ms = specializeOn(tps2) - ms foreach (m => info(m) = SpecialOverload(sym, typeEnv(m))) - ms + specializeOn(tps2) } else Nil } @@ -894,7 +908,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { * * this method will return List('apply$mcII$sp') */ - private def specialOverrides(clazz: Symbol): List[Symbol] = { + private def specialOverrides(clazz: Symbol) = logResultIf[List[Symbol]]("specialOverrides(" + clazz + ")", _.nonEmpty) { /** Return the overridden symbol in syms that needs a specialized overriding symbol, * together with its specialization environment. The overridden symbol may not be * the closest to 'overriding', in a given hierarchy. @@ -917,24 +931,21 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } if (!overriding.isParamAccessor) { for (overridden <- overriding.allOverriddenSymbols) { - debuglog( - "Overridden: " + overridden.fullName + - ": " + overridden.info + - "\n by " + overriding.fullName + - ": " + overriding.info - ) val stvars = specializedTypeVars(overridden.info) if (stvars.nonEmpty) { - debuglog("\t\tspecializedTVars: " + stvars) + debuglog("specialized override of %s by %s%s".format(overridden.fullLocationString, overriding.fullLocationString, + if (stvars.isEmpty) "" else stvars.map(_.name).mkString("(", ", ", ")"))) + if (currentRun compiles overriding) checkOverriddenTParams(overridden) val env = unify(overridden.info, overriding.info, emptyEnv, false) def atNext = afterSpecialize(overridden.owner.info.decl(specializedName(overridden, env))) - debuglog("\t\tenv: " + env + "isValid: " + TypeEnv.isValid(env, overridden) + "found: " + atNext) - if (TypeEnv.restrict(env, stvars).nonEmpty && TypeEnv.isValid(env, overridden) && atNext != NoSymbol) + if (TypeEnv.restrict(env, stvars).nonEmpty && TypeEnv.isValid(env, overridden) && atNext != NoSymbol) { + debuglog(" " + pp(env) + " found " + atNext) return (overridden, env) + } } } } @@ -945,7 +956,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case (NoSymbol, _) => None case (overridden, env) => val om = specializedOverload(clazz, overridden, env) - debuglog("Added specialized overload %s for %s in env: %s with type: %s".format(om, overriding.fullName, env, om.info)) + debuglog("specialized overload %s for %s in %s: %s".format(om, overriding.name.decode, pp(env), om.info)) typeEnv(om) = env addConcreteSpecMethod(overriding) info(om) = ( @@ -993,7 +1004,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { */ private def unify(tp1: Type, tp2: Type, env: TypeEnv, strict: Boolean): TypeEnv = (tp1, tp2) match { case (TypeRef(_, sym1, _), _) if isSpecialized(sym1) => - debuglog("Unify - basic case: " + tp1 + ", " + tp2) + debuglog("Unify " + tp1 + ", " + tp2) if (isPrimitiveValueClass(tp2.typeSymbol) || isSpecializedAnyRefSubtype(tp2, sym1)) env + ((sym1, tp2)) else if (isSpecializedAnyRefSubtype(tp2, sym1)) @@ -1003,19 +1014,21 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { else env case (TypeRef(_, sym1, args1), TypeRef(_, sym2, args2)) => - debuglog("Unify TypeRefs: " + tp1 + " and " + tp2 + " with args " + (args1, args2) + " - ") + if (args1.nonEmpty || args2.nonEmpty) + debuglog("Unify types " + tp1 + " and " + tp2) + if (strict && args1.length != args2.length) unifyError(tp1, tp2) val e = unify(args1, args2, env, strict) - debuglog("unified to: " + e) + if (e.nonEmpty) debuglog("unified to: " + e) e case (TypeRef(_, sym1, _), _) if sym1.isTypeParameterOrSkolem => env case (MethodType(params1, res1), MethodType(params2, res2)) => if (strict && params1.length != params2.length) unifyError(tp1, tp2) - debuglog("Unify MethodTypes: " + tp1 + " and " + tp2) + debuglog("Unify methods " + tp1 + " and " + tp2) unify(res1 :: (params1 map (_.tpe)), res2 :: (params2 map (_.tpe)), env, strict) case (PolyType(tparams1, res1), PolyType(tparams2, res2)) => - debuglog("Unify PolyTypes: " + tp1 + " and " + tp2) + debuglog("Unify polytypes " + tp1 + " and " + tp2) if (strict && tparams1.length != tparams2.length) unifyError(tp1, tp2) else @@ -1123,11 +1136,12 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { if (tparams.isEmpty) afterSpecialize(parents map (_.typeSymbol.info)) - val parents1 = parents map specializedType - debuglog("transformInfo %s %s with parents1 %s ph: %s".format( - if (tparams.nonEmpty) " (poly)" else "", - clazz, parents1, phase) - ) + val parents1 = parents mapConserve specializedType + if (parents ne parents1) { + debuglog("specialization transforms %s%s parents to %s".format( + if (tparams.nonEmpty) "(poly) " else "", clazz, parents1) + ) + } val newScope = newScopeWith(specializeClass(clazz, typeEnv(clazz)) ++ specialOverrides(clazz): _*) // If tparams.isEmpty, this is just the ClassInfoType. GenPolyType(tparams, ClassInfoType(parents1, newScope, clazz)) @@ -1253,7 +1267,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { override def traverse(tree: Tree) = tree match { case DefDef(_, _, _, vparams :: Nil, _, rhs) => if (concreteSpecMethods(tree.symbol) || tree.symbol.isConstructor) { - debuglog("!!! adding body of a defdef %s, symbol %s: %s".format(tree, tree.symbol, rhs)) + // debuglog("!!! adding body of a defdef %s, symbol %s: %s".format(tree, tree.symbol, rhs)) body(tree.symbol) = rhs // body(tree.symbol) = tree // whole method parameters(tree.symbol) = vparams.map(_.symbol) @@ -1559,7 +1573,8 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { val env = typeEnv(symbol) val boundTvars = env.keySet val origtparams = source.typeParams.filter(tparam => !boundTvars(tparam) || !isScalaValueType(env(tparam))) - debuglog("substituting " + origtparams + " for " + symbol.typeParams) + if (origtparams.nonEmpty || symbol.typeParams.nonEmpty) + debuglog("substituting " + origtparams + " for " + symbol.typeParams) // skolemize type parameters val oldtparams = tparams map (_.symbol) @@ -1656,7 +1671,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { buf += ClassDef(specCls, atPos(impl.pos)(Template(parents, emptyValDef, List())) .setSymbol(specCls.newLocalDummy(sym1.pos))) setPos tree.pos - debuglog("created synthetic class: " + specCls + " of " + sym1 + " in env: " + env) + debuglog("created synthetic class: " + specCls + " of " + sym1 + " in " + pp(env)) } case _ => } diff --git a/src/library/scala/concurrent/Awaitable.scala b/src/library/scala/concurrent/Awaitable.scala index 6c9995eb05..052e6e2366 100644 --- a/src/library/scala/concurrent/Awaitable.scala +++ b/src/library/scala/concurrent/Awaitable.scala @@ -16,8 +16,13 @@ import scala.concurrent.util.Duration trait Awaitable[+T] { - @implicitNotFound(msg = "Waiting must be done by calling `blocking(timeout) b`, where `b` is the `Awaitable` object or a potentially blocking piece of code.") - def await(atMost: Duration)(implicit canawait: CanAwait): T + def ready(atMost: Duration)(implicit permit: CanAwait): this.type + + /** + * Throws exceptions if cannot produce a T within the specified time + * This method should not be called directly. + */ + def result(atMost: Duration)(implicit permit: CanAwait): T } diff --git a/src/library/scala/concurrent/ConcurrentPackageObject.scala b/src/library/scala/concurrent/ConcurrentPackageObject.scala index 3471095051..ba98757906 100644 --- a/src/library/scala/concurrent/ConcurrentPackageObject.scala +++ b/src/library/scala/concurrent/ConcurrentPackageObject.scala @@ -59,14 +59,18 @@ abstract class ConcurrentPackageObject { /* concurrency constructs */ def future[T](body: =>T)(implicit execCtx: ExecutionContext = executionContext): Future[T] = - execCtx future body + Future[T](body) def promise[T]()(implicit execCtx: ExecutionContext = executionContext): Promise[T] = - execCtx promise + Promise[T]() /** Wraps a block of code into an awaitable object. */ def body2awaitable[T](body: =>T) = new Awaitable[T] { - def await(atMost: Duration)(implicit cb: CanAwait) = body + def ready(atMost: Duration)(implicit permit: CanAwait) = { + body + this + } + def result(atMost: Duration)(implicit permit: CanAwait) = body } /** Used to block on a piece of code which potentially blocks. @@ -78,8 +82,8 @@ abstract class ConcurrentPackageObject { * - InterruptedException - in the case that a wait within the blockable object was interrupted * - TimeoutException - in the case that the blockable object timed out */ - def blocking[T](atMost: Duration)(body: =>T)(implicit execCtx: ExecutionContext): T = - executionContext.blocking(atMost)(body) + def blocking[T](body: =>T)(implicit execCtx: ExecutionContext): T = + executionContext.blocking(body) /** Blocks on an awaitable object. * diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index c4a45f9fb5..a206a2d4ea 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -22,19 +22,11 @@ import collection._ trait ExecutionContext { - protected implicit object CanAwaitEvidence extends CanAwait - def execute(runnable: Runnable): Unit def execute[U](body: () => U): Unit - def promise[T]: Promise[T] - - def future[T](body: Callable[T]): Future[T] = future(body.call()) - - def future[T](body: => T): Future[T] - - def blocking[T](atMost: Duration)(body: =>T): T + def blocking[T](body: =>T): T def blocking[T](awaitable: Awaitable[T], atMost: Duration): T @@ -44,89 +36,8 @@ trait ExecutionContext { private implicit val executionContext = this - def keptPromise[T](result: T): Promise[T] = { - val p = promise[T] - p success result - } - - def brokenPromise[T](t: Throwable): Promise[T] = { - val p = promise[T] - p failure t - } - - /** TODO some docs - * - */ - def all[T, Coll[X] <: Traversable[X]](futures: Coll[Future[T]])(implicit cbf: CanBuildFrom[Coll[_], T, Coll[T]]): Future[Coll[T]] = { - import nondeterministic._ - val buffer = new mutable.ArrayBuffer[T] - val counter = new AtomicInteger(1) // how else could we do this? - val p: Promise[Coll[T]] = promise[Coll[T]] // we need an implicit execctx in the signature - var idx = 0 - - def tryFinish() = if (counter.decrementAndGet() == 0) { - val builder = cbf(futures) - builder ++= buffer - p success builder.result - } - - for (f <- futures) { - val currentIndex = idx - buffer += null.asInstanceOf[T] - counter.incrementAndGet() - f onComplete { - case Failure(t) => - p tryFailure t - case Success(v) => - buffer(currentIndex) = v - tryFinish() - } - idx += 1 - } - - tryFinish() - - p.future - } - - /** TODO some docs - * - */ - def any[T](futures: Traversable[Future[T]]): Future[T] = { - val p = promise[T] - val completeFirst: Try[T] => Unit = elem => p tryComplete elem - - futures foreach (_ onComplete completeFirst) - - p.future - } - - /** TODO some docs - * - */ - def find[T](futures: Traversable[Future[T]])(predicate: T => Boolean): Future[Option[T]] = { - if (futures.isEmpty) Promise.kept[Option[T]](None).future - else { - val result = promise[Option[T]] - val count = new AtomicInteger(futures.size) - val search: Try[T] => Unit = { - v => v match { - case Success(r) => if (predicate(r)) result trySuccess Some(r) - case _ => - } - if (count.decrementAndGet() == 0) result trySuccess None - } - - futures.foreach(_ onComplete search) - - result.future - } - } - } -sealed trait CanAwait - diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 1dc8e38355..d73801aa90 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -134,8 +134,26 @@ self => /** Creates a new promise. */ def newPromise[S]: Promise[S] - - + + /** Returns whether the future has already been completed with + * a value or an exception. + * + * $nonDeterministic + * + * @return `true` if the future is already completed, `false` otherwise + */ + def isCompleted: Boolean + + /** The value of this `Future`. + * + * If the future is not completed the returned value will be `None`. + * If the future is completed the value will be `Some(Success(t))` + * if it contains a valid result, or `Some(Failure(error))` if it contains + * an exception. + */ + def value: Option[Try[T]] + + /* Projections */ /** Returns a failed projection of this future. @@ -235,8 +253,8 @@ self => * val f = future { 5 } * val g = f filter { _ % 2 == 1 } * val h = f filter { _ % 2 == 0 } - * await(0) g // evaluates to 5 - * await(0) h // throw a NoSuchElementException + * await(g, 0) // evaluates to 5 + * await(h, 0) // throw a NoSuchElementException * }}} */ def filter(pred: T => Boolean): Future[T] = { @@ -272,8 +290,8 @@ self => * val h = f collect { * case x if x > 0 => x * 2 * } - * await(0) g // evaluates to 5 - * await(0) h // throw a NoSuchElementException + * await(g, 0) // evaluates to 5 + * await(h, 0) // throw a NoSuchElementException * }}} */ def collect[S](pf: PartialFunction[T, S]): Future[S] = { @@ -383,7 +401,7 @@ self => * val f = future { sys.error("failed") } * val g = future { 5 } * val h = f orElse g - * await(0) h // evaluates to 5 + * await(h, 0) // evaluates to 5 * }}} */ def fallbackTo[U >: T](that: Future[U]): Future[U] = { @@ -445,7 +463,7 @@ self => * val f = future { sys.error("failed") } * val g = future { 5 } * val h = f either g - * await(0) h // evaluates to either 5 or throws a runtime exception + * await(h, 0) // evaluates to either 5 or throws a runtime exception * }}} */ def either[U >: T](that: Future[U]): Future[U] = { @@ -466,26 +484,102 @@ self => -/** TODO some docs +/** Future companion object. * * @define nonDeterministic * Note: using this method yields nondeterministic dataflow programs. */ object Future { + + def apply[T](body: =>T)(implicit executor: ExecutionContext): Future[T] = impl.Future(body) + + import scala.collection.mutable.Builder + import scala.collection.generic.CanBuildFrom + + /** Simple version of `Futures.traverse`. Transforms a `Traversable[Future[A]]` into a `Future[Traversable[A]]`. + * Useful for reducing many `Future`s into a single `Future`. + */ + def sequence[A, M[_] <: Traversable[_]](in: M[Future[A]])(implicit cbf: CanBuildFrom[M[Future[A]], A, M[A]], executor: ExecutionContext): Future[M[A]] = { + in.foldLeft(Promise.successful(cbf(in)).future) { + (fr, fa) => for (r <- fr; a <- fa.asInstanceOf[Future[A]]) yield (r += a) + } map (_.result) + } + + /** Returns a `Future` to the result of the first future in the list that is completed. + */ + def firstCompletedOf[T](futures: Traversable[Future[T]])(implicit executor: ExecutionContext): Future[T] = { + val p = Promise[T]() + + val completeFirst: Try[T] => Unit = p tryComplete _ + futures.foreach(_ onComplete completeFirst) - // TODO make more modular by encoding all other helper methods within the execution context - /** TODO some docs + p.future + } + + /** Returns a `Future` that will hold the optional result of the first `Future` with a result that matches the predicate. */ - def all[T, Coll[X] <: Traversable[X]](futures: Coll[Future[T]])(implicit cbf: CanBuildFrom[Coll[_], T, Coll[T]], ec: ExecutionContext): Future[Coll[T]] = - ec.all[T, Coll](futures) + def find[T](futures: Traversable[Future[T]])(predicate: T => Boolean)(implicit executor: ExecutionContext): Future[Option[T]] = { + if (futures.isEmpty) Promise.successful[Option[T]](None).future + else { + val result = Promise[Option[T]]() + val ref = new AtomicInteger(futures.size) + val search: Try[T] => Unit = v => try { + v match { + case Success(r) => if (predicate(r)) result tryComplete Success(Some(r)) + case _ => + } + } finally { + if (ref.decrementAndGet == 0) + result tryComplete Success(None) + } - // move this to future companion object - @inline def apply[T](body: =>T)(implicit executor: ExecutionContext): Future[T] = executor.future(body) + futures.foreach(_ onComplete search) - def any[T](futures: Traversable[Future[T]])(implicit ec: ExecutionContext): Future[T] = ec.any(futures) + result.future + } + } - def find[T](futures: Traversable[Future[T]])(predicate: T => Boolean)(implicit ec: ExecutionContext): Future[Option[T]] = ec.find(futures)(predicate) + /** A non-blocking fold over the specified futures, with the start value of the given zero. + * The fold is performed on the thread where the last future is completed, + * the result will be the first failure of any of the futures, or any failure in the actual fold, + * or the result of the fold. + * + * Example: + * {{{ + * val result = Await.result(Future.fold(futures)(0)(_ + _), 5 seconds) + * }}} + */ + def fold[T, R](futures: Traversable[Future[T]])(zero: R)(foldFun: (R, T) => R)(implicit executor: ExecutionContext): Future[R] = { + if (futures.isEmpty) Promise.successful(zero).future + else sequence(futures).map(_.foldLeft(zero)(foldFun)) + } + /** Initiates a fold over the supplied futures where the fold-zero is the result value of the `Future` that's completed first. + * + * Example: + * {{{ + * val result = Await.result(Futures.reduce(futures)(_ + _), 5 seconds) + * }}} + */ + def reduce[T, R >: T](futures: Traversable[Future[T]])(op: (R, T) => R)(implicit executor: ExecutionContext): Future[R] = { + if (futures.isEmpty) Promise[R].failure(new NoSuchElementException("reduce attempted on empty collection")).future + else sequence(futures).map(_ reduceLeft op) + } + + /** Transforms a `Traversable[A]` into a `Future[Traversable[B]]` using the provided function `A => Future[B]`. + * This is useful for performing a parallel map. For example, to apply a function to all items of a list + * in parallel: + * + * {{{ + * val myFutureList = Future.traverse(myList)(x => Future(myFunc(x))) + * }}} + */ + def traverse[A, B, M[_] <: Traversable[_]](in: M[A])(fn: A => Future[B])(implicit cbf: CanBuildFrom[M[A], B, M[B]], executor: ExecutionContext): Future[M[B]] = + in.foldLeft(Promise.successful(cbf(in)).future) { (fr, a) => + val fb = fn(a.asInstanceOf[A]) + for (r <- fr; b <- fb) yield (r += b) + }.map(_.result) + } diff --git a/src/library/scala/concurrent/Promise.scala b/src/library/scala/concurrent/Promise.scala index 4404e90971..61e21606e6 100644 --- a/src/library/scala/concurrent/Promise.scala +++ b/src/library/scala/concurrent/Promise.scala @@ -30,8 +30,6 @@ import scala.util.{ Try, Success, Failure } */ trait Promise[T] { - import nondeterministic._ - /** Future containing the value of this promise. */ def future: Future[T] @@ -114,12 +112,18 @@ trait Promise[T] { object Promise { - def kept[T](result: T)(implicit execctx: ExecutionContext): Promise[T] = - execctx keptPromise result + /** Creates a new promise. + */ + def apply[T]()(implicit executor: ExecutionContext): Promise[T] = new impl.Promise.DefaultPromise[T]() - def broken[T](t: Throwable)(implicit execctx: ExecutionContext): Promise[T] = - execctx brokenPromise t + /** Creates an already completed Promise with the specified exception + */ + def failed[T](exception: Throwable)(implicit executor: ExecutionContext): Promise[T] = new impl.Promise.KeptPromise[T](Failure(exception)) + /** Creates an already completed Promise with the specified result + */ + def successful[T](result: T)(implicit executor: ExecutionContext): Promise[T] = new impl.Promise.KeptPromise[T](Success(result)) + } diff --git a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala index 8ac745fd25..5dc440f42b 100644 --- a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala +++ b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala @@ -15,7 +15,6 @@ import scala.concurrent.forkjoin._ import scala.concurrent.{ExecutionContext, resolver, Awaitable, body2awaitable} import scala.util.{ Try, Success, Failure } import scala.concurrent.util.{ Duration } -import scala.collection.mutable.Stack @@ -38,32 +37,12 @@ private[scala] class ExecutionContextImpl(val executorService: AnyRef) extends E def run() = body() }) - def promise[T]: Promise[T] = new Promise.DefaultPromise[T]()(this) - - def future[T](body: =>T): Future[T] = { - val p = promise[T] - - dispatchFuture { - () => - p complete { - try { - Success(body) - } catch { - case e => resolver(e) - } - } - } - - p.future - } - - def blocking[T](atMost: Duration)(body: =>T): T = blocking(body2awaitable(body), atMost) + def blocking[T](body: =>T): T = blocking(body2awaitable(body), Duration.fromNanos(0)) def blocking[T](awaitable: Awaitable[T], atMost: Duration): T = { - currentExecutionContext.get match { - case null => awaitable.await(atMost)(null) // outside - TODO - fix timeout case - case x => x.blockingCall(awaitable) // inside an execution context thread - } + Future.releaseStack(this) + + awaitable.result(atMost)(scala.concurrent.Await.canAwaitEvidence) } def reportFailure(t: Throwable) = t match { @@ -71,61 +50,6 @@ private[scala] class ExecutionContextImpl(val executorService: AnyRef) extends E case t => t.printStackTrace() } - /** Only callable from the tasks running on the same execution context. */ - private def blockingCall[T](body: Awaitable[T]): T = { - releaseStack() - - // TODO see what to do with timeout - body.await(Duration.fromNanos(0))(CanAwaitEvidence) - } - - // an optimization for batching futures - // TODO we should replace this with a public queue, - // so that it can be stolen from - // OR: a push to the local task queue should be so cheap that this is - // not even needed, but stealing is still possible - private val _taskStack = new ThreadLocal[Stack[() => Unit]]() - - private def releaseStack(): Unit = - _taskStack.get match { - case stack if (stack ne null) && stack.nonEmpty => - val tasks = stack.elems - stack.clear() - _taskStack.remove() - dispatchFuture(() => _taskStack.get.elems = tasks, true) - case null => - // do nothing - there is no local batching stack anymore - case _ => - _taskStack.remove() - } - - private[impl] def dispatchFuture(task: () => Unit, force: Boolean = false): Unit = - _taskStack.get match { - case stack if (stack ne null) && !force => stack push task - case _ => this.execute( - new Runnable { - def run() { - try { - val taskStack = Stack[() => Unit](task) - _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 - reportFailure(e) - } - } - } finally { - _taskStack.remove() - } - } - } - ) - } - } diff --git a/src/library/scala/concurrent/impl/Future.scala b/src/library/scala/concurrent/impl/Future.scala index b4385ea34a..6833b2467f 100644 --- a/src/library/scala/concurrent/impl/Future.scala +++ b/src/library/scala/concurrent/impl/Future.scala @@ -8,13 +8,17 @@ package scala.concurrent.impl + + import scala.concurrent.{Awaitable, ExecutionContext} import scala.util.{ Try, Success, Failure } -//import scala.util.continuations._ +import scala.collection.mutable.Stack + + private[concurrent] trait Future[+T] extends scala.concurrent.Future[T] with Awaitable[T] { - implicit def executor: ExecutionContextImpl + implicit def executor: ExecutionContext /** For use only within a Future.flow block or another compatible Delimited Continuations reset block. * @@ -40,7 +44,7 @@ private[concurrent] trait Future[+T] extends scala.concurrent.Future[T] with Awa * that conforms to A's erased type or a ClassCastException otherwise. */ final def mapTo[T](implicit m: Manifest[T]) = { - val p = executor.promise[T] + val p = new Promise.DefaultPromise[T] onComplete { case f @ Failure(t) => p complete f.asInstanceOf[Try[T]] @@ -48,7 +52,7 @@ private[concurrent] trait Future[+T] extends scala.concurrent.Future[T] with Awa p complete (try { Success(Future.boxedType(m.erasure).cast(v).asInstanceOf[T]) } catch { - case e: ClassCastException ⇒ Failure(e) + case e: ClassCastException => Failure(e) }) } @@ -86,4 +90,66 @@ object Future { 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]() + executor.execute(new Runnable { + def run = { + promise complete { + try { + Success(body) + } catch { + case e => scala.concurrent.resolver(e) + } + } + } + }) + promise.future + } + + // an optimization for batching futures + // TODO we should replace this with a public queue, + // so that it can be stolen from + // OR: a push to the local task queue should be so cheap that this is + // not even needed, but stealing is still possible + private val _taskStack = new ThreadLocal[Stack[() => Unit]]() + + private[impl] def releaseStack(executor: ExecutionContext): Unit = + _taskStack.get match { + case stack if (stack ne null) && stack.nonEmpty => + val tasks = stack.elems + stack.clear() + _taskStack.remove() + dispatchFuture(executor, () => _taskStack.get.elems = tasks, true) + case null => + // do nothing - there is no local batching stack anymore + case _ => + _taskStack.remove() + } + + 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 _ => executor.execute(new Runnable { + def run() { + try { + val taskStack = Stack[() => Unit](task) + _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) + } + } + } finally { + _taskStack.remove() + } + } + }) + } + } diff --git a/src/library/scala/concurrent/impl/Promise.scala b/src/library/scala/concurrent/impl/Promise.scala index 4a983b5001..c79b0d02cc 100644 --- a/src/library/scala/concurrent/impl/Promise.scala +++ b/src/library/scala/concurrent/impl/Promise.scala @@ -26,7 +26,7 @@ private[concurrent] trait Promise[T] extends scala.concurrent.Promise[T] with Fu def future = this - def newPromise[S]: Promise[S] = executor promise + def newPromise[S]: scala.concurrent.Promise[S] = new Promise.DefaultPromise() // TODO refine answer and return types here from Any to type parameters // then move this up in the hierarchy @@ -75,6 +75,7 @@ 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]] @@ -101,7 +102,7 @@ object Promise { /** Default promise implementation. */ - class DefaultPromise[T](implicit val executor: ExecutionContextImpl) extends AbstractPromise with Promise[T] { + class DefaultPromise[T](implicit val executor: ExecutionContext) extends AbstractPromise with Promise[T] { self => updater.set(this, Promise.EmptyPending()) @@ -126,14 +127,14 @@ object Promise { value.isDefined } - executor.blocking(concurrent.body2awaitable(awaitUnsafe(dur2long(atMost))), Duration.fromNanos(0)) + executor.blocking(concurrent.body2awaitable(awaitUnsafe(dur2long(atMost))), atMost) } - private def ready(atMost: Duration)(implicit permit: CanAwait): this.type = + def ready(atMost: Duration)(implicit permit: CanAwait): this.type = if (value.isDefined || tryAwait(atMost)) this else throw new TimeoutException("Futures timed out after [" + atMost.toMillis + "] milliseconds") - def await(atMost: Duration)(implicit permit: CanAwait): T = + def result(atMost: Duration)(implicit permit: CanAwait): T = ready(atMost).value.get match { case util.Failure(e) => throw e case util.Success(r) => r @@ -176,9 +177,9 @@ object Promise { case null => false case cs if cs.isEmpty => true case cs => - executor dispatchFuture { + Future.dispatchFuture(executor, { () => cs.foreach(f => notifyCompleted(f, value)) - } + }) true } } @@ -197,9 +198,9 @@ object Promise { if (tryAddCallback()) { val result = value.get - executor dispatchFuture { + Future.dispatchFuture(executor, { () => notifyCompleted(func, result) - } + }) } this @@ -218,22 +219,22 @@ object Promise { * * Useful in Future-composition when a value to contribute is already available. */ - final class KeptPromise[T](suppliedValue: Try[T])(implicit val executor: ExecutionContextImpl) extends Promise[T] { + final class KeptPromise[T](suppliedValue: Try[T])(implicit val executor: ExecutionContext) extends Promise[T] { val value = Some(resolve(suppliedValue)) def tryComplete(value: Try[T]): Boolean = false def onComplete[U](func: Try[T] => U): this.type = { val completedAs = value.get - executor dispatchFuture { + Future.dispatchFuture(executor, { () => func(completedAs) - } + }) this } - private def ready(atMost: Duration)(implicit permit: CanAwait): this.type = this + def ready(atMost: Duration)(implicit permit: CanAwait): this.type = this - def await(atMost: Duration)(implicit permit: CanAwait): T = value.get match { + def result(atMost: Duration)(implicit permit: CanAwait): T = value.get match { case util.Failure(e) => throw e case util.Success(r) => r } diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index 204b3f2673..e2ae17498f 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -20,27 +20,17 @@ package object concurrent extends scala.concurrent.ConcurrentPackageObject { } package concurrent { - object await { - def ready[T](atMost: Duration)(awaitable: Awaitable[T])(implicit execCtx: ExecutionContext = executionContext): Awaitable[T] = { - try blocking(awaitable, atMost) - catch { case _ => } - awaitable - } - - def result[T](atMost: Duration)(awaitable: Awaitable[T])(implicit execCtx: ExecutionContext = executionContext): T = { - blocking(awaitable, atMost) - } + + sealed trait CanAwait + + object Await { + private[concurrent] implicit val canAwaitEvidence = new CanAwait {} + + def ready[T](awaitable: Awaitable[T], atMost: Duration): Awaitable[T] = awaitable.ready(atMost) + + def result[T](awaitable: Awaitable[T], atMost: Duration): T = awaitable.result(atMost) } - /** Importing this object allows using some concurrency primitives - * on futures and promises that can yield nondeterministic programs. - * - * While program determinism is broken when using these primitives, - * some programs cannot be written without them (e.g. multiple client threads - * cannot send requests to a server thread through regular promises and futures). - */ - object nondeterministic { } - /** A timeout exception. * * Futures are failed with a timeout exception when their timeout expires. diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala index 70221c0de1..75e2b92ff6 100644 --- a/test/files/jvm/scala-concurrent-tck.scala +++ b/test/files/jvm/scala-concurrent-tck.scala @@ -74,7 +74,7 @@ trait FutureCallbacks extends TestBase { done() throw new Exception } - f onSuccess { + f onSuccess { case _ => assert(false) } } diff --git a/test/pending/pos/t1476.scala b/test/pending/pos/t1476.scala new file mode 100644 index 0000000000..1f8e95c28f --- /dev/null +++ b/test/pending/pos/t1476.scala @@ -0,0 +1,23 @@ +abstract class Module { + def moduleDemands(): List[Module] +} + +object Test { + new Module { owner: Module => + def moduleDemands() = Nil + + val a = new Module { def moduleDemands(): List[Module] = Nil } + val b = new Module { def moduleDemands(): List[Module] = owner :: c :: Nil } + val c = new Module { def moduleDemands(): List[Module] = owner :: a :: Nil } + } +} + +object Test2 { + new Module { owner => + def moduleDemands() = Nil + + val a = new Module { def moduleDemands(): List[Module] = Nil } + val b = new Module { def moduleDemands(): List[Module] = owner :: c :: Nil } + val c = new Module { def moduleDemands(): List[Module] = owner :: a :: Nil } + } +} diff --git a/test/pending/pos/t5626.scala b/test/pending/pos/t5626.scala new file mode 100644 index 0000000000..7ab3881827 --- /dev/null +++ b/test/pending/pos/t5626.scala @@ -0,0 +1,12 @@ +object Test { + val blob0 = new { + case class Foo(i : Int) + } + val foo0 = blob0.Foo(22) + + val blob1 = new { + class Foo(i: Int) + object Foo { def apply(i: Int): Foo = new Foo(i) } + } + val foo1 = blob1.Foo(22) +} diff --git a/test/pending/run/t5629.check b/test/pending/run/t5629.check new file mode 100644 index 0000000000..6a2d630f4e --- /dev/null +++ b/test/pending/run/t5629.check @@ -0,0 +1,2 @@ +int child got: 33 +any child got: 33 diff --git a/test/pending/run/t5629.scala b/test/pending/run/t5629.scala new file mode 100644 index 0000000000..28e74a1c94 --- /dev/null +++ b/test/pending/run/t5629.scala @@ -0,0 +1,25 @@ +import scala.{specialized => spec} + +trait GrandParent[@spec(Int) -A] { + def foo(a:A): Unit + def bar[B <: A](b:B): Unit = println("grandparent got: %s" format b) +} + +trait Parent[@spec(Int) -A] extends GrandParent[A] { + def foo(a:A) = bar(a) +} + +class IntChild extends Parent[Int] { + override def bar[B <: Int](b:B): Unit = println("int child got: %s" format b) +} + +class AnyChild extends Parent[Any] { + override def bar[B <: Any](b:B): Unit = println("any child got: %s" format b) +} + +object Test { + def main(args:Array[String]) { + new IntChild().foo(33) + new AnyChild().foo(33) + } +} |