Skip to content

Add subtype annotations #4625

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

Closed
wants to merge 15 commits into from
Closed
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
/** An extractor for def of a closure contained the block of the closure. */
object closureDef {
def unapply(tree: Tree): Option[DefDef] = tree match {
case Block(Nil, expr) => unapply(expr)
case Block((meth @ DefDef(nme.ANON_FUN, _, _, _, _)) :: Nil, closure: Closure) =>
Some(meth)
case _ => None
Expand Down
25 changes: 25 additions & 0 deletions compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,31 @@ object Trees {

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

def sameTree(that: Tree[_]): Boolean = {
def isSame(x: Any, y: Any): Boolean =
x.asInstanceOf[AnyRef].eq(y.asInstanceOf[AnyRef]) || {
x match {
case x: Tree[_] =>
y match {
case y: Tree[_] => x.sameTree(y)
case _ => false
}
case x: List[_] =>
y match {
case y: List[_] => x.corresponds(y)(isSame)
case _ => false
}
case _ =>
false
}
}
this.getClass == that.getClass && {
val it1 = this.productIterator
val it2 = that.productIterator
it1.corresponds(it2)(isSame)
}
}

override def hashCode(): Int = uniqueId // for debugging; was: System.identityHashCode(this)
override def equals(that: Any) = this eq that.asInstanceOf[AnyRef]

Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {

case class Inline() extends Mod(Flags.Inline)

case class Transparent() extends Mod(Flags.Transparent)

case class Enum() extends Mod(Flags.Enum)
}

Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Annotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ object Annotations {
def isEvaluated: Boolean = true

def ensureCompleted(implicit ctx: Context): Unit = tree

def sameAnnotation(that: Annotation)(implicit ctx: Context) =
symbol == that.symbol && tree.sameTree(that.tree)
}

case class ConcreteAnnotation(t: Tree) extends Annotation {
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,8 @@ class Definitions {
def ClassfileAnnotationClass(implicit ctx: Context) = ClassfileAnnotationType.symbol.asClass
lazy val StaticAnnotationType = ctx.requiredClassRef("scala.annotation.StaticAnnotation")
def StaticAnnotationClass(implicit ctx: Context) = StaticAnnotationType.symbol.asClass
lazy val SubTypeAnnotationType = ctx.requiredClassRef("scala.annotation.SubTypeAnnotation")
def SubTypeAnnotationClass(implicit ctx: Context) = SubTypeAnnotationType.symbol.asClass

// Annotation classes
lazy val AliasAnnotType = ctx.requiredClassRef("scala.annotation.internal.Alias")
Expand Down
25 changes: 17 additions & 8 deletions compiler/src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,10 @@ object Flags {
/** Symbol is a Java default method */
final val DefaultMethod = termFlag(38, "<defaultmethod>")

/** Symbol is a Java enum */
/** Labelled with `transparent` modifier */
final val Transparent = termFlag(39, "transparent")

/** Symbol is an enum class or enum case (if used with case) */
final val Enum = commonFlag(40, "<enum>")

/** Labeled with `erased` modifier (erased value) */
Expand Down Expand Up @@ -436,7 +439,7 @@ object Flags {

/** Flags representing source modifiers */
final val SourceModifierFlags =
commonFlags(Private, Protected, Abstract, Final, Inline,
commonFlags(Private, Protected, Abstract, Final, Inline, Transparent,
Sealed, Case, Implicit, Override, AbsOverride, Lazy, JavaStatic, Erased)

/** Flags representing modifiers that can appear in trees */
Expand All @@ -457,7 +460,7 @@ object Flags {
Scala2ExistentialCommon | Mutable.toCommonFlags | Touched | JavaStatic |
CovariantOrOuter | ContravariantOrLabel | CaseAccessor.toCommonFlags |
NonMember | ImplicitCommon | Permanent | Synthetic |
SuperAccessorOrScala2x | Inline
SuperAccessorOrScala2x | Inline | Transparent.toCommonFlags

/** Flags that are not (re)set when completing the denotation, or, if symbol is
* a top-level class or object, when completing the denotation once the class
Expand Down Expand Up @@ -513,7 +516,7 @@ object Flags {
/** Flags that can apply to a module val */
final val RetainedModuleValFlags: FlagSet = RetainedModuleValAndClassFlags |
Override | Final | Method | Implicit | Lazy |
Accessor | AbsOverride | Stable | Captured | Synchronized | Inline | Erased
Accessor | AbsOverride | Stable | Captured | Synchronized | Erased

/** Flags that can apply to a module class */
final val RetainedModuleClassFlags: FlagSet = RetainedModuleValAndClassFlags | ImplClass | Enum
Expand Down Expand Up @@ -548,8 +551,8 @@ object Flags {
/** Either method or lazy or deferred */
final val MethodOrLazyOrDeferred = Method | Lazy | Deferred

/** Labeled `private`, `final`, or `inline` */
final val PrivateOrFinalOrInline = Private | Final | Inline
/** Labeled `private`, `final`, `inline`, or `transparent` */
final val EffectivelyFinal = Private | Final | Inline | Transparent.toCommonFlags

/** A private method */
final val PrivateMethod = allOf(Private, Method)
Expand All @@ -560,6 +563,9 @@ object Flags {
/** An inline method */
final val InlineMethod = allOf(Inline, Method)

/** An transparent method */
final val TransparentMethod = allOf(Transparent, Method)

/** An inline parameter */
final val InlineParam = allOf(Inline, Param)

Expand All @@ -575,6 +581,9 @@ object Flags {
/** An accessor or label */
final val AccessorOrLabel = Accessor | Label

/** An accessor or a synthetic symbol */
final val AccessorOrSynthetic = Accessor | Synthetic

/** A synthetic or private definition */
final val SyntheticOrPrivate = Synthetic | Private

Expand All @@ -584,8 +593,8 @@ object Flags {
/** A deferred type member or parameter (these don't have right hand sides) */
final val DeferredOrTypeParam = Deferred | TypeParam

/** value that's final or inline */
final val FinalOrInline = Final | Inline
/** value that's final, inline, or transparent */
final val FinalOrInlineOrTransparent = Final | Inline | Transparent.toCommonFlags

/** A covariant type parameter instance */
final val LocalCovariant = allOf(Local, Covariant)
Expand Down
1 change: 0 additions & 1 deletion compiler/src/dotty/tools/dotc/core/Mode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -93,5 +93,4 @@ object Mode {

/** We are in the IDE */
val Interactive = newMode(20, "Interactive")

}
10 changes: 8 additions & 2 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -779,7 +779,13 @@ object SymDenotations {

def isSkolem: Boolean = name == nme.SKOLEM

def isInlineMethod(implicit ctx: Context): Boolean = is(InlineMethod, butNot = Accessor)
def isInlinedMethod(implicit ctx: Context): Boolean =
is(InlineMethod, butNot = Accessor)

def isTransparentMethod(implicit ctx: Context): Boolean =
is(TransparentMethod, butNot = Accessor)

def isInlineableMethod(implicit ctx: Context) = isInlinedMethod || isTransparentMethod

/** ()T and => T types should be treated as equivalent for this symbol.
* Note: For the moment, we treat Scala-2 compiled symbols as loose matching,
Expand Down Expand Up @@ -905,7 +911,7 @@ object SymDenotations {

/** A symbol is effectively final if it cannot be overridden in a subclass */
final def isEffectivelyFinal(implicit ctx: Context): Boolean =
is(PrivateOrFinalOrInline) || !owner.isClass || owner.is(ModuleOrFinal) || owner.isAnonymousClass
is(EffectivelyFinal) || !owner.isClass || owner.is(ModuleOrFinal) || owner.isAnonymousClass

/** The class containing this denotation which has the given effective name. */
final def enclosingClassNamed(name: Name)(implicit ctx: Context): Symbol = {
Expand Down
21 changes: 14 additions & 7 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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.isSubTypeAnnotated =>
recur(tp1, tp2.tpe)
case tp2: ThisType =>
def compareThis = {
val cls2 = tp2.cls
Expand Down Expand Up @@ -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.isSubTypeAnnotated =>
recur(tp1.tpe, tp2)
case AndType(tp11, tp12) =>
if (tp11.stripTypeVar eq tp12.stripTypeVar) recur(tp11, tp2)
Expand Down Expand Up @@ -567,6 +567,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
false
}
compareTypeBounds
case tp2: AnnotatedType if tp2.isSubTypeAnnotated =>
(tp1.derivesAnnotWith(tp2.annot.sameAnnotation) || defn.isBottomType(tp1)) &&
recur(tp1, tp2.tpe)
case ClassInfo(pre2, cls2, _, _, _) =>
def compareClassInfo = tp1 match {
case ClassInfo(pre1, cls1, _, _, _) =>
Expand Down Expand Up @@ -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.isSubTypeAnnotated =>
isNewSubType(tp1.tpe)
case JavaArrayType(elem1) =>
def compareJavaArray = tp2 match {
case JavaArrayType(elem2) => isSubType(elem1, elem2)
Expand Down Expand Up @@ -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.isSubTypeAnnotated =>
isMatchingApply(tycon1.underlying)
case _ =>
false
Expand Down Expand Up @@ -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.isSubTypeAnnotated =>
recur(tp1, tp2.superType)
case tycon2: AppliedType =>
fallback(tycon2.lowerBound)
Expand Down Expand Up @@ -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.isSubTypeAnnotated =>
tp1.underlying & tp2
case _ =>
NoType
Expand All @@ -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.isSubTypeAnnotated =>
tp1.underlying | tp2
case _ =>
NoType
Expand Down
20 changes: 20 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
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 == _)
Expand Down Expand Up @@ -3693,6 +3702,17 @@ object Types {

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

private[this] var isSubTypeAnnotatedKnown = false
private[this] var isSubTypeAnnotatedCache: Boolean = _

def isSubTypeAnnotated(implicit ctx: Context) = {
if (!isSubTypeAnnotatedKnown) {
isSubTypeAnnotatedCache = annot.symbol.derivesFrom(defn.SubTypeAnnotationClass)
isSubTypeAnnotatedKnown = true
}
isSubTypeAnnotatedCache
}

override def iso(that: Any, bs: BinderPairs): Boolean = that match {
case that: AnnotatedType => tpe.equals(that.tpe, bs) && (annot `eq` that.annot)
case _ => false
Expand Down
44 changes: 24 additions & 20 deletions compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,8 @@ Standard-Section: "ASTs" TopLevelStat*
ERASED
LAZY
OVERRIDE
INLINE // inline method
INLINE
TRANSPARENT
MACRO // inline method containing toplevel splices
STATIC // mapped to static Java member
OBJECT // an object or its class
Expand Down Expand Up @@ -280,25 +281,26 @@ object TastyFormat {
final val LAZY = 14
final val OVERRIDE = 15
final val INLINE = 16
final val STATIC = 17
final val OBJECT = 18
final val TRAIT = 19
final val ENUM = 20
final val LOCAL = 21
final val SYNTHETIC = 22
final val ARTIFACT = 23
final val MUTABLE = 24
final val LABEL = 25
final val FIELDaccessor = 26
final val CASEaccessor = 27
final val COVARIANT = 28
final val CONTRAVARIANT = 29
final val SCALA2X = 30
final val DEFAULTparameterized = 31
final val STABLE = 32
final val MACRO = 33
final val ERASED = 34
final val PARAMsetter = 35
final val TRANSPARENT = 17
final val STATIC = 18
final val OBJECT = 19
final val TRAIT = 20
final val ENUM = 21
final val LOCAL = 22
final val SYNTHETIC = 23
final val ARTIFACT = 24
final val MUTABLE = 25
final val LABEL = 26
final val FIELDaccessor = 27
final val CASEaccessor = 28
final val COVARIANT = 29
final val CONTRAVARIANT = 30
final val SCALA2X = 31
final val DEFAULTparameterized = 32
final val STABLE = 33
final val MACRO = 34
final val ERASED = 35
final val PARAMsetter = 36

// Cat. 2: tag Nat

Expand Down Expand Up @@ -446,6 +448,7 @@ object TastyFormat {
| LAZY
| OVERRIDE
| INLINE
| TRANSPARENT
| MACRO
| STATIC
| OBJECT
Expand Down Expand Up @@ -502,6 +505,7 @@ object TastyFormat {
case LAZY => "LAZY"
case OVERRIDE => "OVERRIDE"
case INLINE => "INLINE"
case TRANSPARENT => "TRANSPARENT"
case MACRO => "MACRO"
case STATIC => "STATIC"
case OBJECT => "OBJECT"
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,7 @@ class TreePickler(pickler: TastyPickler) {
if (flags is Case) writeByte(CASE)
if (flags is Override) writeByte(OVERRIDE)
if (flags is Inline) writeByte(INLINE)
if (flags is Transparent) writeByte(TRANSPARENT)
if (flags is Macro) writeByte(MACRO)
if (flags is JavaStatic) writeByte(STATIC)
if (flags is Module) writeByte(OBJECT)
Expand Down
Loading