Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

check defined in src for cache in companion #14557

Merged
merged 1 commit into from Feb 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 16 additions & 8 deletions compiler/src/dotty/tools/dotc/core/Symbols.scala
Expand Up @@ -132,16 +132,24 @@ object Symbols {
private[core] def defRunId: RunId =
if (lastDenot == null) NoRunId else lastDenot.validFor.runId

private inline def associatedFileMatches(inline filter: AbstractFile => Boolean)(using Context): Boolean =
try
val file = associatedFile
file != null && filter(file)
catch case ex: StaleSymbol =>
// can happen for constructor proxy companions. Test case is pos-macros/i9484.
false

/** Does this symbol come from a currently compiled source file? */
final def isDefinedInCurrentRun(using Context): Boolean =
span.exists && defRunId == ctx.runId && {
try
val file = associatedFile
file != null && ctx.run.files.contains(file)
catch case ex: StaleSymbol =>
// can happen for constructor proxy companions. Test case is pos-macros/i9484.
false
}
span.exists && defRunId == ctx.runId && associatedFileMatches(ctx.run.files.contains)

/** Is this symbol valid in the current run and has an associated file that is
* not a binary file. e.g. This will return true for
* symbols defined by the user in a prior run of the REPL, that are still valid.
*/
final def isDefinedInSource(using Context): Boolean =
span.exists && isValidInCurrentRun && associatedFileMatches(_.extension != "class")

dwijnand marked this conversation as resolved.
Show resolved Hide resolved
/** Is symbol valid in current run? */
final def isValidInCurrentRun(using Context): Boolean =
Expand Down
24 changes: 13 additions & 11 deletions compiler/src/dotty/tools/dotc/transform/SymUtils.scala
Expand Up @@ -111,18 +111,20 @@ object SymUtils:
self.isAllOf(Given | Method) && isCodefined(self.info)

def useCompanionAsSumMirror(using Context): Boolean =
def companionExtendsSum(using Context): Boolean =
self.linkedClass.isSubClass(defn.Mirror_SumClass)
self.linkedClass.exists
&& !self.is(Scala2x)
&& (
// If the sum type is compiled from source, and `self` is a "generic sum"
// then its companion object will become a sum mirror in `posttyper`. (This method
// can be called from `typer` when summoning a Mirror.)
// However if `self` is from a prior run then we should check that its companion subclasses `Mirror.Sum`.
// e.g. before Scala 3.1, hierarchical sum types were not considered "generic sums", so their
// companion would not cache the mirror. Companions from TASTy will already be typed as `Mirror.Sum`.
self.isDefinedInCurrentRun
|| self.linkedClass.isSubClass(defn.Mirror_SumClass)
)
&& !self.is(Scala2x)
&& (
// If the sum type is compiled from source, and `self` is a "generic sum"
// then its companion object will become a sum mirror in `posttyper`. (This method
// can be called from `typer` when summoning a Mirror.)
// However if `self` is from a binary file, then we should check that its companion
// subclasses `Mirror.Sum`. e.g. before Scala 3.1, hierarchical sum types were not
// considered "generic sums", so their companion would not cache the mirror.
// Companions from TASTy will already be typed as `Mirror.Sum`.
self.isDefinedInSource || companionExtendsSum
)

/** Is this a sealed class or trait for which a sum mirror is generated?
* It must satisfy the following conditions:
Expand Down
7 changes: 7 additions & 0 deletions compiler/test-resources/repl/i14540
@@ -0,0 +1,7 @@
scala> enum I14540 { case A }
// defined class I14540
scala> summon[scala.deriving.Mirror.SumOf[I14540]] eq I14540
val res0: Boolean = true
scala> enum I14540B { case A }; summon[scala.deriving.Mirror.SumOf[I14540B]] eq I14540B
// defined class I14540B
val res1: Boolean = true
Expand Up @@ -5,4 +5,5 @@ import scala.deriving.Mirror
object Main:
def main(args: Array[String]): Unit =
val mirrorTop = summon[Mirror.SumOf[lib.Top]]
assert(mirrorTop ne lib.Top) // **NOT** cached in companion - previous run, pre-3.1 tasty dependency
assert(mirrorTop.ordinal(lib.Middle()) == 0)
7 changes: 7 additions & 0 deletions tests/run/i14540-priorRun/Lib_1.scala
@@ -0,0 +1,7 @@
package lib

sealed trait Top
object Top // companion is necessary

case class Middle() extends Top with Bottom
sealed trait Bottom extends Top
6 changes: 6 additions & 0 deletions tests/run/i14540-priorRun/Test_2.scala
@@ -0,0 +1,6 @@
import scala.deriving.Mirror

@main def Test =
val mirrorTop = summon[Mirror.SumOf[lib.Top]]
assert(mirrorTop eq lib.Top) // cached in companion - previous run, tasty dependency
assert(mirrorTop.ordinal(lib.Middle()) == 0)
7 changes: 7 additions & 0 deletions tests/run/i14540-sameRun/Lib.scala
@@ -0,0 +1,7 @@
package lib

sealed trait Top
object Top // companion is necessary

case class Middle() extends Top with Bottom
sealed trait Bottom extends Top
6 changes: 6 additions & 0 deletions tests/run/i14540-sameRun/Test.scala
@@ -0,0 +1,6 @@
import scala.deriving.Mirror

@main def Test =
val mirrorTop = summon[Mirror.SumOf[lib.Top]]
assert(mirrorTop eq lib.Top) // cached in companion - same run, source dependency
assert(mirrorTop.ordinal(lib.Middle()) == 0)