@@ -22,13 +22,16 @@ import typer.Inferencing.isFullyDefined
2222import typer .IfBottom
2323
2424import scala .annotation .internal .sharable
25+ import scala .annotation .threadUnsafe
2526
26- trait TypeOps { thisCtx : Context => // TODO: Make standalone object.
27+ object TypeOps :
28+
29+ @ sharable var track : Boolean = false // for debugging
2730
2831 /** The type `tp` as seen from prefix `pre` and owner `cls`. See the spec
2932 * for what this means.
3033 */
31- final def asSeenFrom (tp : Type , pre : Type , cls : Symbol ): Type = {
34+ final def asSeenFrom (tp : Type , pre : Type , cls : Symbol )( using Context ) : Type = {
3235 pre match {
3336 case pre : QualSkolemType =>
3437 // When a selection has an unstable qualifier, the qualifier type gets
@@ -53,7 +56,7 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
5356 }
5457
5558 /** 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 {
5760 /** Set to true when the result of `apply` was approximated to avoid an unstable prefix. */
5861 var approximated : Boolean = false
5962
@@ -62,7 +65,7 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
6265 /** Map a `C.this` type to the right prefix. If the prefix is unstable, and
6366 * the current variance is <= 0, return a range.
6467 */
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 ) /* <|<*/ {
6669 if ((pre eq NoType ) || (pre eq NoPrefix ) || (cls is PackageClass ))
6770 tp
6871 else pre match {
@@ -89,7 +92,7 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
8992 }
9093 }
9194
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
9396 // All cases except for ThisType are the same as in Map. Inlined for performance
9497 // TODO: generalize the inlining trick?
9598 tp match {
@@ -117,11 +120,11 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
117120 }
118121 }
119122
120- def isLegalPrefix (pre : Type )(implicit ctx : Context ): Boolean =
123+ def isLegalPrefix (pre : Type )(using Context ): Boolean =
121124 pre.isStable || ! ctx.phase.isTyper
122125
123126 /** Implementation of Types#simplified */
124- final def simplify (tp : Type , theMap : SimplifyMap ): Type = {
127+ def simplify (tp : Type , theMap : SimplifyMap )( using Context ): Type = {
125128 def mapOver = (if (theMap != null ) theMap else new SimplifyMap ).mapOver(tp)
126129 tp match {
127130 case tp : NamedType =>
@@ -135,20 +138,20 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
135138 }
136139 case tp : TypeParamRef =>
137140 if (tp.paramName.is(DepParamName )) {
138- val bounds = thisCtx .typeComparer.bounds(tp)
141+ val bounds = ctx .typeComparer.bounds(tp)
139142 if (bounds.lo.isRef(defn.NothingClass )) bounds.hi else bounds.lo
140143 }
141144 else {
142- val tvar = typerState.constraint.typeVarOfParam(tp)
145+ val tvar = ctx. typerState.constraint.typeVarOfParam(tp)
143146 if (tvar.exists) tvar else tp
144147 }
145148 case _ : ThisType | _ : BoundType =>
146149 tp
147150 case tp : AliasingBounds =>
148151 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 ) =>
150153 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 ) =>
152155 simplify(l, theMap) | simplify(r, theMap)
153156 case _ : AppliedType | _ : MatchType =>
154157 val normed = tp.tryNormalize
@@ -158,7 +161,7 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
158161 }
159162 }
160163
161- class SimplifyMap extends TypeMap {
164+ class SimplifyMap ( using Context ) extends TypeMap {
162165 def apply (tp : Type ): Type = simplify(tp, this )
163166 }
164167
@@ -178,7 +181,7 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
178181 * in a "best effort", ad-hoc way by selectively widening types in `T1, ..., Tn`
179182 * and stopping if the resulting union simplifies to a type that is not a disjunction.
180183 */
181- def orDominator (tp : Type ): Type = {
184+ def orDominator (tp : Type )( using Context ) : Type = {
182185
183186 /** a faster version of cs1 intersect cs2 */
184187 def intersect (cs1 : List [ClassSymbol ], cs2 : List [ClassSymbol ]): List [ClassSymbol ] = {
@@ -193,7 +196,7 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
193196 val accu1 = if (accu exists (_ derivesFrom c)) accu else c :: accu
194197 if (cs == c.baseClasses) accu1 else dominators(rest, accu1)
195198 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)
197200 defn.ObjectClass :: Nil
198201 }
199202
@@ -217,7 +220,7 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
217220 case AppliedType (tycon2, args2) =>
218221 tp1.derivedAppliedType(
219222 mergeRefinedOrApplied(tycon1, tycon2),
220- thisCtx .typeComparer.lubArgs(args1, args2, tycon1.typeParams))
223+ ctx .typeComparer.lubArgs(args1, args2, tycon1.typeParams))
221224 case _ => fallback
222225 }
223226 case tp1 @ TypeRef (pre1, _) =>
@@ -327,12 +330,128 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
327330 }
328331 }
329332
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+
330449 /** If `tpe` is of the form `p.x` where `p` refers to a package
331450 * but `x` is not owned by a package, expand it to
332451 *
333452 * p.package.x
334453 */
335- def makePackageObjPrefixExplicit (tpe : NamedType ): Type = {
454+ def makePackageObjPrefixExplicit (tpe : NamedType )( using Context ) : Type = {
336455 def tryInsert (pkgClass : SymDenotation ): Type = pkgClass match {
337456 case pkg : PackageClassDenotation =>
338457 val pobj = pkg.packageObjFor(tpe.symbol)
@@ -370,9 +489,12 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
370489 * In fact the current treatment for this sitiuation can so far only be classified as "not obviously wrong",
371490 * (maybe it still needs to be revised).
372491 */
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 ] = {
376498 val argTypes = args.tpes
377499
378500 /** Replace all wildcards in `tps` with `<app>#<tparam>` where `<tparam>` is the
@@ -465,9 +587,6 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
465587 violations.toList
466588 }
467589
468- /** Are we in an inline method body? */
469- def inInlineMethod : Boolean = owner.ownersIterator.exists(_.isInlineMethod)
470-
471590 /** Refine child based on parent
472591 *
473592 * In child class definition, we have:
@@ -487,7 +606,7 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
487606 * If the subtyping is true, the instantiated type `p.child[Vs]` is
488607 * returned. Otherwise, `NoType` is returned.
489608 */
490- def refineUsingParent (parent : Type , child : Symbol )(implicit ctx : Context ): Type = {
609+ def refineUsingParent (parent : Type , child : Symbol )(using Context ): Type = {
491610 if (child.isTerm && child.is(Case , butNot = Module )) return child.termRef // enum vals always match
492611
493612 // <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.
503622
504623 val childTp = if (child.isTerm) child.termRef else child.typeRef
505624
506- instantiateToSubType(childTp, parent)(ctx.fresh.setNewTyperState()).dealias
625+ instantiateToSubType(childTp, parent)(using ctx.fresh.setNewTyperState())
626+ .dealias
507627 }
508628
509629 /** Instantiate type `tp1` to be a subtype of `tp2`
@@ -513,7 +633,7 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
513633 *
514634 * Otherwise, return NoType.
515635 */
516- private def instantiateToSubType (tp1 : NamedType , tp2 : Type )(implicit ctx : Context ): Type = {
636+ private def instantiateToSubType (tp1 : NamedType , tp2 : Type )(using Context ): Type = {
517637 /** expose abstract type references to their bounds or tvars according to variance */
518638 class AbstractTypeMap (maximize : Boolean )(implicit ctx : Context ) extends TypeMap {
519639 def expose (lo : Type , hi : Type ): Type =
@@ -594,8 +714,9 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
594714 // we manually patch subtyping check instead of changing TypeComparer.
595715 // See tests/patmat/i3645b.scala
596716 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+ }
599720 }
600721
601722 if (protoTp1 <:< tp2)
@@ -612,13 +733,8 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
612733 }
613734 }
614735 }
615- }
616736
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 =
623738 ts.foldRight(defn.UnitType : Type )(defn.PairClass .typeRef.appliedTo(_, _))
624- }
739+
740+ end TypeOps
0 commit comments