Skip to content

Commit 4800cef

Browse files
committed
Introduce RefiningAnnotations
The idea is that such annotations create some proper subtype of the type they annotate. Hence, they cannot be stripped away like normal annotations are.
1 parent c3469da commit 4800cef

File tree

6 files changed

+67
-7
lines changed

6 files changed

+67
-7
lines changed

compiler/src/dotty/tools/dotc/ast/Trees.scala

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,31 @@ object Trees {
213213

214214
override def toText(printer: Printer) = printer.toText(this)
215215

216+
def sameTree(that: Tree[_]): Boolean = {
217+
def isSame(x: Any, y: Any): Boolean =
218+
x.asInstanceOf[AnyRef].eq(y.asInstanceOf[AnyRef]) || {
219+
x match {
220+
case x: Tree[_] =>
221+
y match {
222+
case y: Tree[_] => x.sameTree(y)
223+
case _ => false
224+
}
225+
case x: List[_] =>
226+
y match {
227+
case y: List[_] => x.corresponds(y)(isSame)
228+
case _ => false
229+
}
230+
case _ =>
231+
false
232+
}
233+
}
234+
this.getClass == that.getClass && {
235+
val it1 = this.productIterator
236+
val it2 = that.productIterator
237+
it1.corresponds(it2)(isSame)
238+
}
239+
}
240+
216241
override def hashCode(): Int = uniqueId // for debugging; was: System.identityHashCode(this)
217242
override def equals(that: Any) = this eq that.asInstanceOf[AnyRef]
218243

compiler/src/dotty/tools/dotc/core/Annotations.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ object Annotations {
3434
def isEvaluated: Boolean = true
3535

3636
def ensureCompleted(implicit ctx: Context): Unit = tree
37+
38+
def sameAnnotation(that: Annotation)(implicit ctx: Context) =
39+
symbol == that.symbol && tree.sameTree(that.tree)
3740
}
3841

3942
case class ConcreteAnnotation(t: Tree) extends Annotation {

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,8 @@ class Definitions {
694694
def ClassfileAnnotationClass(implicit ctx: Context) = ClassfileAnnotationType.symbol.asClass
695695
lazy val StaticAnnotationType = ctx.requiredClassRef("scala.annotation.StaticAnnotation")
696696
def StaticAnnotationClass(implicit ctx: Context) = StaticAnnotationType.symbol.asClass
697+
lazy val RefiningAnnotationType = ctx.requiredClassRef("scala.annotation.RefiningAnnotation")
698+
def RefiningAnnotationClass(implicit ctx: Context) = RefiningAnnotationType.symbol.asClass
697699

698700
// Annotation classes
699701
lazy val AliasAnnotType = ctx.requiredClassRef("scala.annotation.internal.Alias")

compiler/src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
236236
compareWild
237237
case tp2: LazyRef =>
238238
!tp2.evaluating && recur(tp1, tp2.ref)
239-
case tp2: AnnotatedType =>
240-
recur(tp1, tp2.tpe) // todo: refine?
239+
case tp2: AnnotatedType if !tp2.isRefining =>
240+
recur(tp1, tp2.tpe)
241241
case tp2: ThisType =>
242242
def compareThis = {
243243
val cls2 = tp2.cls
@@ -345,7 +345,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
345345
// because that would cause an assertionError. Return false instead.
346346
// See i859.scala for an example where we hit this case.
347347
!tp1.evaluating && recur(tp1.ref, tp2)
348-
case tp1: AnnotatedType =>
348+
case tp1: AnnotatedType if !tp1.isRefining =>
349349
recur(tp1.tpe, tp2)
350350
case AndType(tp11, tp12) =>
351351
if (tp11.stripTypeVar eq tp12.stripTypeVar) recur(tp11, tp2)
@@ -567,6 +567,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
567567
false
568568
}
569569
compareTypeBounds
570+
case tp2: AnnotatedType if tp2.isRefining =>
571+
(tp1.derivesAnnotWith(tp2.annot.sameAnnotation) || defn.isBottomType(tp1)) &&
572+
recur(tp1, tp2.tpe)
570573
case ClassInfo(pre2, cls2, _, _, _) =>
571574
def compareClassInfo = tp1 match {
572575
case ClassInfo(pre1, cls1, _, _, _) =>
@@ -661,6 +664,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
661664
case _ =>
662665
}
663666
either(recur(tp11, tp2), recur(tp12, tp2))
667+
case tp1: AnnotatedType if tp1.isRefining =>
668+
isNewSubType(tp1.tpe)
664669
case JavaArrayType(elem1) =>
665670
def compareJavaArray = tp2 match {
666671
case JavaArrayType(elem2) => isSubType(elem1, elem2)
@@ -700,7 +705,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
700705
}
701706
case tycon1: TypeVar =>
702707
isMatchingApply(tycon1.underlying)
703-
case tycon1: AnnotatedType =>
708+
case tycon1: AnnotatedType if !tycon1.isRefining =>
704709
isMatchingApply(tycon1.underlying)
705710
case _ =>
706711
false
@@ -811,7 +816,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
811816
fourthTry
812817
}
813818
}
814-
case _: TypeVar | _: AnnotatedType =>
819+
case _: TypeVar =>
820+
recur(tp1, tp2.superType)
821+
case tycon2: AnnotatedType if !tycon2.isRefining =>
815822
recur(tp1, tp2.superType)
816823
case tycon2: AppliedType =>
817824
fallback(tycon2.lowerBound)
@@ -1546,7 +1553,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
15461553
}
15471554
case tp1: TypeVar if tp1.isInstantiated =>
15481555
tp1.underlying & tp2
1549-
case tp1: AnnotatedType =>
1556+
case tp1: AnnotatedType if !tp1.isRefining =>
15501557
tp1.underlying & tp2
15511558
case _ =>
15521559
NoType
@@ -1565,7 +1572,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
15651572
ExprType(rt1 | tp2.widenExpr)
15661573
case tp1: TypeVar if tp1.isInstantiated =>
15671574
tp1.underlying | tp2
1568-
case tp1: AnnotatedType =>
1575+
case tp1: AnnotatedType if !tp1.isRefining =>
15691576
tp1.underlying | tp2
15701577
case _ =>
15711578
NoType

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,15 @@ object Types {
283283
case _ => false
284284
}
285285

286+
/** Does this type have a supertype with an annotation satisfying given predicate `p`? */
287+
def derivesAnnotWith(p: Annotation => Boolean)(implicit ctx: Context): Boolean = this match {
288+
case tp: AnnotatedType => p(tp.annot) || tp.tpe.derivesAnnotWith(p)
289+
case tp: TypeProxy => tp.superType.derivesAnnotWith(p)
290+
case AndType(l, r) => l.derivesAnnotWith(p) || r.derivesAnnotWith(p)
291+
case OrType(l, r) => l.derivesAnnotWith(p) && r.derivesAnnotWith(p)
292+
case _ => false
293+
}
294+
286295
/** Does this type occur as a part of type `that`? */
287296
final def occursIn(that: Type)(implicit ctx: Context): Boolean =
288297
that existsPart (this == _)
@@ -3693,6 +3702,17 @@ object Types {
36933702

36943703
override def stripAnnots(implicit ctx: Context): Type = tpe.stripAnnots
36953704

3705+
private[this] var isRefiningKnown = false
3706+
private[this] var isRefiningCache: Boolean = _
3707+
3708+
def isRefining(implicit ctx: Context) = {
3709+
if (!isRefiningKnown) {
3710+
isRefiningCache = annot.symbol.derivesFrom(defn.RefiningAnnotationClass)
3711+
isRefiningKnown = true
3712+
}
3713+
isRefiningCache
3714+
}
3715+
36963716
override def iso(that: Any, bs: BinderPairs): Boolean = that match {
36973717
case that: AnnotatedType => tpe.equals(that.tpe, bs) && (annot `eq` that.annot)
36983718
case _ => false
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package scala.annotation
2+
3+
trait RefiningAnnotation extends StaticAnnotation

0 commit comments

Comments
 (0)