@@ -66,24 +66,52 @@ class CheckRealizable(implicit ctx: Context) {
6666 */
6767 private def isLateInitialized (sym : Symbol ) = sym.is(LateInitialized , butNot = Module )
6868
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. */
7073 def realizability (tp : Type ): Realizability = tp.dealias match {
7174 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.
7277 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
75109 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
85113 }
86- if ( r == Realizable || tp.info.isStableRealizable) Realizable else r
114+ stableMember andAlso realizability(tp.prefix).mapError( r => new ProblemInUnderlying ( tp.prefix, r))
87115 case _ : SingletonType | NoPrefix =>
88116 Realizable
89117 case tp =>
0 commit comments