@@ -9,7 +9,8 @@ import ProtoTypes.*
9
9
import NameKinds .UniqueName
10
10
import util .Spans .*
11
11
import util .{Stats , SimpleIdentityMap , SimpleIdentitySet , SrcPos }
12
- import Decorators .*
12
+ import transform .TypeUtils .isTransparent
13
+ import Decorators ._
13
14
import config .Printers .{gadts , typr }
14
15
import annotation .tailrec
15
16
import reporting .*
@@ -60,7 +61,9 @@ object Inferencing {
60
61
def instantiateSelected (tp : Type , tvars : List [Type ])(using Context ): Unit =
61
62
if (tvars.nonEmpty)
62
63
IsFullyDefinedAccumulator (
63
- ForceDegree .Value (tvars.contains, IfBottom .flip), minimizeSelected = true
64
+ new ForceDegree .Value (IfBottom .flip):
65
+ override def appliesTo (tvar : TypeVar ) = tvars.contains(tvar),
66
+ minimizeSelected = true
64
67
).process(tp)
65
68
66
69
/** Instantiate any type variables in `tp` whose bounds contain a reference to
@@ -154,15 +157,66 @@ object Inferencing {
154
157
* their lower bound. Record whether successful.
155
158
* 2nd Phase: If first phase was successful, instantiate all remaining type variables
156
159
* to their upper bound.
160
+ *
161
+ * Instance types can be improved by replacing covariant occurrences of Nothing
162
+ * with fresh type variables, if `force` allows this in its `canImprove` implementation.
157
163
*/
158
164
private class IsFullyDefinedAccumulator (force : ForceDegree .Value , minimizeSelected : Boolean = false )
159
165
(using Context ) extends TypeAccumulator [Boolean ] {
160
166
161
- private def instantiate (tvar : TypeVar , fromBelow : Boolean ): Type = {
167
+ /** Replace toplevel-covariant occurrences (i.e. covariant without double flips)
168
+ * of Nothing by fresh type variables. Double-flips are not covered to be
169
+ * conservative and save a bit of time on traversals; we could probably
170
+ * generalize that if we see use cases.
171
+ * For singleton types and references to module classes: try to
172
+ * improve the widened type. For module classes, the widened type
173
+ * is the intersection of all its non-transparent parent types.
174
+ */
175
+ private def improve (tvar : TypeVar ) = new TypeMap :
176
+ def apply (t : Type ) = trace(i " improve $t" , show = true ):
177
+ def tryWidened (widened : Type ): Type =
178
+ val improved = apply(widened)
179
+ if improved ne widened then improved else mapOver(t)
180
+ if variance > 0 then
181
+ t match
182
+ case t : TypeRef =>
183
+ if t.symbol == defn.NothingClass then
184
+ newTypeVar(TypeBounds .empty, nestingLevel = tvar.nestingLevel)
185
+ else if t.symbol.is(ModuleClass ) then
186
+ tryWidened(t.parents.filter(! _.isTransparent())
187
+ .foldLeft(defn.AnyType : Type )(TypeComparer .andType(_, _)))
188
+ else
189
+ mapOver(t)
190
+ case t : TermRef =>
191
+ tryWidened(t.widen)
192
+ case _ =>
193
+ mapOver(t)
194
+ else t
195
+
196
+ // Don't map Nothing arguments for higher-kinded types; we'd get the wrong kind */
197
+ override def mapArg (arg : Type , tparam : ParamInfo ): Type =
198
+ if tparam.paramInfo.isLambdaSub then arg
199
+ else super .mapArg(arg, tparam)
200
+ end improve
201
+
202
+ /** Instantiate type variable with possibly improved computed instance type.
203
+ * @return true if variable was instantiated with improved type, which
204
+ * in this case should not be instantiated further, false otherwise.
205
+ */
206
+ private def instantiate (tvar : TypeVar , fromBelow : Boolean ): Boolean =
207
+ if fromBelow && force.canImprove(tvar) then
208
+ val inst = tvar.typeToInstantiateWith(fromBelow = true )
209
+ if apply(true , inst) then
210
+ // need to recursively check before improving, since improving adds type vars
211
+ // which should not be instantiated at this point
212
+ val better = improve(tvar)(inst)
213
+ if better <:< TypeComparer .fullUpperBound(tvar.origin) then
214
+ typr.println(i " forced instantiation of invariant ${tvar.origin} = $inst, improved to $better" )
215
+ tvar.instantiateWith(better)
216
+ return true
162
217
val inst = tvar.instantiate(fromBelow)
163
218
typr.println(i " forced instantiation of ${tvar.origin} = $inst" )
164
- inst
165
- }
219
+ false
166
220
167
221
private var toMaximize : List [TypeVar ] = Nil
168
222
@@ -178,26 +232,27 @@ object Inferencing {
178
232
&& ctx.typerState.constraint.contains(tvar)
179
233
&& {
180
234
var fail = false
235
+ var skip = false
181
236
val direction = instDirection(tvar.origin)
182
237
if minimizeSelected then
183
238
if direction <= 0 && tvar.hasLowerBound then
184
- instantiate(tvar, fromBelow = true )
239
+ skip = instantiate(tvar, fromBelow = true )
185
240
else if direction >= 0 && tvar.hasUpperBound then
186
- instantiate(tvar, fromBelow = false )
241
+ skip = instantiate(tvar, fromBelow = false )
187
242
// else hold off instantiating unbounded unconstrained variable
188
243
else if direction != 0 then
189
- instantiate(tvar, fromBelow = direction < 0 )
244
+ skip = instantiate(tvar, fromBelow = direction < 0 )
190
245
else if variance >= 0 && tvar.hasLowerBound then
191
- instantiate(tvar, fromBelow = true )
246
+ skip = instantiate(tvar, fromBelow = true )
192
247
else if (variance > 0 || variance == 0 && ! tvar.hasUpperBound)
193
248
&& force.ifBottom == IfBottom .ok
194
249
then // if variance == 0, prefer upper bound if one is given
195
- instantiate(tvar, fromBelow = true )
250
+ skip = instantiate(tvar, fromBelow = true )
196
251
else if variance >= 0 && force.ifBottom == IfBottom .fail then
197
252
fail = true
198
253
else
199
254
toMaximize = tvar :: toMaximize
200
- ! fail && foldOver(x, tvar)
255
+ ! fail && (skip || foldOver(x, tvar) )
201
256
}
202
257
case tp => foldOver(x, tp)
203
258
}
@@ -467,7 +522,7 @@ object Inferencing {
467
522
*
468
523
* we want to instantiate U to x.type right away. No need to wait further.
469
524
*/
470
- private def variances (tp : Type , pt : Type = WildcardType )(using Context ): VarianceMap [TypeVar ] = {
525
+ def variances (tp : Type , pt : Type = WildcardType )(using Context ): VarianceMap [TypeVar ] = {
471
526
Stats .record(" variances" )
472
527
val constraint = ctx.typerState.constraint
473
528
@@ -769,14 +824,30 @@ trait Inferencing { this: Typer =>
769
824
}
770
825
771
826
/** An enumeration controlling the degree of forcing in "is-fully-defined" checks. */
772
- @ sharable object ForceDegree {
773
- class Value (val appliesTo : TypeVar => Boolean , val ifBottom : IfBottom ):
774
- override def toString = s " ForceDegree.Value(.., $ifBottom) "
775
- val none : Value = new Value (_ => false , IfBottom .ok) { override def toString = " ForceDegree.none" }
776
- val all : Value = new Value (_ => true , IfBottom .ok) { override def toString = " ForceDegree.all" }
777
- val failBottom : Value = new Value (_ => true , IfBottom .fail) { override def toString = " ForceDegree.failBottom" }
778
- val flipBottom : Value = new Value (_ => true , IfBottom .flip) { override def toString = " ForceDegree.flipBottom" }
779
- }
827
+ @ sharable object ForceDegree :
828
+ class Value (val ifBottom : IfBottom ):
829
+
830
+ /** Does `tv` need to be instantiated? */
831
+ def appliesTo (tv : TypeVar ): Boolean = true
832
+
833
+ /** Should we try to improve the computed instance type by replacing bottom types
834
+ * with fresh type variables?
835
+ */
836
+ def canImprove (tv : TypeVar ): Boolean = false
837
+
838
+ override def toString = s " ForceDegree.Value( $ifBottom) "
839
+ end Value
840
+
841
+ val none : Value = new Value (IfBottom .ok):
842
+ override def appliesTo (tv : TypeVar ) = false
843
+ override def toString = " ForceDegree.none"
844
+ val all : Value = new Value (IfBottom .ok):
845
+ override def toString = " ForceDegree.all"
846
+ val failBottom : Value = new Value (IfBottom .fail):
847
+ override def toString = " ForceDegree.failBottom"
848
+ val flipBottom : Value = new Value (IfBottom .flip):
849
+ override def toString = " ForceDegree.flipBottom"
850
+ end ForceDegree
780
851
781
852
enum IfBottom :
782
853
case ok, fail, flip
0 commit comments