-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add refining annotations #4626
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
Add refining annotations #4626
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -236,8 +236,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { | |
compareWild | ||
case tp2: LazyRef => | ||
!tp2.evaluating && recur(tp1, tp2.ref) | ||
case tp2: AnnotatedType => | ||
recur(tp1, tp2.tpe) // todo: refine? | ||
case tp2: AnnotatedType if !tp2.isRefining => | ||
recur(tp1, tp2.tpe) | ||
case tp2: ThisType => | ||
def compareThis = { | ||
val cls2 = tp2.cls | ||
|
@@ -345,7 +345,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { | |
// because that would cause an assertionError. Return false instead. | ||
// See i859.scala for an example where we hit this case. | ||
!tp1.evaluating && recur(tp1.ref, tp2) | ||
case tp1: AnnotatedType => | ||
case tp1: AnnotatedType if !tp1.isRefining => | ||
recur(tp1.tpe, tp2) | ||
case AndType(tp11, tp12) => | ||
if (tp11.stripTypeVar eq tp12.stripTypeVar) recur(tp11, tp2) | ||
|
@@ -567,6 +567,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { | |
false | ||
} | ||
compareTypeBounds | ||
case tp2: AnnotatedType if tp2.isRefining => | ||
(tp1.derivesAnnotWith(tp2.annot.sameAnnotation) || defn.isBottomType(tp1)) && | ||
recur(tp1, tp2.tpe) | ||
case ClassInfo(pre2, cls2, _, _, _) => | ||
def compareClassInfo = tp1 match { | ||
case ClassInfo(pre1, cls1, _, _, _) => | ||
|
@@ -661,6 +664,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { | |
case _ => | ||
} | ||
either(recur(tp11, tp2), recur(tp12, tp2)) | ||
case tp1: AnnotatedType if tp1.isRefining => | ||
isNewSubType(tp1.tpe) | ||
case JavaArrayType(elem1) => | ||
def compareJavaArray = tp2 match { | ||
case JavaArrayType(elem2) => isSubType(elem1, elem2) | ||
|
@@ -700,7 +705,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { | |
} | ||
case tycon1: TypeVar => | ||
isMatchingApply(tycon1.underlying) | ||
case tycon1: AnnotatedType => | ||
case tycon1: AnnotatedType if !tycon1.isRefining => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure when a type constructor would have a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, well spotted. Narrowing the lower bound is always safe, and it is the only thing we can do in this case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like this PR was merged without addressing this comment? |
||
isMatchingApply(tycon1.underlying) | ||
case _ => | ||
false | ||
|
@@ -811,7 +816,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { | |
fourthTry | ||
} | ||
} | ||
case _: TypeVar | _: AnnotatedType => | ||
case _: TypeVar => | ||
recur(tp1, tp2.superType) | ||
case tycon2: AnnotatedType if !tycon2.isRefining => | ||
recur(tp1, tp2.superType) | ||
case tycon2: AppliedType => | ||
fallback(tycon2.lowerBound) | ||
|
@@ -1546,7 +1553,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { | |
} | ||
case tp1: TypeVar if tp1.isInstantiated => | ||
tp1.underlying & tp2 | ||
case tp1: AnnotatedType => | ||
case tp1: AnnotatedType if !tp1.isRefining => | ||
tp1.underlying & tp2 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not rewrap the annotation around the intersection if it's a refining annotation? |
||
case _ => | ||
NoType | ||
|
@@ -1565,7 +1572,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { | |
ExprType(rt1 | tp2.widenExpr) | ||
case tp1: TypeVar if tp1.isInstantiated => | ||
tp1.underlying | tp2 | ||
case tp1: AnnotatedType => | ||
case tp1: AnnotatedType if !tp1.isRefining => | ||
tp1.underlying | tp2 | ||
case _ => | ||
NoType | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -283,6 +283,15 @@ object Types { | |
case _ => false | ||
} | ||
|
||
/** Does this type have a supertype with an annotation satisfying given predicate `p`? */ | ||
def derivesAnnotWith(p: Annotation => Boolean)(implicit ctx: Context): Boolean = this match { | ||
case tp: AnnotatedType => p(tp.annot) || tp.tpe.derivesAnnotWith(p) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider renaming There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good idea. |
||
case tp: TypeProxy => tp.superType.derivesAnnotWith(p) | ||
case AndType(l, r) => l.derivesAnnotWith(p) || r.derivesAnnotWith(p) | ||
case OrType(l, r) => l.derivesAnnotWith(p) && r.derivesAnnotWith(p) | ||
case _ => false | ||
} | ||
|
||
/** Does this type occur as a part of type `that`? */ | ||
final def occursIn(that: Type)(implicit ctx: Context): Boolean = | ||
that existsPart (this == _) | ||
|
@@ -3693,6 +3702,17 @@ object Types { | |
|
||
override def stripAnnots(implicit ctx: Context): Type = tpe.stripAnnots | ||
|
||
private[this] var isRefiningKnown = false | ||
private[this] var isRefiningCache: Boolean = _ | ||
|
||
def isRefining(implicit ctx: Context) = { | ||
if (!isRefiningKnown) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't the cache be invalidated at the start of a new run? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was thinking about that too, but decided that it's probably fine given the internal nature of such annotations. Then again, we do expose RefiningAnnotation in the public |
||
isRefiningCache = annot.symbol.derivesFrom(defn.RefiningAnnotationClass) | ||
isRefiningKnown = true | ||
} | ||
isRefiningCache | ||
} | ||
|
||
override def iso(that: Any, bs: BinderPairs): Boolean = that match { | ||
case that: AnnotatedType => tpe.equals(that.tpe, bs) && (annot `eq` that.annot) | ||
case _ => false | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package scala.annotation | ||
|
||
trait RefiningAnnotation extends StaticAnnotation | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why expose that in the stdlib, isn't this compiler internal only? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, you might want to define your own annotations (say units of measure). Maybe they need a compiler plugin to order to be checked, or maybe you can do it in the future with annotation macros. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Misaligned
=
)