@@ -66,24 +66,53 @@ 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 and
71+ * not depending on mutable state.
72+ * Beware that subtypes of realizable types might not be realizable; hence
73+ * realizability checking restricts overriding. */
7074 def realizability (tp : Type ): Realizability = tp.dealias match {
7175 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.
7278 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
75110 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
85114 }
86- if ( r == Realizable || tp.info.isStableRealizable) Realizable else r
115+ stableMember andAlso realizability(tp.prefix).mapError( r => new ProblemInUnderlying ( tp.prefix, r))
87116 case _ : SingletonType | NoPrefix =>
88117 Realizable
89118 case tp =>
0 commit comments