@@ -8,6 +8,7 @@ import config.Printers._
8
8
import util .Positions ._
9
9
import Decorators ._
10
10
import StdNames ._
11
+ import Annotations ._
11
12
import util .SimpleMap
12
13
import collection .mutable
13
14
import ast .tpd ._
@@ -23,31 +24,29 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
23
24
*
24
25
* and an expression `e` of type `C`. Then computing the type of `e.f` leads
25
26
* to the query asSeenFrom(`C`, `(x: T)T`). What should its result be? The
26
- * naive answer `(x: C. T)C. T` is incorrect given that we treat `C. T` as the existential
27
+ * naive answer `(x: C# T)C# T` is incorrect given that we treat `C# T` as the existential
27
28
* `exists(c: C)c.T`. What we need to do instead is to skolemize the existential. So
28
29
* the answer would be `(x: c.T)c.T` for some (unknown) value `c` of type `C`.
29
30
* `c.T` is expressed in the compiler as a skolem type `Skolem(C)`.
30
31
*
31
32
* Now, skolemization is messy and expensive, so we want to do it only if we absolutely
32
- * must. We must skolemize if an unstable prefix is used in nonvariant or
33
- * contravariant position of the return type of asSeenFrom.
33
+ * must. Also, skolemizing immediately would mean that asSeenFrom was no longer
34
+ * idempotent - each call would return a type with a different skolem.
35
+ * Instead we produce an annotated type that marks the prefix as unsafe:
34
36
*
35
- * In the implementation of asSeenFrom, we first try to run asSeenFrom without
36
- * skolemizing. If that would be incorrect we will be told by the fact that
37
- * `unstable` is set in the passed AsSeenFromMap. In that case we run asSeenFrom
38
- * again with a skolemized prefix.
37
+ * (x: (C @ UnsafeNonvariant)#T)C#T
38
+
39
+ * We also set a global state flag `unsafeNonvariant` to the current run.
40
+ * When typing a Select node, typer will check that flag, and if it
41
+ * points to the current run will scan the result type of the select for
42
+ * @UnsafeNonvariant annotations. If it finds any, it will introduce a skolem
43
+ * constant for the prefix and try again.
39
44
*
40
- * In the interest of speed we want to avoid creating an AsSeenFromMap every time
41
- * asSeenFrom is called. So we do this here only if the prefix is unstable
42
- * (because then we need the map as a container for the unstable field). For
43
- * stable prefixes the map is `null`; it might however be instantiated later
44
- * for more complicated types.
45
+ * The scheme is efficient in particular because we expect that unsafe situations are rare;
46
+ * most compiles would contain none, so no scanning would be necessary.
45
47
*/
46
- final def asSeenFrom (tp : Type , pre : Type , cls : Symbol ): Type = {
47
- val m = if (isLegalPrefix(pre)) null else new AsSeenFromMap (pre, cls)
48
- var res = asSeenFrom(tp, pre, cls, m)
49
- if (m != null && m.unstable) asSeenFrom(tp, SkolemType (pre), cls) else res
50
- }
48
+ final def asSeenFrom (tp : Type , pre : Type , cls : Symbol ): Type =
49
+ asSeenFrom(tp, pre, cls, null )
51
50
52
51
/** Helper method, taking a map argument which is instantiated only for more
53
52
* complicated cases of asSeenFrom.
@@ -65,9 +64,11 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
65
64
case pre : SuperType => toPrefix(pre.thistpe, cls, thiscls)
66
65
case _ =>
67
66
if (thiscls.derivesFrom(cls) && pre.baseTypeRef(thiscls).exists) {
68
- if (theMap != null && theMap.currentVariance <= 0 && ! isLegalPrefix(pre))
69
- theMap.unstable = true
70
- pre
67
+ if (theMap != null && theMap.currentVariance <= 0 && ! isLegalPrefix(pre)) {
68
+ ctx.base.unsafeNonvariant = ctx.runId
69
+ AnnotatedType (pre, Annotation (defn.UnsafeNonvariantAnnot , Nil ))
70
+ }
71
+ else pre
71
72
}
72
73
else if ((pre.termSymbol is Package ) && ! (thiscls is Package ))
73
74
toPrefix(pre.select(nme.PACKAGE ), cls, thiscls)
@@ -82,17 +83,14 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
82
83
val sym = tp.symbol
83
84
if (sym.isStatic) tp
84
85
else {
85
- val prevStable = theMap == null || ! theMap.unstable
86
86
val pre1 = asSeenFrom(tp.prefix, pre, cls, theMap)
87
- if (theMap != null && theMap.unstable && prevStable) {
87
+ if (pre1.isUnsafeNonvariant)
88
88
pre1.member(tp.name).info match {
89
89
case TypeAlias (alias) =>
90
90
// try to follow aliases of this will avoid skolemization.
91
- theMap.unstable = false
92
91
return alias
93
92
case _ =>
94
93
}
95
- }
96
94
tp.derivedSelect(pre1)
97
95
}
98
96
case tp : ThisType =>
@@ -122,11 +120,6 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
122
120
123
121
/** A method to export the current variance of the map */
124
122
def currentVariance = variance
125
-
126
- /** A field which indicates whether an unstable argument in nonvariant
127
- * or contravariant position was encountered.
128
- */
129
- var unstable = false
130
123
}
131
124
132
125
/** Approximate a type `tp` with a type that does not contain skolem types.
0 commit comments