diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala | 9 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Namers.scala | 4 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/RefChecks.scala | 10 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/util/ClassPath.scala | 12 | ||||
-rw-r--r-- | src/library/scala/concurrent/Future.scala | 11 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/Flags.scala | 13 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/HasFlags.scala | 5 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/Symbols.scala | 2 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/TreeGen.scala | 2 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/Types.scala | 2 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/tpe/TypeMaps.scala | 2 | ||||
-rw-r--r-- | test/files/pos/t7815.scala | 30 | ||||
-rw-r--r-- | test/files/run/future-flatmap-exec-count.check | 6 | ||||
-rw-r--r-- | test/files/run/future-flatmap-exec-count.scala | 61 | ||||
-rw-r--r-- | test/files/run/t7398.scala | 6 | ||||
-rw-r--r-- | test/files/run/t7825.scala | 34 |
16 files changed, 176 insertions, 33 deletions
diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala index 6f27eb8660..8b739958ff 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -164,10 +164,17 @@ abstract class SymbolLoaders { if (settings.verbose) inform("[symloader] no class, picked up source file for " + src.path) enterToplevelsFromSource(owner, classRep.name, src) case (Some(bin), _) => - enterClassAndModule(owner, classRep.name, new ClassfileLoader(bin)) + enterClassAndModule(owner, classRep.name, newClassLoader(bin)) } } + /** Create a new loader from a binary classfile. + * This is intented as a hook allowing to support loading symbols from + * files other than .class files. + */ + protected def newClassLoader(bin: AbstractFile): SymbolLoader = + new ClassfileLoader(bin) + /** * A lazy type that completes itself by calling parameter doComplete. * Any linked modules/classes or module classes are also initialized. diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 3bbf18fe3b..12e8b0a3f9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -1272,9 +1272,7 @@ trait Namers extends MethodSynthesis { val defRhs = copyUntyped(vparam.rhs) val defaultTree = atPos(vparam.pos.focus) { - DefDef( - Modifiers(meth.flags & DefaultGetterFlags) | (SYNTHETIC | DEFAULTPARAM | oflag).toLong, - name, deftParams, defvParamss, defTpt, defRhs) + DefDef(Modifiers(paramFlagsToDefaultGetter(meth.flags)) | oflag, name, deftParams, defvParamss, defTpt, defRhs) } if (!isConstr) methOwner.resetFlag(INTERFACE) // there's a concrete member now diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 61759a9f00..f3aed66bc5 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -127,14 +127,14 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // those with the DEFAULTPARAM flag, and infer the methods. Looking for the methods // directly requires inspecting the parameter list of every one. That modification // shaved 95% off the time spent in this method. - val defaultGetters = defaultClass.info.findMembers(0L, DEFAULTPARAM) + val defaultGetters = defaultClass.info.findMembers(excludedFlags = PARAM, requiredFlags = DEFAULTPARAM) val defaultMethodNames = defaultGetters map (sym => nme.defaultGetterToMethod(sym.name)) defaultMethodNames.toList.distinct foreach { name => - val methods = clazz.info.findMember(name, 0L, METHOD, stableOnly = false).alternatives + val methods = clazz.info.findMember(name, 0L, requiredFlags = METHOD, stableOnly = false).alternatives def hasDefaultParam(tpe: Type): Boolean = tpe match { case MethodType(params, restpe) => (params exists (_.hasDefault)) || hasDefaultParam(restpe) - case _ => false + case _ => false } val haveDefaults = methods filter (sym => hasDefaultParam(sym.info) && !nme.isProtectedAccessorName(sym.name)) @@ -411,7 +411,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans overrideError("cannot be used here - classes can only override abstract types") } else if (other.isEffectivelyFinal) { // (1.2) overrideError("cannot override final member") - } else if (!other.isDeferred && !other.hasFlag(DEFAULTMETHOD) && !member.isAnyOverride && !member.isSynthetic) { // (*) + } else if (!other.isDeferredOrDefault && !other.hasFlag(DEFAULTMETHOD) && !member.isAnyOverride && !member.isSynthetic) { // (*) // (*) Synthetic exclusion for (at least) default getters, fixes SI-5178. We cannot assign the OVERRIDE flag to // the default getter: one default getter might sometimes override, sometimes not. Example in comment on ticket. if (isNeitherInClass && !(other.owner isSubClass member.owner)) @@ -596,7 +596,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans def checkNoAbstractMembers(): Unit = { // Avoid spurious duplicates: first gather any missing members. def memberList = clazz.info.nonPrivateMembersAdmitting(VBRIDGE) - val (missing, rest) = memberList partition (m => m.isDeferred && !ignoreDeferred(m)) + val (missing, rest) = memberList partition (m => m.isDeferredNotDefault && !ignoreDeferred(m)) // Group missing members by the name of the underlying symbol, // to consolidate getters and setters. val grouped = missing groupBy (sym => analyzer.underlyingSymbol(sym).name) diff --git a/src/compiler/scala/tools/nsc/util/ClassPath.scala b/src/compiler/scala/tools/nsc/util/ClassPath.scala index 906a575d90..c63d7f6820 100644 --- a/src/compiler/scala/tools/nsc/util/ClassPath.scala +++ b/src/compiler/scala/tools/nsc/util/ClassPath.scala @@ -96,6 +96,12 @@ object ClassPath { */ def isValidName(name: String): Boolean = true + /** Filters for assessing validity of various entities. + */ + def validClassFile(name: String) = endsClass(name) && isValidName(name) + def validPackage(name: String) = (name != "META-INF") && (name != "") && (name.charAt(0) != '.') + def validSourceFile(name: String) = endsScala(name) || endsJava(name) + /** From the representation to its identifier. */ def toBinaryName(rep: T): String @@ -208,9 +214,9 @@ abstract class ClassPath[T] { /** Filters for assessing validity of various entities. */ - def validClassFile(name: String) = endsClass(name) && context.isValidName(name) - def validPackage(name: String) = (name != "META-INF") && (name != "") && (name.charAt(0) != '.') - def validSourceFile(name: String) = endsScala(name) || endsJava(name) + def validClassFile(name: String) = context.validClassFile(name) + def validPackage(name: String) = context.validPackage(name) + def validSourceFile(name: String) = context.validSourceFile(name) /** * Find a ClassRep given a class name of the form "package.subpackage.ClassName". diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 4e3f3c6c81..b9f73c2872 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -96,11 +96,8 @@ trait Future[+T] extends Awaitable[T] { // of the Future trait. Note that this will // (modulo bugs) _never_ execute a callback // other than those below in this same file. - // As a nice side benefit, having this implicit - // here forces an ambiguity in those methods - // that also have an executor parameter, which - // keeps us from accidentally forgetting to use - // the executor parameter. + // + // See the documentation on `InternalCallbackExecutor` for more details. private def internalExecutor = Future.InternalCallbackExecutor /* Callbacks */ @@ -254,7 +251,7 @@ trait Future[+T] extends Awaitable[T] { case Success(v) => try f(v) match { // If possible, link DefaultPromises to avoid space leaks case dp: DefaultPromise[_] => dp.asInstanceOf[DefaultPromise[S]].linkRootOf(p) - case fut => fut onComplete p.complete + case fut => fut.onComplete(p.complete)(internalExecutor) } catch { case NonFatal(t) => p failure t } } p.future @@ -344,7 +341,7 @@ trait Future[+T] extends Awaitable[T] { def recoverWith[U >: T](pf: PartialFunction[Throwable, Future[U]])(implicit executor: ExecutionContext): Future[U] = { val p = Promise[U]() onComplete { - case Failure(t) => try pf.applyOrElse(t, (_: Throwable) => this) onComplete p.complete catch { case NonFatal(t) => p failure t } + case Failure(t) => try pf.applyOrElse(t, (_: Throwable) => this).onComplete(p.complete)(internalExecutor) catch { case NonFatal(t) => p failure t } case other => p complete other } p.future diff --git a/src/reflect/scala/reflect/internal/Flags.scala b/src/reflect/scala/reflect/internal/Flags.scala index c286ea53c6..dcdf6728ce 100644 --- a/src/reflect/scala/reflect/internal/Flags.scala +++ b/src/reflect/scala/reflect/internal/Flags.scala @@ -279,11 +279,13 @@ class Flags extends ModifierFlags { final val GetterFlags = ~(PRESUPER | MUTABLE) final val SetterFlags = ~(PRESUPER | MUTABLE | STABLE | CASEACCESSOR | IMPLICIT) - /** When a symbol for a default getter is created, it inherits these - * flags from the method with the default. Other flags applied at creation - * time are SYNTHETIC, DEFAULTPARAM, and possibly OVERRIDE, and maybe PRESUPER. + /** Since DEFAULTPARAM is overloaded with TRAIT, we need some additional + * means of determining what that bit means. Usually DEFAULTPARAM is coupled + * with PARAM, which suffices. Default getters get METHOD instead. + * This constant is the mask of flags which can survive from the parameter modifiers. + * See paramFlagsToDefaultGetter for the full logic. */ - final val DefaultGetterFlags = PRIVATE | PROTECTED | FINAL + final val DefaultGetterFlags = PRIVATE | PROTECTED | FINAL | PARAMACCESSOR /** When a symbol for a method parameter is created, only these flags survive * from Modifiers. Others which may be applied at creation time are: @@ -321,6 +323,9 @@ class Flags extends ModifierFlags { */ final val TopLevelPickledFlags = PickledFlags & ~(MODULE | METHOD | PACKAGE | PARAM | EXISTENTIAL) + def paramFlagsToDefaultGetter(paramFlags: Long): Long = + (paramFlags & DefaultGetterFlags) | SYNTHETIC | METHOD | DEFAULTPARAM + def getterFlags(fieldFlags: Long): Long = ACCESSOR + ( if ((fieldFlags & MUTABLE) != 0) fieldFlags & ~MUTABLE & ~PRESUPER else fieldFlags & ~PRESUPER | STABLE diff --git a/src/reflect/scala/reflect/internal/HasFlags.scala b/src/reflect/scala/reflect/internal/HasFlags.scala index 420b5fef81..4e12998ac7 100644 --- a/src/reflect/scala/reflect/internal/HasFlags.scala +++ b/src/reflect/scala/reflect/internal/HasFlags.scala @@ -81,7 +81,7 @@ trait HasFlags { // identically, testing for a single flag. def hasAbstractFlag = hasFlag(ABSTRACT) def hasAccessorFlag = hasFlag(ACCESSOR) - def hasDefault = hasAllFlags(DEFAULTPARAM | PARAM) + def hasDefault = hasFlag(DEFAULTPARAM) && hasFlag(METHOD | PARAM) // Second condition disambiguates with TRAIT def hasLocalFlag = hasFlag(LOCAL) def hasModuleFlag = hasFlag(MODULE) def hasPackageFlag = hasFlag(PACKAGE) @@ -116,6 +116,9 @@ trait HasFlags { def isSynthetic = hasFlag(SYNTHETIC) def isTrait = hasFlag(TRAIT) && !hasFlag(PARAM) + def isDeferredOrDefault = hasFlag(DEFERRED | DEFAULTMETHOD) + def isDeferredNotDefault = isDeferred && !hasFlag(DEFAULTMETHOD) + def flagBitsToString(bits: Long): String = { // Fast path for common case if (bits == 0L) "" else { diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index d58ea09386..14d742fc2c 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -571,7 +571,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => def isValue = false def isValueParameter = false def isVariable = false - override def hasDefault = false def isTermMacro = false /** Qualities of MethodSymbols, always false for TypeSymbols @@ -2601,7 +2600,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def companionSymbol: Symbol = companionClass override def moduleClass = if (isModule) referenced else NoSymbol - override def hasDefault = this hasFlag DEFAULTPARAM // overloaded with TRAIT override def isBridge = this hasFlag BRIDGE override def isEarlyInitialized = this hasFlag PRESUPER override def isMethod = this hasFlag METHOD diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index c8ae4df13d..9be1768289 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -376,7 +376,7 @@ abstract class TreeGen extends macros.TreeBuilder { DefDef(constrMods, nme.CONSTRUCTOR, List(), vparamss1, TypeTree(), Block(lvdefs ::: List(superCall), Literal(Constant()))))) } } - constr foreach (ensureNonOverlapping(_, parents ::: gvdefs, focus=false)) + constr foreach (ensureNonOverlapping(_, parents ::: gvdefs, focus = false)) // Field definitions for the class - remove defaults. val fieldDefs = vparamss.flatten map (vd => copyValDef(vd)(mods = vd.mods &~ DEFAULTPARAM, rhs = EmptyTree)) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index dd6917814e..77f4b1b70c 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -2553,7 +2553,7 @@ trait Types private var isdepmeth: ThreeValue = UNKNOWN override def isDependentMethodType: Boolean = { - if (isdepmeth == UNKNOWN) isdepmeth = fromBoolean(IsDependentCollector.collect(resultType)) + if (isdepmeth == UNKNOWN) isdepmeth = fromBoolean(IsDependentCollector.collect(resultType.dealias)) toBoolean(isdepmeth) } diff --git a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala index be61c45041..d6310c8cec 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala @@ -841,7 +841,7 @@ private[internal] trait TypeMaps { object IsDependentCollector extends TypeCollector(false) { def traverse(tp: Type) { if (tp.isImmediatelyDependent) result = true - else if (!result) mapOver(tp) + else if (!result) mapOver(tp.dealias) } } diff --git a/test/files/pos/t7815.scala b/test/files/pos/t7815.scala new file mode 100644 index 0000000000..12a434c5b0 --- /dev/null +++ b/test/files/pos/t7815.scala @@ -0,0 +1,30 @@ +import language.higherKinds + +trait Foo[A <: AnyRef] { + type Repr + def f(a: A): Repr + def g(a: A): Option[Repr] + + type M[X] + def m(a: A): M[a.type] + + type Id[X] = X + def n(a: A): Id[(Repr, M[a.type])] + +} + +object Foo { + type Aux[A <: AnyRef, B] = Foo[A] { type Repr = B; type M[X] = Int } + +} + +object Main extends App { + def mapWithFoo[A <: AnyRef, B](as: List[A])(implicit foo: Foo.Aux[A, B]) = { + // Should be Eta expandable because the result type of `f` is not + // dependant on the value, it is just `B`. + as map foo.f + as map foo.g + as map foo.m + as map foo.n + } +} diff --git a/test/files/run/future-flatmap-exec-count.check b/test/files/run/future-flatmap-exec-count.check new file mode 100644 index 0000000000..dd9dce64ed --- /dev/null +++ b/test/files/run/future-flatmap-exec-count.check @@ -0,0 +1,6 @@ +mapping +execute() +flatmapping +execute() +recovering +execute() diff --git a/test/files/run/future-flatmap-exec-count.scala b/test/files/run/future-flatmap-exec-count.scala new file mode 100644 index 0000000000..86c37be938 --- /dev/null +++ b/test/files/run/future-flatmap-exec-count.scala @@ -0,0 +1,61 @@ +import scala.concurrent._ +import java.util.concurrent.atomic.AtomicInteger + +object Test { + def main(args: Array[String]) { + test() + } + + def test() = { + def await(f: Future[Any]) = + Await.result(f, duration.Duration.Inf) + + val ec = new TestExecutionContext(ExecutionContext.Implicits.global) + + { + val p = Promise[Int]() + val fp = p.future + println("mapping") + val mapped = fp.map(x => x)(ec) + p.success(0) + await(mapped) + } + + { + println("flatmapping") + val p = Promise[Int]() + val fp = p.future + val flatMapped = fp.flatMap({ (x: Int) => + Future.successful(2 * x) + })(ec) + p.success(0) + await(flatMapped) + } + + { + println("recovering") + val recovered = Future.failed(new Throwable()).recoverWith { + case _ => Future.successful(2) + }(ec) + await(recovered) + } + } + + class TestExecutionContext(delegate: ExecutionContext) extends ExecutionContext { + def execute(runnable: Runnable): Unit = ??? + + def reportFailure(t: Throwable): Unit = ??? + + override def prepare(): ExecutionContext = { + val preparedDelegate = delegate.prepare() + return new ExecutionContext { + def execute(runnable: Runnable): Unit = { + println("execute()") + preparedDelegate.execute(runnable) + } + + def reportFailure(t: Throwable): Unit = ??? + } + } + } +} diff --git a/test/files/run/t7398.scala b/test/files/run/t7398.scala index dd59697b71..493c4dcf40 100644 --- a/test/files/run/t7398.scala +++ b/test/files/run/t7398.scala @@ -3,11 +3,9 @@ import scala.tools.partest._ object Test extends CompilerTest { import global._ - // This way we auto-pass on non-java8 since there's nothing to check - override lazy val units: List[CompilationUnit] = testUnderJavaAtLeast("1.8") { + override lazy val units: List[CompilationUnit] = { + // This test itself does not depend on JDK8. javaCompilationUnits(global)(defaultMethodSource) - } otherwise { - Nil } private def defaultMethodSource = """ diff --git a/test/files/run/t7825.scala b/test/files/run/t7825.scala new file mode 100644 index 0000000000..65ca06fdfc --- /dev/null +++ b/test/files/run/t7825.scala @@ -0,0 +1,34 @@ +import scala.tools.partest._ + +object Test extends CompilerTest { + import global._ + + override lazy val units: List[CompilationUnit] = { + // We can test this on JDK6. + javaCompilationUnits(global)(defaultMethodSource) ++ compilationUnits(global)(scalaExtendsDefault) + } + + private def defaultMethodSource = """ +public interface Iterator<E> { + boolean hasNext(); + E next(); + default void remove() { + throw new UnsupportedOperationException("remove"); + } +} + """ + + private def scalaExtendsDefault = """ +object Test { + object X extends Iterator[String] { + def hasNext = true + def next = "!" + } +} + """ + + // We're only checking we that the Scala compilation unit passes refchecks + // No further checks are needed here. + def check(source: String, unit: global.CompilationUnit): Unit = { + } +} |