Skip to content

Commit 1fc78c2

Browse files
committed
Rework denotations
Make PreDenotation a superclass of Denotation. The immediate need for this is that we would like to add an `asSeenFrom` to Denotations, but if we do that it clashes with the `asSeenFrom` in Predenotations.
1 parent f796d5a commit 1fc78c2

File tree

1 file changed

+169
-142
lines changed

1 file changed

+169
-142
lines changed

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

Lines changed: 169 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,78 @@ object Denotations {
7272

7373
implicit def eqDenotation: Eq[Denotation, Denotation] = Eq
7474

75+
/** A PreDenotation represents a group of single denotations or a single multi-denotation
76+
* It is used as an optimization to avoid forming MultiDenotations too eagerly.
77+
*/
78+
abstract class PreDenotation extends util.DotClass {
79+
80+
/** A denotation in the group exists */
81+
def exists: Boolean
82+
83+
/** First/last denotation in the group */
84+
def first: Denotation
85+
def last: Denotation
86+
87+
/** Convert to full denotation by &-ing all elements */
88+
def toDenot(pre: Type)(implicit ctx: Context): Denotation
89+
90+
/** Group contains a denotation that refers to given symbol */
91+
def containsSym(sym: Symbol): Boolean
92+
93+
/** Group contains a denotation with the same signature as `other` */
94+
def matches(other: SingleDenotation)(implicit ctx: Context): Boolean
95+
96+
/** Keep only those denotations in this group which satisfy predicate `p`. */
97+
def filterWithPredicate(p: SingleDenotation => Boolean): PreDenotation
98+
99+
/** Keep only those denotations in this group which have a signature
100+
* that's not already defined by `denots`.
101+
*/
102+
def filterDisjoint(denots: PreDenotation)(implicit ctx: Context): PreDenotation
103+
104+
/** Keep only those inherited members M of this predenotation for which the following is true
105+
* - M is not marked Private
106+
* - If M has a unique symbol, it does not appear in `prevDenots`.
107+
* - M's signature as seen from prefix `pre` does not appear in `ownDenots`
108+
* Return the denotation as seen from `pre`.
109+
* Called from SymDenotations.computeMember. There, `ownDenots` are the denotations found in
110+
* the base class, which shadow any inherited denotations with the same signature.
111+
* `prevDenots` are the denotations that are defined in the class or inherited from
112+
* a base type which comes earlier in the linearization.
113+
*/
114+
def mapInherited(ownDenots: PreDenotation, prevDenots: PreDenotation, pre: Type)(implicit ctx: Context): PreDenotation
115+
116+
/** Keep only those denotations in this group whose flags do not intersect
117+
* with `excluded`.
118+
*/
119+
def filterExcluded(excluded: FlagSet)(implicit ctx: Context): PreDenotation
120+
121+
private[this] var cachedPrefix: Type = _
122+
private[this] var cachedAsSeenFrom: AsSeenFromResult = _
123+
private[this] var validAsSeenFrom: Period = Nowhere
124+
125+
type AsSeenFromResult <: PreDenotation
126+
127+
/** The denotation with info(s) as seen from prefix type */
128+
final def asSeenFrom(pre: Type)(implicit ctx: Context): AsSeenFromResult =
129+
if (Config.cacheAsSeenFrom) {
130+
if ((cachedPrefix ne pre) || ctx.period != validAsSeenFrom) {
131+
cachedAsSeenFrom = computeAsSeenFrom(pre)
132+
cachedPrefix = pre
133+
validAsSeenFrom = ctx.period
134+
}
135+
cachedAsSeenFrom
136+
} else computeAsSeenFrom(pre)
137+
138+
protected def computeAsSeenFrom(pre: Type)(implicit ctx: Context): AsSeenFromResult
139+
140+
/** The union of two groups. */
141+
def union(that: PreDenotation): PreDenotation =
142+
if (!this.exists) that
143+
else if (!that.exists) this
144+
else DenotUnion(this, that)
145+
}
146+
75147
/** A denotation is the result of resolving
76148
* a name (either simple identifier or select) during a given period.
77149
*
@@ -99,7 +171,9 @@ object Denotations {
99171
*
100172
* @param symbol The referencing symbol, or NoSymbol is none exists
101173
*/
102-
abstract class Denotation(val symbol: Symbol) extends util.DotClass with printing.Showable {
174+
abstract class Denotation(val symbol: Symbol) extends PreDenotation with printing.Showable {
175+
176+
type AsSeenFromResult <: Denotation
103177

104178
/** The type info of the denotation, exists only for non-overloaded denotations */
105179
def info(implicit ctx: Context): Type
@@ -121,6 +195,14 @@ object Denotations {
121195
/** Is this denotation overloaded? */
122196
final def isOverloaded = isInstanceOf[MultiDenotation]
123197

198+
/** Denotation points to unique symbol; false for overloaded denotations
199+
* and JointRef denotations.
200+
*/
201+
def hasUniqueSym: Boolean
202+
203+
/** The name of the denotation */
204+
def name(implicit ctx: Context): Name
205+
124206
/** The signature of the denotation. */
125207
def signature(implicit ctx: Context): Signature
126208

@@ -553,61 +635,19 @@ object Denotations {
553635
final def asSymDenotation = asInstanceOf[SymDenotation]
554636

555637
def toText(printer: Printer): Text = printer.toText(this)
556-
}
557638

558-
/** An overloaded denotation consisting of the alternatives of both given denotations.
559-
*/
560-
case class MultiDenotation(denot1: Denotation, denot2: Denotation) extends Denotation(NoSymbol) {
561-
final def infoOrCompleter = multiHasNot("info")
562-
final def info(implicit ctx: Context) = infoOrCompleter
563-
final def validFor = denot1.validFor & denot2.validFor
564-
final def isType = false
565-
final def signature(implicit ctx: Context) = Signature.OverloadedSignature
566-
def atSignature(sig: Signature, site: Type, relaxed: Boolean)(implicit ctx: Context): Denotation =
567-
if (sig eq Signature.OverloadedSignature) this
568-
else derivedUnionDenotation(denot1.atSignature(sig, site, relaxed), denot2.atSignature(sig, site, relaxed))
569-
def current(implicit ctx: Context): Denotation =
570-
derivedUnionDenotation(denot1.current, denot2.current)
571-
def altsWith(p: Symbol => Boolean): List[SingleDenotation] =
572-
denot1.altsWith(p) ++ denot2.altsWith(p)
573-
def suchThat(p: Symbol => Boolean)(implicit ctx: Context): SingleDenotation = {
574-
val sd1 = denot1.suchThat(p)
575-
val sd2 = denot2.suchThat(p)
576-
if (sd1.exists)
577-
if (sd2.exists)
578-
if (isDoubleDef(denot1.symbol, denot2.symbol)) doubleDefError(denot1, denot2)
579-
else throw new TypeError(i"failure to disambiguate overloaded reference at $this")
580-
else sd1
581-
else sd2
582-
}
583-
def hasAltWith(p: SingleDenotation => Boolean): Boolean =
584-
denot1.hasAltWith(p) || denot2.hasAltWith(p)
585-
def accessibleFrom(pre: Type, superAccess: Boolean)(implicit ctx: Context): Denotation = {
586-
val d1 = denot1 accessibleFrom (pre, superAccess)
587-
val d2 = denot2 accessibleFrom (pre, superAccess)
588-
if (!d1.exists) d2
589-
else if (!d2.exists) d1
590-
else derivedUnionDenotation(d1, d2)
591-
}
592-
def mapInfo(f: Type => Type)(implicit ctx: Context): Denotation =
593-
derivedUnionDenotation(denot1.mapInfo(f), denot2.mapInfo(f))
594-
def derivedUnionDenotation(d1: Denotation, d2: Denotation): Denotation =
595-
if ((d1 eq denot1) && (d2 eq denot2)) this
596-
else if (!d1.exists) d2
597-
else if (!d2.exists) d1
598-
else MultiDenotation(d1, d2)
599-
override def toString = alternatives.mkString(" <and> ")
639+
// ------ PreDenotation ops ----------------------------------------------
600640

601-
private def multiHasNot(op: String): Nothing =
602-
throw new UnsupportedOperationException(
603-
s"multi-denotation with alternatives $alternatives does not implement operation $op")
641+
final def toDenot(pre: Type)(implicit ctx: Context): Denotation = this
642+
final def containsSym(sym: Symbol): Boolean = hasUniqueSym && (symbol eq sym)
604643
}
605644

606645
/** A non-overloaded denotation */
607-
abstract class SingleDenotation(symbol: Symbol) extends Denotation(symbol) with PreDenotation {
608-
def hasUniqueSym: Boolean
646+
abstract class SingleDenotation(symbol: Symbol) extends Denotation(symbol) {
609647
protected def newLikeThis(symbol: Symbol, info: Type): SingleDenotation
610648

649+
final def name(implicit ctx: Context): Name = symbol.name
650+
611651
final def signature(implicit ctx: Context): Signature =
612652
if (isType) Signature.NotAMethod // don't force info if this is a type SymDenotation
613653
else info match {
@@ -979,13 +1019,13 @@ object Denotations {
9791019

9801020
final def first = this
9811021
final def last = this
982-
final def toDenot(pre: Type)(implicit ctx: Context): Denotation = this
983-
final def containsSym(sym: Symbol): Boolean = hasUniqueSym && (symbol eq sym)
1022+
9841023
final def matches(other: SingleDenotation)(implicit ctx: Context): Boolean = {
9851024
val d = signature.matchDegree(other.signature)
9861025
d == Signature.FullMatch ||
9871026
d >= Signature.ParamMatch && info.matches(other.info)
9881027
}
1028+
9891029
final def filterWithPredicate(p: SingleDenotation => Boolean): SingleDenotation =
9901030
if (p(this)) this else NoDenotation
9911031
final def filterDisjoint(denots: PreDenotation)(implicit ctx: Context): SingleDenotation =
@@ -1080,105 +1120,92 @@ object Denotations {
10801120
denot2.info, denot2.info)
10811121
}
10821122

1083-
// --------------- PreDenotations -------------------------------------------------
1084-
1085-
/** A PreDenotation represents a group of single denotations
1086-
* It is used as an optimization to avoid forming MultiDenotations too eagerly.
1087-
*/
1088-
trait PreDenotation {
1089-
1090-
/** A denotation in the group exists */
1091-
def exists: Boolean
1092-
1093-
/** First/last denotation in the group */
1094-
def first: Denotation
1095-
def last: Denotation
1096-
1097-
/** Convert to full denotation by &-ing all elements */
1098-
def toDenot(pre: Type)(implicit ctx: Context): Denotation
1099-
1100-
/** Group contains a denotation that refers to given symbol */
1101-
def containsSym(sym: Symbol): Boolean
1102-
1103-
/** Group contains a denotation with given signature */
1104-
def matches(other: SingleDenotation)(implicit ctx: Context): Boolean
1105-
1106-
/** Keep only those denotations in this group which satisfy predicate `p`. */
1107-
def filterWithPredicate(p: SingleDenotation => Boolean): PreDenotation
1108-
1109-
/** Keep only those denotations in this group which have a signature
1110-
* that's not already defined by `denots`.
1111-
*/
1112-
def filterDisjoint(denots: PreDenotation)(implicit ctx: Context): PreDenotation
1113-
1114-
/** Keep only those inherited members M of this predenotation for which the following is true
1115-
* - M is not marked Private
1116-
* - If M has a unique symbol, it does not appear in `prevDenots`.
1117-
* - M's signature as seen from prefix `pre` does not appear in `ownDenots`
1118-
* Return the denotation as seen from `pre`.
1119-
* Called from SymDenotations.computeMember. There, `ownDenots` are the denotations found in
1120-
* the base class, which shadow any inherited denotations with the same signature.
1121-
* `prevDenots` are the denotations that are defined in the class or inherited from
1122-
* a base type which comes earlier in the linearization.
1123-
*/
1124-
def mapInherited(ownDenots: PreDenotation, prevDenots: PreDenotation, pre: Type)(implicit ctx: Context): PreDenotation
1125-
1126-
/** Keep only those denotations in this group whose flags do not intersect
1127-
* with `excluded`.
1128-
*/
1129-
def filterExcluded(excluded: FlagSet)(implicit ctx: Context): PreDenotation
1130-
1131-
private[this] var cachedPrefix: Type = _
1132-
private[this] var cachedAsSeenFrom: AsSeenFromResult = _
1133-
private[this] var validAsSeenFrom: Period = Nowhere
1134-
type AsSeenFromResult <: PreDenotation
1135-
1136-
/** The denotation with info(s) as seen from prefix type */
1137-
final def asSeenFrom(pre: Type)(implicit ctx: Context): AsSeenFromResult =
1138-
if (Config.cacheAsSeenFrom) {
1139-
if ((cachedPrefix ne pre) || ctx.period != validAsSeenFrom) {
1140-
cachedAsSeenFrom = computeAsSeenFrom(pre)
1141-
cachedPrefix = pre
1142-
validAsSeenFrom = ctx.period
1143-
}
1144-
cachedAsSeenFrom
1145-
} else computeAsSeenFrom(pre)
1123+
// --- Overloaded denotations and predenotations -------------------------------------------------
11461124

1147-
protected def computeAsSeenFrom(pre: Type)(implicit ctx: Context): AsSeenFromResult
1125+
trait MultiPreDenotation extends PreDenotation {
1126+
def denot1: PreDenotation
1127+
def denot2: PreDenotation
11481128

1149-
/** The union of two groups. */
1150-
def union(that: PreDenotation) =
1151-
if (!this.exists) that
1152-
else if (!that.exists) this
1153-
else DenotUnion(this, that)
1129+
assert(denot1.exists && denot2.exists, s"Union of non-existing denotations ($denot1) and ($denot2)")
1130+
def first = denot1.first
1131+
def last = denot2.last
1132+
def matches(other: SingleDenotation)(implicit ctx: Context): Boolean =
1133+
denot1.matches(other) || denot2.matches(other)
1134+
def filterWithPredicate(p: SingleDenotation => Boolean): PreDenotation =
1135+
derivedUnion(denot1 filterWithPredicate p, denot2 filterWithPredicate p)
1136+
def filterDisjoint(denot: PreDenotation)(implicit ctx: Context): PreDenotation =
1137+
derivedUnion(denot1 filterDisjoint denot, denot2 filterDisjoint denot)
1138+
def mapInherited(owndenot: PreDenotation, prevdenot: PreDenotation, pre: Type)(implicit ctx: Context): PreDenotation =
1139+
derivedUnion(denot1.mapInherited(owndenot, prevdenot, pre), denot2.mapInherited(owndenot, prevdenot, pre))
1140+
def filterExcluded(excluded: FlagSet)(implicit ctx: Context): PreDenotation =
1141+
derivedUnion(denot1.filterExcluded(excluded), denot2.filterExcluded(excluded))
1142+
protected def derivedUnion(denot1: PreDenotation, denot2: PreDenotation) =
1143+
if ((denot1 eq this.denot1) && (denot2 eq this.denot2)) this
1144+
else denot1 union denot2
11541145
}
11551146

1156-
final case class DenotUnion(denots1: PreDenotation, denots2: PreDenotation) extends PreDenotation {
1157-
assert(denots1.exists && denots2.exists, s"Union of non-existing denotations ($denots1) and ($denots2)")
1147+
final case class DenotUnion(denot1: PreDenotation, denot2: PreDenotation) extends MultiPreDenotation {
11581148
def exists = true
1159-
def first = denots1.first
1160-
def last = denots2.last
11611149
def toDenot(pre: Type)(implicit ctx: Context) =
1162-
(denots1 toDenot pre) & (denots2 toDenot pre, pre)
1150+
(denot1 toDenot pre) & (denot2 toDenot pre, pre)
11631151
def containsSym(sym: Symbol) =
1164-
(denots1 containsSym sym) || (denots2 containsSym sym)
1165-
def matches(other: SingleDenotation)(implicit ctx: Context): Boolean =
1166-
denots1.matches(other) || denots2.matches(other)
1167-
def filterWithPredicate(p: SingleDenotation => Boolean): PreDenotation =
1168-
derivedUnion(denots1 filterWithPredicate p, denots2 filterWithPredicate p)
1169-
def filterDisjoint(denots: PreDenotation)(implicit ctx: Context): PreDenotation =
1170-
derivedUnion(denots1 filterDisjoint denots, denots2 filterDisjoint denots)
1171-
def mapInherited(ownDenots: PreDenotation, prevDenots: PreDenotation, pre: Type)(implicit ctx: Context): PreDenotation =
1172-
derivedUnion(denots1.mapInherited(ownDenots, prevDenots, pre), denots2.mapInherited(ownDenots, prevDenots, pre))
1173-
def filterExcluded(excluded: FlagSet)(implicit ctx: Context): PreDenotation =
1174-
derivedUnion(denots1.filterExcluded(excluded), denots2.filterExcluded(excluded))
1175-
1152+
(denot1 containsSym sym) || (denot2 containsSym sym)
11761153
type AsSeenFromResult = PreDenotation
1177-
protected def computeAsSeenFrom(pre: Type)(implicit ctx: Context): PreDenotation =
1178-
derivedUnion(denots1.asSeenFrom(pre), denots2.asSeenFrom(pre))
1179-
private def derivedUnion(denots1: PreDenotation, denots2: PreDenotation) =
1180-
if ((denots1 eq this.denots1) && (denots2 eq this.denots2)) this
1181-
else denots1 union denots2
1154+
def computeAsSeenFrom(pre: Type)(implicit ctx: Context): PreDenotation =
1155+
derivedUnion(denot1.asSeenFrom(pre), denot2.asSeenFrom(pre))
1156+
}
1157+
1158+
/** An overloaded denotation consisting of the alternatives of both given denotations.
1159+
*/
1160+
case class MultiDenotation(denot1: Denotation, denot2: Denotation) extends Denotation(NoSymbol) with MultiPreDenotation {
1161+
final def infoOrCompleter = multiHasNot("info")
1162+
final def info(implicit ctx: Context) = infoOrCompleter
1163+
final def validFor = denot1.validFor & denot2.validFor
1164+
final def isType = false
1165+
final def hasUniqueSym = false
1166+
final def name(implicit ctx: Context) = denot1.name
1167+
final def signature(implicit ctx: Context) = Signature.OverloadedSignature
1168+
def atSignature(sig: Signature, site: Type, relaxed: Boolean)(implicit ctx: Context): Denotation =
1169+
if (sig eq Signature.OverloadedSignature) this
1170+
else derivedUnionDenotation(denot1.atSignature(sig, site, relaxed), denot2.atSignature(sig, site, relaxed))
1171+
def current(implicit ctx: Context): Denotation =
1172+
derivedUnionDenotation(denot1.current, denot2.current)
1173+
def altsWith(p: Symbol => Boolean): List[SingleDenotation] =
1174+
denot1.altsWith(p) ++ denot2.altsWith(p)
1175+
def suchThat(p: Symbol => Boolean)(implicit ctx: Context): SingleDenotation = {
1176+
val sd1 = denot1.suchThat(p)
1177+
val sd2 = denot2.suchThat(p)
1178+
if (sd1.exists)
1179+
if (sd2.exists)
1180+
if (isDoubleDef(denot1.symbol, denot2.symbol)) doubleDefError(denot1, denot2)
1181+
else throw new TypeError(i"failure to disambiguate overloaded reference at $this")
1182+
else sd1
1183+
else sd2
1184+
}
1185+
def hasAltWith(p: SingleDenotation => Boolean): Boolean =
1186+
denot1.hasAltWith(p) || denot2.hasAltWith(p)
1187+
def accessibleFrom(pre: Type, superAccess: Boolean)(implicit ctx: Context): Denotation = {
1188+
val d1 = denot1 accessibleFrom (pre, superAccess)
1189+
val d2 = denot2 accessibleFrom (pre, superAccess)
1190+
if (!d1.exists) d2
1191+
else if (!d2.exists) d1
1192+
else derivedUnionDenotation(d1, d2)
1193+
}
1194+
def mapInfo(f: Type => Type)(implicit ctx: Context): Denotation =
1195+
derivedUnionDenotation(denot1.mapInfo(f), denot2.mapInfo(f))
1196+
def derivedUnionDenotation(d1: Denotation, d2: Denotation): Denotation =
1197+
if ((d1 eq denot1) && (d2 eq denot2)) this
1198+
else if (!d1.exists) d2
1199+
else if (!d2.exists) d1
1200+
else MultiDenotation(d1, d2)
1201+
type AsSeenFromResult = Denotation
1202+
def computeAsSeenFrom(pre: Type)(implicit ctx: Context): Denotation =
1203+
derivedUnionDenotation(denot1.asSeenFrom(pre), denot2.asSeenFrom(pre))
1204+
override def toString = alternatives.mkString(" <and> ")
1205+
1206+
private def multiHasNot(op: String): Nothing =
1207+
throw new UnsupportedOperationException(
1208+
s"multi-denotation with alternatives $alternatives does not implement operation $op")
11821209
}
11831210

11841211
// --------------- Context Base Trait -------------------------------

0 commit comments

Comments
 (0)