Skip to content

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

Merged
merged 5 commits into from
Jun 8, 2018
Merged
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
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ object desugar {
case tp: NamedType if tp.symbol.exists && (tp.symbol.owner eq originalOwner) =>
val defctx = ctx.outersIterator.dropWhile(_.scope eq ctx.scope).next()
var local = defctx.denotNamed(tp.name ++ suffix).suchThat(_.isParamOrAccessor).symbol
if (local.exists) (defctx.owner.thisType select local).dealias
if (local.exists) (defctx.owner.thisType select local).dealiasKeepAnnots
else {
def msg =
s"no matching symbol for ${tp.symbol.showLocated} in ${defctx.owner} / ${defctx.effectiveScope.toList}"
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: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -963,7 +963,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
val alternatives = ctx.typer.resolveOverloaded(allAlts, proto)
assert(alternatives.size == 1,
i"${if (alternatives.isEmpty) "no" else "multiple"} overloads available for " +
i"$method on ${receiver.tpe.widenDealias} with targs: $targs%, %; args: $args%, % of types ${args.tpes}%, %; expectedType: $expectedType." +
i"$method on ${receiver.tpe.widenDealiasKeepAnnots} with targs: $targs%, %; args: $args%, % of types ${args.tpes}%, %; expectedType: $expectedType." +
i" isAnnotConstructor = $isAnnotConstructor.\n" +
i"all alternatives: ${allAlts.map(_.symbol.showDcl).mkString(", ")}\n" +
i"matching alternatives: ${alternatives.map(_.symbol.showDcl).mkString(", ")}.") // this is parsed from bytecode tree. there's nothing user can do about it
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
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ trait ConstraintHandling {
case tp: OrType => isFullyDefined(tp.tp1) && isFullyDefined(tp.tp2)
case _ => true
}
def isOrType(tp: Type): Boolean = tp.stripTypeVar.dealias match {
def isOrType(tp: Type): Boolean = tp.dealias match {
case tp: OrType => true
case tp: RefinedOrRecType => isOrType(tp.parent)
case AndType(tp1, tp2) => isOrType(tp1) | isOrType(tp2)
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 RefiningAnnotationType = ctx.requiredClassRef("scala.annotation.RefiningAnnotation")
def RefiningAnnotationClass(implicit ctx: Context) = RefiningAnnotationType.symbol.asClass

// Annotation classes
lazy val AliasAnnotType = ctx.requiredClassRef("scala.annotation.internal.Alias")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
* that are not top-level are not affected.
*/
def replace(param: TypeParamRef, tp: Type)(implicit ctx: Context): OrderingConstraint = {
val replacement = tp.dealias.stripTypeVar
val replacement = tp.dealiasKeepAnnots.stripTypeVar
if (param == replacement) this
else {
assert(replacement.isValueTypeOrLambda)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1208,7 +1208,7 @@ object SymDenotations {
case tp: LambdaType => tp.paramInfos.exists(hasSkolems) || hasSkolems(tp.resType)
case tp: AndType => hasSkolems(tp.tp1) || hasSkolems(tp.tp2)
case tp: OrType => hasSkolems(tp.tp1) || hasSkolems(tp.tp2)
case tp: AnnotatedType => hasSkolems(tp.tpe)
case tp: AnnotatedType => hasSkolems(tp.parent)
case _ => false
}

Expand Down
36 changes: 21 additions & 15 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.isRefining =>
recur(tp1, tp2.parent)
case tp2: ThisType =>
def compareThis = {
val cls2 = tp2.cls
Expand Down Expand Up @@ -345,13 +345,13 @@ 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 =>
recur(tp1.tpe, tp2)
case tp1: AnnotatedType if !tp1.isRefining =>
recur(tp1.parent, tp2)
case AndType(tp11, tp12) =>
if (tp11.stripTypeVar eq tp12.stripTypeVar) recur(tp11, tp2)
else thirdTry
case tp1 @ OrType(tp11, tp12) =>
def joinOK = tp2.dealias match {
def joinOK = tp2.dealiasKeepRefiningAnnots match {
case tp2: AppliedType if !tp2.tycon.typeSymbol.isClass =>
// If we apply the default algorithm for `A[X] | B[Y] <: C[Z]` where `C` is a
// type parameter, we will instantiate `C` to `A` and then fail when comparing
Expand Down Expand Up @@ -510,7 +510,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
}
compareTypeLambda
case OrType(tp21, tp22) =>
val tp1a = tp1.widenDealias
val tp1a = tp1.widenDealiasKeepRefiningAnnots
if (tp1a ne tp1)
// Follow the alias; this might avoid truncating the search space in the either below
// Note that it's safe to widen here because singleton types cannot be part of `|`.
Expand Down Expand Up @@ -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.parent)
case ClassInfo(pre2, cls2, _, _, _) =>
def compareClassInfo = tp1 match {
case ClassInfo(pre1, cls1, _, _, _) =>
Expand Down Expand Up @@ -641,7 +644,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
}
compareHKLambda
case AndType(tp11, tp12) =>
val tp2a = tp2.dealias
val tp2a = tp2.dealiasKeepRefiningAnnots
if (tp2a ne tp2) // Follow the alias; this might avoid truncating the search space in the either below
return recur(tp1, tp2a)

Expand All @@ -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.parent)
case JavaArrayType(elem1) =>
def compareJavaArray = tp2 match {
case JavaArrayType(elem2) => isSubType(elem1, elem2)
Expand All @@ -685,13 +690,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
*/
def isMatchingApply(tp1: Type): Boolean = tp1 match {
case AppliedType(tycon1, args1) =>
tycon1.dealias match {
tycon1.dealiasKeepRefiningAnnots match {
case tycon1: TypeParamRef =>
(tycon1 == tycon2 ||
canConstrain(tycon1) && tryInstantiate(tycon1, tycon2)) &&
isSubArgs(args1, args2, tp1, tparams)
case tycon1: TypeRef =>
tycon2.dealias match {
tycon2.dealiasKeepRefiningAnnots match {
case tycon2: TypeRef if tycon1.symbol == tycon2.symbol =>
isSubType(tycon1.prefix, tycon2.prefix) &&
isSubArgs(args1, args2, tp1, tparams)
Expand All @@ -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 =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure when a type constructor would have a RefiningAnnotation, but wouldn't it in principle be okay to widen here, regardless of whether the annotation is refining?

Copy link
Contributor Author

@odersky odersky Jun 8, 2018

Choose a reason for hiding this comment

The 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.

Copy link
Member

Choose a reason for hiding this comment

The 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
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.isRefining =>
recur(tp1, tp2.superType)
case tycon2: AppliedType =>
fallback(tycon2.lowerBound)
Expand Down Expand Up @@ -1097,11 +1104,10 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
* is some combination of TypeRefs that point to classes, where the
* combiners are AppliedTypes, RefinedTypes, RecTypes, And/Or-Types or AnnotatedTypes.
*/
private def isCovered(tp: Type): Boolean = tp.dealias.stripTypeVar match {
private def isCovered(tp: Type): Boolean = tp.dealiasKeepRefiningAnnots.stripTypeVar match {
case tp: TypeRef => tp.symbol.isClass && tp.symbol != NothingClass && tp.symbol != NullClass
case tp: AppliedType => isCovered(tp.tycon)
case tp: RefinedOrRecType => isCovered(tp.parent)
case tp: AnnotatedType => isCovered(tp.underlying)
case tp: AndType => isCovered(tp.tp1) && isCovered(tp.tp2)
case tp: OrType => isCovered(tp.tp1) && isCovered(tp.tp2)
case _ => false
Expand Down Expand Up @@ -1546,7 +1552,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
Copy link
Member

Choose a reason for hiding this comment

The 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
Expand All @@ -1565,7 +1571,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
Expand Down
Loading