summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala4
-rw-r--r--src/library/scala/concurrent/Future.scala11
-rw-r--r--src/reflect/scala/reflect/internal/HasFlags.scala3
-rw-r--r--src/reflect/scala/reflect/internal/Types.scala2
-rw-r--r--src/reflect/scala/reflect/internal/tpe/TypeMaps.scala2
-rw-r--r--test/files/pos/t7815.scala30
-rw-r--r--test/files/run/future-flatmap-exec-count.check6
-rw-r--r--test/files/run/future-flatmap-exec-count.scala61
-rw-r--r--test/files/run/t7398.scala6
-rw-r--r--test/files/run/t7825.scala34
10 files changed, 144 insertions, 15 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index 61759a9f00..d921868f31 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -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/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/HasFlags.scala b/src/reflect/scala/reflect/internal/HasFlags.scala
index 420b5fef81..292b784c0c 100644
--- a/src/reflect/scala/reflect/internal/HasFlags.scala
+++ b/src/reflect/scala/reflect/internal/HasFlags.scala
@@ -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/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 = {
+ }
+}