Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 25 additions & 8 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -536,17 +536,34 @@ class Namer { typer: Typer =>
// to just prepending the new Child annotation.
def isReady(ann: Annotation): Boolean =
ann.symbol == defn.ChildAnnot && !ann.isEvaluating
// Forcing a Child annotation may throw `StaleSymbol` if it refers to a
// symbol from a previous run that is no longer valid. This happens when
// compilation was suspended and the child class is re-typechecked in the
// current run, leaving an outdated Child annotation on the parent (see
// tests/pos/i24414). Treat such annotations as stale and drop them.
def childOf(ann: Annotation): Option[Symbol] =
try Annotation.Child.unapply(ann)
catch case _: StaleSymbol => None
def insertInto(annots: List[Annotation]): List[Annotation] =
annots.find(isReady) match {
case Some(Annotation.Child(other)) if other.span.exists && childStart <= other.span.start =>
if (child == other)
annots // can happen if a class has several inaccessible children
else {
assert(childStart != other.span.start || child.source != other.source, i"duplicate child annotation $child / $other")
val (prefix, otherAnnot :: rest) = annots.span(ann => !isReady(ann)): @unchecked
prefix ::: otherAnnot :: insertInto(rest)
case Some(ann) =>
childOf(ann) match {
case Some(other) if other.span.exists && childStart <= other.span.start =>
if (child == other)
annots // can happen if a class has several inaccessible children
else {
assert(childStart != other.span.start || child.source != other.source, i"duplicate child annotation $child / $other")
val (prefix, otherAnnot :: rest) = annots.span(a => (a ne ann)): @unchecked
prefix ::: otherAnnot :: insertInto(rest)
}
case None =>
// Drop the stale Child annotation and continue searching.
val (prefix, rest) = annots.span(a => (a ne ann))
prefix ::: insertInto(rest.tail)
case _ =>
Annotation.Child(child, cls.span.startPos) :: annots
}
case _ =>
case None =>
Annotation.Child(child, cls.span.startPos) :: annots
}
cls.annotations = insertInto(cls.annotations)
Expand Down
1 change: 1 addition & 0 deletions tests/pos/i24414/CloudEventKey_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
object CloudEventKey extends RefinedType[String, MessageKey]
12 changes: 12 additions & 0 deletions tests/pos/i24414/Lib_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// https://github.com/scala/scala3/issues/24414
trait Constraint[A, C]:
inline def test: Boolean

final class RuntimeConstraint[A, C](_test: Boolean)
object RuntimeConstraint:
inline given fromConstraint[A, C](using c: Constraint[A, C]): RuntimeConstraint[A, C] =
new RuntimeConstraint[A, C](c.test)

sealed trait Refined[A, C](using _rtc: RuntimeConstraint[A, C])

trait RefinedType[A, C] extends Refined[A, C]
9 changes: 9 additions & 0 deletions tests/pos/i24414/MessageKey_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import scala.quoted.*

final class MessageKey

object MessageKey:
inline given Constraint[String, MessageKey] with
override inline def test: Boolean = ${ check() }

def check()(using Quotes): Expr[Boolean] = Expr(true)
Loading