Skip to content

Commit c05e58b

Browse files
committed
Document stability and realizability
1 parent 43455d6 commit c05e58b

File tree

3 files changed

+40
-4
lines changed

3 files changed

+40
-4
lines changed

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

+23-2
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,15 @@ object CheckRealizable {
5050
private val LateInitialized = Lazy | Erased
5151
}
5252

53-
/** Compute realizability status */
53+
/** Compute realizability status.
54+
*
55+
* A type T is realizable iff it is inhabited by non-null values. This ensures that its type members have good bounds
56+
* (in the sense from DOT papers). A type projection T#L is legal if T is realizable, and can be understood as
57+
* Scala 2's `v.L forSome { val v: T }`.
58+
*
59+
* In general, a realizable type can have multiple inhabitants, hence it need not be stable (in the sense of
60+
* Type.isStable).
61+
*/
5462
class CheckRealizable(implicit ctx: Context) {
5563
import CheckRealizable._
5664

@@ -66,6 +74,15 @@ class CheckRealizable(implicit ctx: Context) {
6674

6775
/** The realizability status of given type `tp`*/
6876
def realizability(tp: Type): Realizability = tp.dealias match {
77+
/*
78+
* A `TermRef` for a path `p` is realizable if
79+
* - `p`'s type is stable and realizable, or
80+
* - its underlying path is idempotent (that is, *stable*), total, and not null.
81+
* We don't check yet the "not null" clause: that will require null-safety checking.
82+
*
83+
* We assume that stability of tp.prefix is checked elsewhere, since that's necessary for the path to be legal in
84+
* the first place.
85+
*/
6986
case tp: TermRef =>
7087
val sym = tp.symbol
7188
lazy val tpInfoRealizable = realizability(tp.info)
@@ -86,7 +103,11 @@ class CheckRealizable(implicit ctx: Context) {
86103
if (sym.isStableMember) sym.setFlag(StableRealizable) // it's known to be stable and realizable
87104
realizability(tp.prefix)
88105
} mapError { r =>
89-
if (tp.info.isStable && tpInfoRealizable == Realizable) Realizable else r
106+
// A mutable path is in fact stable and realizable if it has a realizable singleton type.
107+
if (tp.info.isStable && tpInfoRealizable == Realizable) {
108+
sym.setFlag(StableRealizable)
109+
Realizable
110+
} else r
90111
}
91112
}
92113
case _: SingletonType | NoPrefix =>

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

+10-1
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,16 @@ object SymDenotations {
608608
}
609609
)
610610

611-
/** Is this a denotation of a stable term (or an arbitrary type)? */
611+
/** Is this a denotation of a stable term (or an arbitrary type)?
612+
* Terms are stable if they are idempotent (as in TreeInfo.Idempotent): that is, they always return the same value,
613+
* if any.
614+
*
615+
* A *member* is stable, basically, if it behaves like a field projection: that is, it projects a constant result
616+
* out of its owner.
617+
*
618+
* However, a stable member might not yet be initialized (if it is an object or anyhow lazy).
619+
* So the first call to a stable member might fail and/or produce side effects.
620+
*/
612621
final def isStableMember(implicit ctx: Context): Boolean = {
613622
def isUnstableValue = is(UnstableValue) || info.isInstanceOf[ExprType]
614623
isType || is(StableRealizable) || !isUnstableValue

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

+7-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,11 @@ object Types {
147147
/** Is this a value type or a wildcard? */
148148
final def isValueTypeOrWildcard: Boolean = isValueType || this.isInstanceOf[WildcardType]
149149

150-
/** Does this type denote a stable reference (i.e. singleton type)? */
150+
/** Does this type denote a stable reference (i.e. singleton type)?
151+
*
152+
* Like in isStableMember, "stability" means idempotence.
153+
* Rationale: If an expression has a stable type, the expression must be idempotent, so stable types
154+
* must be singleton types of stable expressions. */
151155
final def isStable(implicit ctx: Context): Boolean = stripTypeVar match {
152156
case tp: TermRef => tp.symbol.isStableMember && tp.prefix.isStable || tp.info.isStable
153157
case _: SingletonType | NoPrefix => true
@@ -2200,6 +2204,8 @@ object Types {
22002204
def underlyingRef: TermRef
22012205
}
22022206

2207+
/** The singleton type for path prefix#myDesignator.
2208+
*/
22032209
abstract case class TermRef(override val prefix: Type,
22042210
private var myDesignator: Designator)
22052211
extends NamedType with SingletonType with ImplicitRef {

0 commit comments

Comments
 (0)