Skip to content

Commit

Permalink
Replace symbol traversal with tree traversal when finding top level e…
Browse files Browse the repository at this point in the history
…xperimentals (#21827)

Aims to fix stale symbol errors caused by the symbol traversal after suspending by a macro
  • Loading branch information
jchyb authored Nov 14, 2024
1 parent a304c85 commit 11d4295
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 15 deletions.
30 changes: 15 additions & 15 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -806,20 +806,20 @@ object Checking {
*
*/
def checkAndAdaptExperimentalImports(trees: List[Tree])(using Context): Unit =
def nonExperimentalTopLevelDefs(pack: Symbol): Iterator[Symbol] =
def isNonExperimentalTopLevelDefinition(sym: Symbol) =
sym.isDefinedInCurrentRun
&& sym.source == ctx.compilationUnit.source
&& !sym.isConstructor // not constructor of package object
&& !sym.is(Package) && !sym.name.isPackageObjectName
&& !sym.isExperimental

pack.info.decls.toList.iterator.flatMap: sym =>
if sym.isClass && (sym.is(Package) || sym.isPackageObject) then
nonExperimentalTopLevelDefs(sym)
else if isNonExperimentalTopLevelDefinition(sym) then
sym :: Nil
else Nil
def nonExperimentalTopLevelDefs(): List[Symbol] =
new TreeAccumulator[List[Symbol]] {
override def apply(x: List[Symbol], tree: tpd.Tree)(using Context): List[Symbol] =
def addIfNotExperimental(sym: Symbol) =
if !sym.isExperimental then sym :: x
else x
tree match {
case tpd.PackageDef(_, contents) => apply(x, contents)
case typeDef @ tpd.TypeDef(_, temp: Template) if typeDef.symbol.isPackageObject =>
apply(x, temp.body)
case mdef: tpd.MemberDef => addIfNotExperimental(mdef.symbol)
case _ => x
}
}.apply(Nil, ctx.compilationUnit.tpdTree)

def unitExperimentalLanguageImports =
def isAllowedImport(sel: untpd.ImportSelector) =
Expand All @@ -837,7 +837,7 @@ object Checking {

if ctx.owner.is(Package) || ctx.owner.name.startsWith(str.REPL_SESSION_LINE) then
def markTopLevelDefsAsExperimental(why: String): Unit =
for sym <- nonExperimentalTopLevelDefs(ctx.owner) do
for sym <- nonExperimentalTopLevelDefs() do
sym.addAnnotation(ExperimentalAnnotation(s"Added by $why", sym.span))

unitExperimentalLanguageImports match
Expand Down
15 changes: 15 additions & 0 deletions tests/pos-macros/i21802/Macro.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class MetricsGroup[A]
object MetricsGroup:
import scala.quoted.*

transparent inline final def refine[A]: MetricsGroup[A] =
${ refineImpl[A] }

private def refineImpl[A](using qctx: Quotes, tpe: Type[A]): Expr[MetricsGroup[A]] =
import qctx.reflect.*

val mt = MethodType(Nil)(_ => Nil, _ => TypeRepr.of[A])
val tpe = Refinement(TypeRepr.of[MetricsGroup[A]], "apply", mt).asType
tpe match
case '[tpe] =>
'{ MetricsGroup[A]().asInstanceOf[MetricsGroup[A] & tpe] }
13 changes: 13 additions & 0 deletions tests/pos-macros/i21802/Test.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//> using options -experimental -Ydebug

class ProbeFailedException(cause: Exception) extends Exception(cause)
trait Probing:
self: Metrics =>
val probeFailureCounter: MetricsGroup[Counter] =
counters("ustats_probe_failures_count").labelled


trait Counter
class Metrics:
class counters(name: String):
transparent inline final def labelled: MetricsGroup[Counter] = MetricsGroup.refine[Counter]

0 comments on commit 11d4295

Please sign in to comment.