@@ -3,6 +3,8 @@ package core
3
3
4
4
import Contexts ._ , Types ._ , Symbols ._ , Names ._ , Flags ._ , Scopes ._
5
5
import SymDenotations ._
6
+ import config .Printers ._
7
+ import Decorators ._
6
8
import util .SimpleMap
7
9
8
10
trait TypeOps { this : Context =>
@@ -74,8 +76,29 @@ trait TypeOps { this: Context =>
74
76
def apply (tp : Type ) = simplify(tp, this )
75
77
}
76
78
79
+ /** A type is volatile if its DNF contains an alternative of the form
80
+ * {P1, ..., Pn}, {N1, ..., Nk}, where the Pi are parent typerefs and the
81
+ * Nj are refinement names, and one the 4 following conditions is met:
82
+ *
83
+ * 1. At least two of the parents Pi are abstract types.
84
+ * 2. One of the parents Pi is an abstract type, and one other type Pj,
85
+ * j != i has an abstract member which has the same name as an
86
+ * abstract member of the whole type.
87
+ * 3. One of the parents Pi is an abstract type, and one of the refinement
88
+ * names Nj refers to an abstract member of the whole type.
89
+ * 4. One of the parents Pi is an an alias type with a volatile alias
90
+ * or an abstract type with a volatile upper bound.
91
+ *
92
+ * Lazy values are not allowed to have volatile type, as otherwise
93
+ * unsoundness can result.
94
+ */
77
95
final def isVolatile (tp : Type ): Boolean = {
78
- /** Pre-filter to avoid expensive DNF computation */
96
+
97
+ /** Pre-filter to avoid expensive DNF computation
98
+ * If needsChecking returns false it is guaranteed that
99
+ * DNF does not contain intersections, or abstract types with upper
100
+ * bounds that themselves need checking.
101
+ */
79
102
def needsChecking (tp : Type , isPart : Boolean ): Boolean = tp match {
80
103
case tp : TypeRef =>
81
104
tp.info match {
@@ -88,31 +111,62 @@ trait TypeOps { this: Context =>
88
111
needsChecking(tp.parent, true )
89
112
case tp : TypeProxy =>
90
113
needsChecking(tp.underlying, isPart)
91
- case AndType (l, r) =>
92
- needsChecking(l, true ) || needsChecking(r, true )
93
- case OrType (l, r) =>
94
- isPart || needsChecking(l , isPart) && needsChecking(r , isPart)
114
+ case tp : AndType =>
115
+ true
116
+ case tp : OrType =>
117
+ isPart || needsChecking(tp.tp1 , isPart) && needsChecking(tp.tp2 , isPart)
95
118
case _ =>
96
119
false
97
120
}
121
+
98
122
needsChecking(tp, false ) && {
99
- tp. DNF forall { case (parents, refinedNames) =>
123
+ DNF (tp) forall { case (parents, refinedNames) =>
100
124
val absParents = parents filter (_.symbol is Deferred )
101
- absParents.size >= 2 || {
102
- val ap = absParents.head
103
- ((parents exists (p =>
104
- (p ne ap)
105
- || p.memberNames(abstractTypeNameFilter, tp).nonEmpty
106
- || p.memberNames(abstractTermNameFilter, tp).nonEmpty))
107
- || (refinedNames & tp.memberNames(abstractTypeNameFilter, tp)).nonEmpty
108
- || (refinedNames & tp.memberNames(abstractTermNameFilter, tp)).nonEmpty
109
- || isVolatile(ap)
110
- )
125
+ absParents.nonEmpty && {
126
+ absParents.lengthCompare(2 ) >= 0 || {
127
+ val ap = absParents.head
128
+ ((parents exists (p =>
129
+ (p ne ap)
130
+ || p.memberNames(abstractTypeNameFilter, tp).nonEmpty
131
+ || p.memberNames(abstractTermNameFilter, tp).nonEmpty))
132
+ || (refinedNames & tp.memberNames(abstractTypeNameFilter, tp)).nonEmpty
133
+ || (refinedNames & tp.memberNames(abstractTermNameFilter, tp)).nonEmpty
134
+ || isVolatile(ap))
135
+ }
111
136
}
112
137
}
113
138
}
114
139
}
115
140
141
+ /** The disjunctive normal form of this type.
142
+ * This collects a set of alternatives, each alternative consisting
143
+ * of a set of typerefs and a set of refinement names. Both sets are represented
144
+ * as lists, to obtain a deterministic order. Collected are
145
+ * all type refs reachable by following aliases and type proxies, and
146
+ * collecting the elements of conjunctions (&) and disjunctions (|).
147
+ * The set of refinement names in each alternative
148
+ * are the set of names in refinement types encountered during the collection.
149
+ */
150
+ final def DNF (tp : Type ): List [(List [TypeRef ], Set [Name ])] = ctx.traceIndented(s " DNF( $this) " , checks) {
151
+ tp.dealias match {
152
+ case tp : TypeRef =>
153
+ (tp :: Nil , Set [Name ]()) :: Nil
154
+ case RefinedType (parent, name) =>
155
+ for ((ps, rs) <- DNF (parent)) yield (ps, rs + name)
156
+ case tp : TypeProxy =>
157
+ DNF (tp.underlying)
158
+ case AndType (l, r) =>
159
+ for ((lps, lrs) <- DNF (l); (rps, rrs) <- DNF (r))
160
+ yield (lps | rps, lrs | rrs)
161
+ case OrType (l, r) =>
162
+ DNF (l) | DNF (r)
163
+ case tp =>
164
+ TypeOps .emptyDNF
165
+ }
166
+ }
167
+
168
+
169
+
116
170
private def enterArgBinding (formal : Symbol , info : Type , cls : ClassSymbol , decls : Scope ) = {
117
171
val lazyInfo = new LazyType { // needed so we do not force `formal`.
118
172
def complete (denot : SymDenotation )(implicit ctx : Context ): Unit = {
@@ -215,6 +269,6 @@ trait TypeOps { this: Context =>
215
269
}
216
270
217
271
object TypeOps {
218
-
272
+ val emptyDNF = ( Nil , Set [ Name ]()) :: Nil
219
273
var track = false // !!!DEBUG
220
274
}
0 commit comments