@@ -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+ */
5462class 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 =>
0 commit comments