Merge pull request #9308 from lrytz/merge-2.12-to-2.13-nov-9

This commit is contained in:
Lukas Rytz 2020-11-10 09:58:39 +01:00 committed by GitHub
commit a8d3741fb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 186 additions and 8 deletions

View File

@ -365,6 +365,13 @@ val mimaFilterSettings = Seq {
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.reflect.macros.Attachments.removeElement"),
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.reflect.macros.Attachments.addElement"),
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.reflect.macros.Attachments.containsElement"),
// https://github.com/scala/scala/pull/7960: these only cause problems if someone is implausibly extending Attachments somehow
ProblemFilters.exclude[DirectAbstractMethodProblem]("scala.reflect.macros.Attachments.all"),
ProblemFilters.exclude[ReversedAbstractMethodProblem]("scala.reflect.macros.Attachments.all"),
ProblemFilters.exclude[DirectAbstractMethodProblem]("scala.reflect.macros.Attachments.isEmpty"),
ProblemFilters.exclude[ReversedAbstractMethodProblem]("scala.reflect.macros.Attachments.isEmpty"),
ProblemFilters.exclude[MissingClassProblem]("scala.reflect.macros.EmptyAttachments"),
ProblemFilters.exclude[MissingClassProblem]("scala.reflect.macros.SingleAttachment"),
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.reflect.runtime.Settings.async"),

View File

@ -18,6 +18,7 @@ import Flags._
import scala.annotation.tailrec
import scala.collection.mutable
import scala.reflect.NameTransformer
import scala.reflect.internal.util.ListOfNil
abstract class Mixin extends Transform with ast.TreeDSL with AccessorSynthesis {
@ -322,7 +323,11 @@ abstract class Mixin extends Transform with ast.TreeDSL with AccessorSynthesis {
JUnitAnnotations.exists(annot => annot.exists && member.hasAnnotation(annot))
}
if (existsCompetingMethod(clazz.baseClasses) || generateJUnitForwarder)
def generateSerializationForwarder: Boolean = {
(member.name == nme.readResolve || member.name == nme.writeReplace) && member.info.paramss == ListOfNil
}
if (existsCompetingMethod(clazz.baseClasses) || generateJUnitForwarder || generateSerializationForwarder)
genForwarder(required = true)
else if (settings.mixinForwarderChoices.isTruthy)
genForwarder(required = false)

View File

@ -14,7 +14,7 @@ package scala.tools.nsc.transform.async
import scala.collection.mutable
import scala.tools.nsc.transform.{Transform, TypingTransformers}
import scala.reflect.internal.util.SourceFile
import scala.reflect.internal.util.{SourceFile, NoSourceFile}
abstract class AsyncPhase extends Transform with TypingTransformers with AnfTransform with Lifter with LiveVariables {
self =>
@ -75,7 +75,9 @@ abstract class AsyncPhase extends Transform with TypingTransformers with AnfTran
override def transformUnit(unit: CompilationUnit): Unit = {
if (settings.async) {
if (sourceFilesToTransform.contains(unit.source)) super.transformUnit(unit)
// NoSourceFile can happen for, e.g., toolbox compilation; overestimate by always transforming them. See test/async/jvm/toolbox.scala
val shouldTransform = unit.source == NoSourceFile || sourceFilesToTransform.contains(unit.source)
if (shouldTransform) super.transformUnit(unit)
if (awaits.exists(_.isInitialized)) {
unit.body.foreach {
case tree: RefTree if tree.symbol != null && awaits.contains(tree.symbol) =>

View File

@ -16,7 +16,7 @@ package internal
package util
/** @inheritdoc */
class Position extends scala.reflect.api.Position with InternalPositionImpl with DeprecatedPosition {
class Position extends macros.EmptyAttachments with api.Position with InternalPositionImpl with DeprecatedPosition {
type Pos = Position
def pos: Position = this
def withPos(newPos: Position): macros.Attachments { type Pos = Position.this.Pos } = newPos

View File

@ -14,6 +14,8 @@ package scala
package reflect
package macros
import reflect.internal.util.Position
/**
* <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span>
*
@ -35,7 +37,7 @@ package macros
abstract class Attachments { self =>
/** The position type of this attachment */
type Pos >: Null
type Pos >: Null // <: api.Position
/** The underlying position */
def pos: Pos
@ -44,7 +46,7 @@ abstract class Attachments { self =>
def withPos(newPos: Pos): Attachments { type Pos = self.Pos }
/** The underlying payload with the guarantee that no two elements have the same type. */
def all: Set[Any] = Set.empty
def all: Set[Any]
private def matchesTag[T: ClassTag]: (Any => Boolean) = {
// OPT: avoid lambda allocation for each call to `remove`, etc.
@ -78,6 +80,7 @@ abstract class Attachments { self =>
else {
val newAll = all filterNot matchesTag[T]
if (newAll.isEmpty) pos.asInstanceOf[Attachments { type Pos = self.Pos }]
else if (newAll.size == 1) new SingleAttachment[Pos](pos, newAll.head)
else new NonemptyAttachments[Pos](this.pos, newAll)
}
}
@ -88,10 +91,11 @@ abstract class Attachments { self =>
else if (newAll.isEmpty) pos.asInstanceOf[Attachments { type Pos = self.Pos }]
else new NonemptyAttachments[Pos](this.pos, newAll)
}
/** Creates a copy of this attachment with the given element added. */
final def addElement[T](attachment: T): Attachments { type Pos = self.Pos } = {
val newAll = all + attachment
if (newAll eq all) this
if (newAll eq all) this // i.e., this was the same attachment as before
else new NonemptyAttachments[Pos](this.pos, newAll)
}
@ -100,7 +104,7 @@ abstract class Attachments { self =>
all.contains(element)
}
def isEmpty: Boolean = true
def isEmpty: Boolean
def cloneAttachments: Attachments { type Pos = self.Pos } = this
}
@ -110,6 +114,31 @@ private object Attachments {
}
}
private[reflect] abstract class EmptyAttachments extends Attachments { self: Position =>
final override def all: Set[Any] = Set.empty
final override def get[T: ClassTag]: Option[T] = None
final override def contains[T: ClassTag]: Boolean = false
final override def update[T: ClassTag](newAtt: T): Attachments { type Pos = self.Pos } =
new SingleAttachment[Pos](pos, newAtt)
final override def remove[T: ClassTag]: Attachments { type Pos = self.Pos } = this
final override def isEmpty: Boolean = true
}
private final class SingleAttachment[P >: Null](override val pos: P, val att: Any) extends Attachments {
type Pos = P
def withPos(newPos: Pos) = new SingleAttachment[Pos](newPos, att)
override def isEmpty: Boolean = false
override def cloneAttachments: Attachments { type Pos = P } = new SingleAttachment[P](pos, att)
override def all = Set.empty[Any] + att
override def contains[T](implicit tt: ClassTag[T]) = tt.runtimeClass.isInstance(att)
override def get[T](implicit tt: ClassTag[T]) = if (contains(tt)) Some(att.asInstanceOf[T]) else None
override def update[T](newAtt: T)(implicit tt: ClassTag[T]) =
if (contains(tt)) new SingleAttachment[P](pos, newAtt)
else new NonemptyAttachments[P](pos, Set.empty[Any] + att + newAtt)
override def remove[T](implicit tt: ClassTag[T]) =
if (contains(tt)) pos.asInstanceOf[Attachments { type Pos = P }] else this
}
// scala/bug#7018: This used to be an inner class of `Attachments`, but that led to a memory leak in the
// IDE via $outer pointers.
private final class NonemptyAttachments[P >: Null](override val pos: P, override val all: Set[Any]) extends Attachments {

View File

@ -0,0 +1,16 @@
import scala.tools.reflect._
import scala.reflect.runtime._
object Test extends App {
val box = currentMirror.mkToolBox(options = "-Xasync")
val code =
"""
import scala.concurrent._, scala.concurrent.duration._, ExecutionContext.Implicits._
import scala.tools.testkit.async._
val f1 = Future(1)
val f2 = Future(2)
val res = Async.async { Async.await(f1) + Async.await(f2) }
Await.result(res, 1.second)
""".stripMargin
assert(box.eval(box.parse(code)) == 3)
}

View File

@ -0,0 +1,28 @@
// scalac: -Xmixin-force-forwarders:false
import java.io.{ByteArrayInputStream, ByteArrayOutputStream, ObjectInputStream, ObjectOutputStream}
trait T1 extends Serializable {
def writeReplace(): AnyRef = new SerializationProxy(this.asInstanceOf[C].s)
}
trait T2 {
def readResolve: AnyRef = new C(this.asInstanceOf[SerializationProxy].s.toLowerCase)
}
class C(val s: String) extends T1
class SerializationProxy(val s: String) extends T2 with Serializable
object Test {
def serializeDeserialize[T <: AnyRef](obj: T) = {
val buffer = new ByteArrayOutputStream
val out = new ObjectOutputStream(buffer)
out.writeObject(obj)
val in = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray))
in.readObject.asInstanceOf[T]
}
def main(args: Array[String]): Unit = {
val c1 = new C("TEXT")
val c2 = serializeDeserialize(c1)
assert(c2.s == "text")
}
}

View File

@ -0,0 +1,26 @@
import java.io.{ByteArrayInputStream, ByteArrayOutputStream, ObjectInputStream, ObjectOutputStream}
trait T1 extends Serializable {
def writeReplace(): AnyRef = new SerializationProxy(this.asInstanceOf[C].s)
}
trait T2 {
def readResolve: AnyRef = new C(this.asInstanceOf[SerializationProxy].s.toLowerCase)
}
class C(val s: String) extends T1
class SerializationProxy(val s: String) extends T2 with Serializable
object Test {
def serializeDeserialize[T <: AnyRef](obj: T) = {
val buffer = new ByteArrayOutputStream
val out = new ObjectOutputStream(buffer)
out.writeObject(obj)
val in = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray))
in.readObject.asInstanceOf[T]
}
def main(args: Array[String]): Unit = {
val c1 = new C("TEXT")
val c2 = serializeDeserialize(c1)
assert(c2.s == "text")
}
}

View File

@ -0,0 +1,65 @@
package scala.reflect.macros
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import scala.reflect.internal.util._
@RunWith(classOf[JUnit4])
class AttachmentsTest {
def emptyAtt: Attachments = NoPosition
@Test def testAttachments = {
var atts = emptyAtt
assert(atts.isEmpty)
assert(atts.all.isEmpty)
atts = atts update Foo(0)
assert(!atts.isEmpty)
assert(atts.all == Set(Foo(0)))
assert(atts.contains[Foo])
assert(atts.get[Foo] == Some(Foo(0)))
atts = atts.remove[Bar]
assert(!atts.isEmpty)
assert(atts.all == Set(Foo(0)))
assert(atts.contains[Foo])
assert(atts.get[Foo] == Some(Foo(0)))
object theBar extends Bar
atts = atts.update(theBar)
assert(!atts.isEmpty)
assert(atts.all == Set(Foo(0), theBar))
assert(atts.get[Foo] == Some(Foo(0)))
assert(atts.get[Bar] == Some(theBar))
assert(atts.get[theBar.type] == Some(theBar))
atts = atts.update(new Baz)
assert(!atts.isEmpty)
assert(!(atts.all contains Foo(0)))
assert(atts.all.exists(_.isInstanceOf[Baz]))
assert(atts.get[Baz].isDefined)
assert(atts.get[Foo].isEmpty)
assert(atts.get[Bar].isDefined)
atts = atts.update("foo")
atts = atts.update(Zzy)
assert(!atts.isEmpty)
assert(atts.all.size == 4, atts.toString)
assert(atts.contains[String])
assert(atts.contains[Zzy.type])
assert(atts.contains[Baz])
assert(atts.contains[theBar.type])
atts = atts.remove[Zzy.type].remove[String]
assert(atts.all.size == 2)
}
}
case class Foo(i: Int) extends Baz
class Bar
class Baz
object Zzy