@@ -19,131 +19,76 @@ import ast.tpd._
1919trait TypeOps { this : Context => // TODO: Make standalone object.
2020
2121 /** The type `tp` as seen from prefix `pre` and owner `cls`. See the spec
22- * for what this means. Called very often, so the code is optimized heavily.
23- *
24- * A tricky aspect is what to do with unstable prefixes. E.g. say we have a class
25- *
26- * class C { type T; def f(x: T): T }
27- *
28- * and an expression `e` of type `C`. Then computing the type of `e.f` leads
29- * to the query asSeenFrom(`C`, `(x: T)T`). What should its result be? The
30- * naive answer `(x: C#T)C#T` is incorrect given that we treat `C#T` as the existential
31- * `exists(c: C)c.T`. What we need to do instead is to skolemize the existential. So
32- * the answer would be `(x: c.T)c.T` for some (unknown) value `c` of type `C`.
33- * `c.T` is expressed in the compiler as a skolem type `Skolem(C)`.
34- *
35- * Now, skolemization is messy and expensive, so we want to do it only if we absolutely
36- * must. Also, skolemizing immediately would mean that asSeenFrom was no longer
37- * idempotent - each call would return a type with a different skolem.
38- * Instead we produce an annotated type that marks the prefix as unsafe:
39- *
40- * (x: (C @ UnsafeNonvariant)#T)C#T
41- *
42- * We also set a global state flag `unsafeNonvariant` to the current run.
43- * When typing a Select node, typer will check that flag, and if it
44- * points to the current run will scan the result type of the select for
45- * @UnsafeNonvariant annotations. If it finds any, it will introduce a skolem
46- * constant for the prefix and try again.
47- *
48- * The scheme is efficient in particular because we expect that unsafe situations are rare;
49- * most compiles would contain none, so no scanning would be necessary.
22+ * for what this means.
5023 */
5124 final def asSeenFrom (tp : Type , pre : Type , cls : Symbol ): Type =
52- asSeenFrom(tp, pre, cls, null )
25+ new AsSeenFromMap ( pre, cls).apply(tp )
5326
54- /** Helper method, taking a map argument which is instantiated only for more
55- * complicated cases of asSeenFrom.
56- */
57- private def asSeenFrom (tp : Type , pre : Type , cls : Symbol , theMap : AsSeenFromMap ): Type = {
58-
59- /** Map a `C.this` type to the right prefix. If the prefix is unstable and
60- * the `C.this` occurs in nonvariant or contravariant position, mark the map
61- * to be unstable.
62- */
63- def toPrefix (pre : Type , cls : Symbol , thiscls : ClassSymbol ): Type = /* >|>*/ ctx.conditionalTraceIndented(TypeOps .track, s " toPrefix( $pre, $cls, $thiscls) " ) /* <|<*/ {
64- if ((pre eq NoType ) || (pre eq NoPrefix ) || (cls is PackageClass ))
65- tp
66- else pre match {
67- case pre : SuperType => toPrefix(pre.thistpe, cls, thiscls)
68- case _ =>
69- if (thiscls.derivesFrom(cls) && pre.baseTypeRef(thiscls).exists) {
70- if (theMap != null && theMap.currentVariance <= 0 && ! isLegalPrefix(pre)) {
71- ctx.base.unsafeNonvariant = ctx.runId
72- pre match {
73- case AnnotatedType (_, ann) if ann.symbol == defn.UnsafeNonvariantAnnot => pre
74- case _ => AnnotatedType (pre, Annotation (defn.UnsafeNonvariantAnnot , Nil ))
75- }
76- }
77- else pre
78- }
79- else if ((pre.termSymbol is Package ) && ! (thiscls is Package ))
80- toPrefix(pre.select(nme.PACKAGE ), cls, thiscls)
81- else
82- toPrefix(pre.baseTypeRef(cls).normalizedPrefix, cls.owner, thiscls)
27+ /** The TypeMap handling the asSeenFrom */
28+ class AsSeenFromMap (pre : Type , cls : Symbol ) extends ApproximatingTypeMap {
29+
30+ def apply (tp : Type ): Type = {
31+
32+ /** Map a `C.this` type to the right prefix. If the prefix is unstable and
33+ * the `C.this` occurs in nonvariant or contravariant position, mark the map
34+ * to be unstable.
35+ */
36+ def toPrefix (pre : Type , cls : Symbol , thiscls : ClassSymbol ): Type = /* >|>*/ ctx.conditionalTraceIndented(TypeOps .track, s " toPrefix( $pre, $cls, $thiscls) " ) /* <|<*/ {
37+ if ((pre eq NoType ) || (pre eq NoPrefix ) || (cls is PackageClass ))
38+ tp
39+ else pre match {
40+ case pre : SuperType => toPrefix(pre.thistpe, cls, thiscls)
41+ case _ =>
42+ if (thiscls.derivesFrom(cls) && pre.baseTypeRef(thiscls).exists)
43+ if (variance <= 0 && ! isLegalPrefix(pre)) range(pre.bottomType, pre)
44+ else pre
45+ else if ((pre.termSymbol is Package ) && ! (thiscls is Package ))
46+ toPrefix(pre.select(nme.PACKAGE ), cls, thiscls)
47+ else
48+ toPrefix(pre.baseTypeRef(cls).normalizedPrefix, cls.owner, thiscls)
49+ }
8350 }
84- }
8551
86- /* >|>*/ ctx.conditionalTraceIndented(TypeOps .track, s " asSeen ${tp.show} from ( ${pre.show}, ${cls.show}) " , show = true ) /* <|<*/ { // !!! DEBUG
87- tp match {
88- case tp : NamedType =>
89- val sym = tp.symbol
90- if (sym.isStatic) tp
91- else {
92- val pre1 = asSeenFrom(tp.prefix, pre, cls, theMap)
93- if (pre1.isUnsafeNonvariant) {
94- val safeCtx = ctx.withProperty(TypeOps .findMemberLimit, Some (()))
95- pre1.member(tp.name)(safeCtx).info match {
96- case TypeAlias (alias) =>
97- // try to follow aliases of this will avoid skolemization.
98- return alias
99- case _ =>
100- }
52+ /* >|>*/ ctx.conditionalTraceIndented(TypeOps .track, s " asSeen ${tp.show} from ( ${pre.show}, ${cls.show}) " , show = true ) /* <|<*/ { // !!! DEBUG
53+ tp match {
54+ case tp : NamedType => // inlined for performance; TODO: factor out into inline method
55+ if (tp.symbol.isStatic) tp
56+ else {
57+ val saved = variance
58+ variance = variance max 0
59+ val prefix1 = this (tp.prefix)
60+ variance = saved
61+ derivedSelect(tp, prefix1)
10162 }
102- tp.derivedSelect(pre1)
103- }
104- case tp : ThisType =>
105- toPrefix(pre, cls, tp.cls)
106- case _ : BoundType | NoPrefix =>
107- tp
108- case tp : RefinedType =>
109- tp.derivedRefinedType(
110- asSeenFrom(tp.parent, pre, cls, theMap),
111- tp.refinedName,
112- asSeenFrom(tp.refinedInfo, pre, cls, theMap))
113- case tp : TypeAlias if tp.variance == 1 => // if variance != 1, need to do the variance calculation
114- tp.derivedTypeAlias(asSeenFrom(tp.alias, pre, cls, theMap))
115- case _ =>
116- (if (theMap != null ) theMap else new AsSeenFromMap (pre, cls))
117- .mapOver(tp)
63+ case tp : ThisType =>
64+ toPrefix(pre, cls, tp.cls)
65+ case _ : BoundType | NoPrefix =>
66+ tp
67+ case tp : RefinedType =>
68+ derivedRefinedType(tp, apply(tp.parent), apply(tp.refinedInfo))
69+ case tp : TypeAlias if tp.variance == 1 => // if variance != 1, need to do the variance calculation
70+ derivedTypeAlias(tp, apply(tp.alias))
71+ case _ =>
72+ mapOver(tp)
73+ }
11874 }
11975 }
76+
77+ override def reapply (tp : Type ) =
78+ // derives infos have already been subjected to asSeenFrom, hence to need to apply the map again.
79+ tp
12080 }
12181
12282 private def isLegalPrefix (pre : Type )(implicit ctx : Context ) =
12383 pre.isStable || ! ctx.phase.isTyper
12484
125- /** The TypeMap handling the asSeenFrom in more complicated cases */
126- class AsSeenFromMap (pre : Type , cls : Symbol ) extends TypeMap {
127- def apply (tp : Type ) = asSeenFrom(tp, pre, cls, this )
128-
129- /** A method to export the current variance of the map */
130- def currentVariance = variance
131- }
132-
13385 /** Approximate a type `tp` with a type that does not contain skolem types. */
13486 object deskolemize extends ApproximatingTypeMap {
135- private var seen : Set [SkolemType ] = Set ()
136- def apply (tp : Type ) = tp match {
137- case tp : SkolemType =>
138- if (seen contains tp) NoType
139- else {
140- val saved = seen
141- seen += tp
142- try approx(hi = tp.info)
143- finally seen = saved
144- }
145- case _ =>
146- mapOver(tp)
87+ def apply (tp : Type ) = /* ctx.traceIndented(i"deskolemize($tp) at $variance", show = true)*/ {
88+ tp match {
89+ case tp : SkolemType => range(tp.bottomType, atVariance(1 )(apply(tp.info)))
90+ case _ => mapOver(tp)
91+ }
14792 }
14893 }
14994
0 commit comments