@@ -22,13 +22,16 @@ import typer.Inferencing.isFullyDefined
22
22
import typer .IfBottom
23
23
24
24
import scala .annotation .internal .sharable
25
+ import scala .annotation .threadUnsafe
25
26
26
- trait TypeOps { thisCtx : Context => // TODO: Make standalone object.
27
+ object TypeOps :
28
+
29
+ @ sharable var track : Boolean = false // for debugging
27
30
28
31
/** The type `tp` as seen from prefix `pre` and owner `cls`. See the spec
29
32
* for what this means.
30
33
*/
31
- final def asSeenFrom (tp : Type , pre : Type , cls : Symbol ): Type = {
34
+ final def asSeenFrom (tp : Type , pre : Type , cls : Symbol )( using Context ) : Type = {
32
35
pre match {
33
36
case pre : QualSkolemType =>
34
37
// When a selection has an unstable qualifier, the qualifier type gets
@@ -53,7 +56,7 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
53
56
}
54
57
55
58
/** The TypeMap handling the asSeenFrom */
56
- class AsSeenFromMap (pre : Type , cls : Symbol ) extends ApproximatingTypeMap {
59
+ class AsSeenFromMap (pre : Type , cls : Symbol )( using Context ) extends ApproximatingTypeMap {
57
60
/** Set to true when the result of `apply` was approximated to avoid an unstable prefix. */
58
61
var approximated : Boolean = false
59
62
@@ -62,7 +65,7 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
62
65
/** Map a `C.this` type to the right prefix. If the prefix is unstable, and
63
66
* the current variance is <= 0, return a range.
64
67
*/
65
- def toPrefix (pre : Type , cls : Symbol , thiscls : ClassSymbol ): Type = /* >|>*/ trace.conditionally(TypeOps . track, s " toPrefix( $pre, $cls, $thiscls) " , show = true ) /* <|<*/ {
68
+ def toPrefix (pre : Type , cls : Symbol , thiscls : ClassSymbol ): Type = /* >|>*/ trace.conditionally(track, s " toPrefix( $pre, $cls, $thiscls) " , show = true ) /* <|<*/ {
66
69
if ((pre eq NoType ) || (pre eq NoPrefix ) || (cls is PackageClass ))
67
70
tp
68
71
else pre match {
@@ -89,7 +92,7 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
89
92
}
90
93
}
91
94
92
- trace.conditionally(TypeOps . track, s " asSeen ${tp.show} from ( ${pre.show}, ${cls.show}) " , show = true ) { // !!! DEBUG
95
+ trace.conditionally(track, s " asSeen ${tp.show} from ( ${pre.show}, ${cls.show}) " , show = true ) { // !!! DEBUG
93
96
// All cases except for ThisType are the same as in Map. Inlined for performance
94
97
// TODO: generalize the inlining trick?
95
98
tp match {
@@ -117,11 +120,11 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
117
120
}
118
121
}
119
122
120
- def isLegalPrefix (pre : Type )(implicit ctx : Context ): Boolean =
123
+ def isLegalPrefix (pre : Type )(using Context ): Boolean =
121
124
pre.isStable || ! ctx.phase.isTyper
122
125
123
126
/** Implementation of Types#simplified */
124
- final def simplify (tp : Type , theMap : SimplifyMap ): Type = {
127
+ def simplify (tp : Type , theMap : SimplifyMap )( using Context ): Type = {
125
128
def mapOver = (if (theMap != null ) theMap else new SimplifyMap ).mapOver(tp)
126
129
tp match {
127
130
case tp : NamedType =>
@@ -135,20 +138,20 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
135
138
}
136
139
case tp : TypeParamRef =>
137
140
if (tp.paramName.is(DepParamName )) {
138
- val bounds = thisCtx .typeComparer.bounds(tp)
141
+ val bounds = ctx .typeComparer.bounds(tp)
139
142
if (bounds.lo.isRef(defn.NothingClass )) bounds.hi else bounds.lo
140
143
}
141
144
else {
142
- val tvar = typerState.constraint.typeVarOfParam(tp)
145
+ val tvar = ctx. typerState.constraint.typeVarOfParam(tp)
143
146
if (tvar.exists) tvar else tp
144
147
}
145
148
case _ : ThisType | _ : BoundType =>
146
149
tp
147
150
case tp : AliasingBounds =>
148
151
tp.derivedAlias(simplify(tp.alias, theMap))
149
- case AndType (l, r) if ! thisCtx .mode.is(Mode .Type ) =>
152
+ case AndType (l, r) if ! ctx .mode.is(Mode .Type ) =>
150
153
simplify(l, theMap) & simplify(r, theMap)
151
- case OrType (l, r) if ! thisCtx .mode.is(Mode .Type ) =>
154
+ case OrType (l, r) if ! ctx .mode.is(Mode .Type ) =>
152
155
simplify(l, theMap) | simplify(r, theMap)
153
156
case _ : AppliedType | _ : MatchType =>
154
157
val normed = tp.tryNormalize
@@ -158,7 +161,7 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
158
161
}
159
162
}
160
163
161
- class SimplifyMap extends TypeMap {
164
+ class SimplifyMap ( using Context ) extends TypeMap {
162
165
def apply (tp : Type ): Type = simplify(tp, this )
163
166
}
164
167
@@ -178,7 +181,7 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
178
181
* in a "best effort", ad-hoc way by selectively widening types in `T1, ..., Tn`
179
182
* and stopping if the resulting union simplifies to a type that is not a disjunction.
180
183
*/
181
- def orDominator (tp : Type ): Type = {
184
+ def orDominator (tp : Type )( using Context ) : Type = {
182
185
183
186
/** a faster version of cs1 intersect cs2 */
184
187
def intersect (cs1 : List [ClassSymbol ], cs2 : List [ClassSymbol ]): List [ClassSymbol ] = {
@@ -193,7 +196,7 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
193
196
val accu1 = if (accu exists (_ derivesFrom c)) accu else c :: accu
194
197
if (cs == c.baseClasses) accu1 else dominators(rest, accu1)
195
198
case Nil => // this case can happen because after erasure we do not have a top class anymore
196
- assert(thisCtx .erasedTypes || thisCtx .reporter.errorsReported)
199
+ assert(ctx .erasedTypes || ctx .reporter.errorsReported)
197
200
defn.ObjectClass :: Nil
198
201
}
199
202
@@ -217,7 +220,7 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
217
220
case AppliedType (tycon2, args2) =>
218
221
tp1.derivedAppliedType(
219
222
mergeRefinedOrApplied(tycon1, tycon2),
220
- thisCtx .typeComparer.lubArgs(args1, args2, tycon1.typeParams))
223
+ ctx .typeComparer.lubArgs(args1, args2, tycon1.typeParams))
221
224
case _ => fallback
222
225
}
223
226
case tp1 @ TypeRef (pre1, _) =>
@@ -327,12 +330,128 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
327
330
}
328
331
}
329
332
333
+ /** An abstraction of a class info, consisting of
334
+ * - the intersection of its parents,
335
+ * - refined by all non-private fields, methods, and type members,
336
+ * - abstracted over all type parameters (into a type lambda)
337
+ * - where all references to `this` of the class are closed over in a RecType.
338
+ */
339
+ def classBound (info : ClassInfo )(using Context ): Type = {
340
+ val cls = info.cls
341
+ val parentType = info.parents.reduceLeft(ctx.typeComparer.andType(_, _))
342
+
343
+ def addRefinement (parent : Type , decl : Symbol ) = {
344
+ val inherited =
345
+ parentType.findMember(decl.name, cls.thisType,
346
+ required = EmptyFlags , excluded = Private
347
+ ).suchThat(decl.matches(_))
348
+ val inheritedInfo = inherited.info
349
+ val isPolyFunctionApply = decl.name == nme.apply && (parent <:< defn.PolyFunctionType )
350
+ if isPolyFunctionApply
351
+ || inheritedInfo.exists
352
+ && ! decl.isClass
353
+ && decl.info.widenExpr <:< inheritedInfo.widenExpr
354
+ && ! (inheritedInfo.widenExpr <:< decl.info.widenExpr)
355
+ then
356
+ val r = RefinedType (parent, decl.name, decl.info)
357
+ typr.println(i " add ref $parent $decl --> " + r)
358
+ r
359
+ else
360
+ parent
361
+ }
362
+
363
+ def close (tp : Type ) = RecType .closeOver { rt =>
364
+ tp.subst(cls :: Nil , rt.recThis :: Nil ).substThis(cls, rt.recThis)
365
+ }
366
+
367
+ def isRefinable (sym : Symbol ) = ! sym.is(Private ) && ! sym.isConstructor
368
+ val refinableDecls = info.decls.filter(isRefinable)
369
+ val raw = refinableDecls.foldLeft(parentType)(addRefinement)
370
+ HKTypeLambda .fromParams(cls.typeParams, raw) match {
371
+ case tl : HKTypeLambda => tl.derivedLambdaType(resType = close(tl.resType))
372
+ case tp => close(tp)
373
+ }
374
+ }
375
+
376
+ /** An upper approximation of the given type `tp` that does not refer to any symbol in `symsToAvoid`.
377
+ * We need to approximate with ranges:
378
+ *
379
+ * term references to symbols in `symsToAvoid`,
380
+ * term references that have a widened type of which some part refers
381
+ * to a symbol in `symsToAvoid`,
382
+ * type references to symbols in `symsToAvoid`,
383
+ * this types of classes in `symsToAvoid`.
384
+ *
385
+ * Type variables that would be interpolated to a type that
386
+ * needs to be widened are replaced by the widened interpolation instance.
387
+ */
388
+ def avoid (tp : Type , symsToAvoid : => List [Symbol ])(using Context ): Type = {
389
+ val widenMap = new ApproximatingTypeMap {
390
+ @ threadUnsafe lazy val forbidden = symsToAvoid.toSet
391
+ def toAvoid (sym : Symbol ) = ! sym.isStatic && forbidden.contains(sym)
392
+ def partsToAvoid = new NamedPartsAccumulator (tp => toAvoid(tp.symbol))
393
+ def apply (tp : Type ): Type = tp match {
394
+ case tp : TermRef
395
+ if toAvoid(tp.symbol) || partsToAvoid(mutable.Set .empty, tp.info).nonEmpty =>
396
+ tp.info.widenExpr.dealias match {
397
+ case info : SingletonType => apply(info)
398
+ case info => range(defn.NothingType , apply(info))
399
+ }
400
+ case tp : TypeRef if toAvoid(tp.symbol) =>
401
+ tp.info match {
402
+ case info : AliasingBounds =>
403
+ apply(info.alias)
404
+ case TypeBounds (lo, hi) =>
405
+ range(atVariance(- variance)(apply(lo)), apply(hi))
406
+ case info : ClassInfo =>
407
+ range(defn.NothingType , apply(classBound(info)))
408
+ case _ =>
409
+ emptyRange // should happen only in error cases
410
+ }
411
+ case tp : ThisType if toAvoid(tp.cls) =>
412
+ range(defn.NothingType , apply(classBound(tp.cls.classInfo)))
413
+ case tp : SkolemType if partsToAvoid(mutable.Set .empty, tp.info).nonEmpty =>
414
+ range(defn.NothingType , apply(tp.info))
415
+ case tp : TypeVar if mapCtx.typerState.constraint.contains(tp) =>
416
+ val lo = mapCtx.typeComparer.instanceType(
417
+ tp.origin, fromBelow = variance > 0 || variance == 0 && tp.hasLowerBound)
418
+ val lo1 = apply(lo)
419
+ if (lo1 ne lo) lo1 else tp
420
+ case _ =>
421
+ mapOver(tp)
422
+ }
423
+
424
+ /** Three deviations from standard derivedSelect:
425
+ * 1. We first try a widening conversion to the type's info with
426
+ * the original prefix. Since the original prefix is known to
427
+ * be a subtype of the returned prefix, this can improve results.
428
+ * 2. Then, if the approximation result is a singleton reference C#x.type, we
429
+ * replace by the widened type, which is usually more natural.
430
+ * 3. Finally, we need to handle the case where the prefix type does not have a member
431
+ * named `tp.name` anymmore. In that case, we need to fall back to Bot..Top.
432
+ */
433
+ override def derivedSelect (tp : NamedType , pre : Type ) =
434
+ if (pre eq tp.prefix)
435
+ tp
436
+ else tryWiden(tp, tp.prefix).orElse {
437
+ if (tp.isTerm && variance > 0 && ! pre.isSingleton)
438
+ apply(tp.info.widenExpr)
439
+ else if (upper(pre).member(tp.name).exists)
440
+ super .derivedSelect(tp, pre)
441
+ else
442
+ range(defn.NothingType , defn.AnyType )
443
+ }
444
+ }
445
+
446
+ widenMap(tp)
447
+ }
448
+
330
449
/** If `tpe` is of the form `p.x` where `p` refers to a package
331
450
* but `x` is not owned by a package, expand it to
332
451
*
333
452
* p.package.x
334
453
*/
335
- def makePackageObjPrefixExplicit (tpe : NamedType ): Type = {
454
+ def makePackageObjPrefixExplicit (tpe : NamedType )( using Context ) : Type = {
336
455
def tryInsert (pkgClass : SymDenotation ): Type = pkgClass match {
337
456
case pkg : PackageClassDenotation =>
338
457
val pobj = pkg.packageObjFor(tpe.symbol)
@@ -370,9 +489,12 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
370
489
* In fact the current treatment for this sitiuation can so far only be classified as "not obviously wrong",
371
490
* (maybe it still needs to be revised).
372
491
*/
373
- def boundsViolations (args : List [Tree ], boundss : List [TypeBounds ],
374
- instantiate : (Type , List [Type ]) => Type , app : Type )(
375
- implicit ctx : Context ): List [BoundsViolation ] = {
492
+ def boundsViolations (
493
+ args : List [Tree ],
494
+ boundss : List [TypeBounds ],
495
+ instantiate : (Type , List [Type ]) => Type ,
496
+ app : Type )(
497
+ using Context ): List [BoundsViolation ] = {
376
498
val argTypes = args.tpes
377
499
378
500
/** Replace all wildcards in `tps` with `<app>#<tparam>` where `<tparam>` is the
@@ -465,9 +587,6 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
465
587
violations.toList
466
588
}
467
589
468
- /** Are we in an inline method body? */
469
- def inInlineMethod : Boolean = owner.ownersIterator.exists(_.isInlineMethod)
470
-
471
590
/** Refine child based on parent
472
591
*
473
592
* In child class definition, we have:
@@ -487,7 +606,7 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
487
606
* If the subtyping is true, the instantiated type `p.child[Vs]` is
488
607
* returned. Otherwise, `NoType` is returned.
489
608
*/
490
- def refineUsingParent (parent : Type , child : Symbol )(implicit ctx : Context ): Type = {
609
+ def refineUsingParent (parent : Type , child : Symbol )(using Context ): Type = {
491
610
if (child.isTerm && child.is(Case , butNot = Module )) return child.termRef // enum vals always match
492
611
493
612
// <local child> is a place holder from Scalac, it is hopeless to instantiate it.
@@ -503,7 +622,8 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
503
622
504
623
val childTp = if (child.isTerm) child.termRef else child.typeRef
505
624
506
- instantiateToSubType(childTp, parent)(ctx.fresh.setNewTyperState()).dealias
625
+ instantiateToSubType(childTp, parent)(using ctx.fresh.setNewTyperState())
626
+ .dealias
507
627
}
508
628
509
629
/** Instantiate type `tp1` to be a subtype of `tp2`
@@ -513,7 +633,7 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
513
633
*
514
634
* Otherwise, return NoType.
515
635
*/
516
- private def instantiateToSubType (tp1 : NamedType , tp2 : Type )(implicit ctx : Context ): Type = {
636
+ private def instantiateToSubType (tp1 : NamedType , tp2 : Type )(using Context ): Type = {
517
637
/** expose abstract type references to their bounds or tvars according to variance */
518
638
class AbstractTypeMap (maximize : Boolean )(implicit ctx : Context ) extends TypeMap {
519
639
def expose (lo : Type , hi : Type ): Type =
@@ -594,8 +714,9 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
594
714
// we manually patch subtyping check instead of changing TypeComparer.
595
715
// See tests/patmat/i3645b.scala
596
716
def parentQualify = tp1.widen.classSymbol.info.parents.exists { parent =>
597
- implicit val ictx = ctx.fresh.setNewTyperState()
598
- parent.argInfos.nonEmpty && minTypeMap.apply(parent) <:< maxTypeMap.apply(tp2)
717
+ inContext(ctx.fresh.setNewTyperState()) {
718
+ parent.argInfos.nonEmpty && minTypeMap.apply(parent) <:< maxTypeMap.apply(tp2)
719
+ }
599
720
}
600
721
601
722
if (protoTp1 <:< tp2)
@@ -612,13 +733,8 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
612
733
}
613
734
}
614
735
}
615
- }
616
736
617
- object TypeOps {
618
- @ sharable var track : Boolean = false // !!!DEBUG
619
-
620
- // TODO: Move other typeops here. It's a bit weird that they are a part of `ctx`
621
-
622
- def nestedPairs (ts : List [Type ])(implicit ctx : Context ): Type =
737
+ def nestedPairs (ts : List [Type ])(using Context ): Type =
623
738
ts.foldRight(defn.UnitType : Type )(defn.PairClass .typeRef.appliedTo(_, _))
624
- }
739
+
740
+ end TypeOps
0 commit comments