@@ -66,24 +66,53 @@ class CheckRealizable(implicit ctx: Context) {
66
66
*/
67
67
private def isLateInitialized (sym : Symbol ) = sym.is(LateInitialized , butNot = Module )
68
68
69
- /** The realizability status of given type `tp`*/
69
+ /** The realizability status of given type `tp`. Types can only project
70
+ * members from realizable types, that is, types having non-null values and
71
+ * not depending on mutable state.
72
+ * Beware that subtypes of realizable types might not be realizable; hence
73
+ * realizability checking restricts overriding. */
70
74
def realizability (tp : Type ): Realizability = tp.dealias match {
71
75
case tp : TermRef =>
76
+ // Suppose tp is a.b.c.type, where c is declared with type T, then sym is c, tp.info is T and
77
+ // and tp.prefix is a.b.
72
78
val sym = tp.symbol
73
- val r =
74
- if (sym.is(Stable )) realizability(tp.prefix)
79
+ // tp is realizable if it selects a stable member on a realizable prefix.
80
+
81
+ /** A member is always stable if tp.info is a realizable singleton type. We check this last
82
+ for performance, in all cases where some unrelated check might have failed. */
83
+ def patchRealizability (r : Realizability ) =
84
+ r.mapError(if (tp.info.isStableRealizable) Realizable else _)
85
+
86
+ def computeStableMember (): Realizability = {
87
+ // Reject fields that are mutable, by-name, and similar.
88
+ if (! sym.isStable)
89
+ patchRealizability(NotStable )
90
+ // Accept non-lazy symbols "lazy"
91
+ else if (! isLateInitialized(sym))
92
+ patchRealizability(Realizable )
93
+ // Accept symbols that are lazy/erased, can't be overridden, and
94
+ // have a realizable type. We require finality because overrides
95
+ // of realizable fields might not be realizable.
96
+ else if (! sym.isEffectivelyFinal)
97
+ patchRealizability(new NotFinal (sym))
98
+ else
99
+ // Since patchRealizability checks realizability(tp.info) through
100
+ // isStableRealizable, using patchRealizability wouldn't make
101
+ // a difference. Calling it here again might introduce a slowdown
102
+ // exponential in the prefix length, so we avoid it at the cost of
103
+ // code complexity.
104
+ realizability(tp.info).mapError(r => new ProblemInUnderlying (tp.info, r))
105
+ }
106
+
107
+ val stableMember =
108
+ // 1. the symbol is flagged as stable.
109
+ if (sym.is(Stable )) Realizable
75
110
else {
76
- val r =
77
- if (! sym.isStable) NotStable
78
- else if (! isLateInitialized(sym)) Realizable
79
- else if (! sym.isEffectivelyFinal) new NotFinal (sym)
80
- else realizability(tp.info).mapError(r => new ProblemInUnderlying (tp.info, r))
81
- r andAlso {
82
- sym.setFlag(Stable )
83
- realizability(tp.prefix).mapError(r => new ProblemInUnderlying (tp.prefix, r))
84
- }
111
+ val r = computeStableMember()
112
+ if (r == Realizable ) sym.setFlag(Stable )
113
+ r
85
114
}
86
- if ( r == Realizable || tp.info.isStableRealizable) Realizable else r
115
+ stableMember andAlso realizability(tp.prefix).mapError( r => new ProblemInUnderlying ( tp.prefix, r))
87
116
case _ : SingletonType | NoPrefix =>
88
117
Realizable
89
118
case tp =>
0 commit comments