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