Skip to content

Commit a9cbedb

Browse files
committed
Generalize approximating type maps.
Approximating type maps now work with bounds instead of NoTypes. This gives more precision (in particular for type applications), and also makes it possible to combine approximation with other mappings. As a side-effect we provide the hooks for preventing constructing illegal types C#A where C is non-singleton and A is abstract by setting variance to 0 for the prefix of an abstract type selection. There's one test case that fails: One of the types in dependent-exractors.scala does not check out anymore. This has likely to do with the loss of precision incurred by the maps. Exact cause remains to be tracked down.
1 parent d7faafa commit a9cbedb

File tree

4 files changed

+95
-55
lines changed

4 files changed

+95
-55
lines changed

compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -362,13 +362,11 @@ trait ConstraintHandling {
362362
def pruneLambdaParams(tp: Type) =
363363
if (comparedTypeLambdas.nonEmpty) {
364364
val approx = new ApproximatingTypeMap {
365+
if (fromBelow) variance = -1
365366
def apply(t: Type): Type = t match {
366367
case t @ TypeParamRef(tl: TypeLambda, n) if comparedTypeLambdas contains tl =>
367-
val effectiveVariance = if (fromBelow) -variance else variance
368368
val bounds = tl.paramInfos(n)
369-
if (effectiveVariance > 0) bounds.lo
370-
else if (effectiveVariance < 0) bounds.hi
371-
else NoType
369+
range(bounds.lo, bounds.hi)
372370
case _ =>
373371
mapOver(t)
374372
}

compiler/src/dotty/tools/dotc/core/TypeOps.scala

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -132,18 +132,9 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
132132

133133
/** Approximate a type `tp` with a type that does not contain skolem types. */
134134
object deskolemize extends ApproximatingTypeMap {
135-
private var seen: Set[SkolemType] = Set()
136135
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)
136+
case tp: SkolemType => range(hi = apply(tp.info))
137+
case _ => mapOver(tp)
147138
}
148139
}
149140

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 90 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,12 @@ object Types {
136136
case _ => false
137137
}
138138

139+
/** Is this type exactly Nothing (no vars, aliases, refinements etc allowed)? */
140+
def isBottomType(implicit ctx: Context): Boolean = this match {
141+
case tp: TypeRef => tp.symbol eq defn.NothingClass
142+
case _ => false
143+
}
144+
139145
/** Is this type a (neither aliased nor applied) reference to class `sym`? */
140146
def isDirectRef(sym: Symbol)(implicit ctx: Context): Boolean = stripTypeVar match {
141147
case this1: TypeRef =>
@@ -1220,6 +1226,18 @@ object Types {
12201226
case _ => TypeAlias(this)
12211227
}
12221228

1229+
/** The lower bound of a TypeBounds type, the type itself otherwise */
1230+
def loBound = this match {
1231+
case tp: TypeBounds => tp.lo
1232+
case _ => this
1233+
}
1234+
1235+
/** The upper bound of a TypeBounds type, the type itself otherwise */
1236+
def hiBound = this match {
1237+
case tp: TypeBounds => tp.hi
1238+
case _ => this
1239+
}
1240+
12231241
/** The type parameter with given `name`. This tries first `decls`
12241242
* in order not to provoke a cycle by forcing the info. If that yields
12251243
* no symbol it tries `member` as an alternative.
@@ -1766,6 +1784,7 @@ object Types {
17661784
*/
17671785
def derivedSelect(prefix: Type)(implicit ctx: Context): Type =
17681786
if (prefix eq this.prefix) this
1787+
else if (prefix.isBottomType) prefix
17691788
else if (isType) {
17701789
val res = prefix.lookupRefined(name)
17711790
if (res.exists) res
@@ -3713,8 +3732,19 @@ object Types {
37133732
tp match {
37143733
case tp: NamedType =>
37153734
if (stopAtStatic && tp.symbol.isStatic) tp
3716-
else derivedSelect(tp, this(tp.prefix))
3717-
3735+
else {
3736+
val prefix1 = tp.info match {
3737+
case info: TypeBounds if !info.isAlias =>
3738+
// prefix of an abstract type selection is non-variant, since a path
3739+
// cannot be legally widened to its underlying type, or any supertype.
3740+
val saved = variance
3741+
variance = 0
3742+
try this(tp.prefix) finally variance = saved
3743+
case _ =>
3744+
this(tp.prefix)
3745+
}
3746+
derivedSelect(tp, prefix1)
3747+
}
37183748
case _: ThisType
37193749
| _: BoundType
37203750
| NoPrefix => tp
@@ -3842,63 +3872,84 @@ object Types {
38423872
def apply(tp: Type) = tp
38433873
}
38443874

3845-
/** A type map that approximates NoTypes by upper or lower known bounds depending on
3875+
/** A type map that approximates TypeBounds types depending on
38463876
* variance.
38473877
*
38483878
* if variance > 0 : approximate by upper bound
38493879
* variance < 0 : approximate by lower bound
3850-
* variance = 0 : propagate NoType to next outer level
3880+
* variance = 0 : propagate bounds to next outer level
38513881
*/
38523882
abstract class ApproximatingTypeMap(implicit ctx: Context) extends TypeMap { thisMap =>
3853-
def approx(lo: Type = defn.NothingType, hi: Type = defn.AnyType) =
3854-
if (variance == 0) NoType
3855-
else apply(if (variance < 0) lo else hi)
3883+
def range(lo: Type = defn.NothingType, hi: Type = defn.AnyType) =
3884+
if (variance > 0) hi
3885+
else if (variance < 0) lo
3886+
else TypeBounds(lo.loBound, hi.hiBound)
38563887

38573888
override protected def derivedSelect(tp: NamedType, pre: Type) =
38583889
if (pre eq tp.prefix) tp
3859-
else tp.info match {
3860-
case TypeAlias(alias) => apply(alias) // try to heal by following aliases
3861-
case _ =>
3862-
if (pre.exists && !pre.isRef(defn.NothingClass) && variance > 0) tp.derivedSelect(pre)
3863-
else tp.info match {
3864-
case TypeBounds(lo, hi) => approx(lo, hi)
3865-
case _ => approx()
3890+
else pre match {
3891+
case TypeBounds(preLo, preHi) =>
3892+
tp.info match {
3893+
case TypeAlias(alias) => apply(alias)
3894+
case info @ TypeBounds(lo, hi) => apply(range(lo, hi))
3895+
case _ => range(tp.derivedSelect(preLo), tp.derivedSelect(preHi))
38663896
}
3897+
case _ => tp.derivedSelect(pre)
38673898
}
38683899
override protected def derivedRefinedType(tp: RefinedType, parent: Type, info: Type) =
3869-
if (parent.exists && info.exists) tp.derivedRefinedType(parent, tp.refinedName, info)
3870-
else approx(hi = parent)
3900+
parent match {
3901+
case TypeBounds(parentLo, parentHi) =>
3902+
range(derivedRefinedType(tp, parentLo, info), derivedRefinedType(tp, parentHi, info))
3903+
case _ =>
3904+
if (parent.isBottomType) parent
3905+
else info match {
3906+
case TypeBounds(infoLo, infoHi) if tp.refinedName.isTermName =>
3907+
range(derivedRefinedType(tp, parent, infoLo), derivedRefinedType(tp, parent, infoHi))
3908+
case _ =>
3909+
tp.derivedRefinedType(parent, tp.refinedName, info)
3910+
}
3911+
}
38713912
override protected def derivedRecType(tp: RecType, parent: Type) =
3872-
if (parent.exists) tp.rebind(parent)
3873-
else approx()
3913+
parent match {
3914+
case TypeBounds(lo, hi) => range(tp.rebind(lo), tp.rebind(hi))
3915+
case _ => tp.rebind(parent)
3916+
}
38743917
override protected def derivedTypeAlias(tp: TypeAlias, alias: Type) =
3875-
if (alias.exists) tp.derivedTypeAlias(alias)
3876-
else approx(NoType, TypeBounds.empty)
3918+
alias match {
3919+
case bounds: TypeBounds => bounds
3920+
case _ => tp.derivedTypeAlias(alias)
3921+
}
38773922
override protected def derivedTypeBounds(tp: TypeBounds, lo: Type, hi: Type) =
3878-
if (lo.exists && hi.exists) tp.derivedTypeBounds(lo, hi)
3879-
else approx(NoType,
3880-
if (lo.exists) TypeBounds.lower(lo)
3881-
else if (hi.exists) TypeBounds.upper(hi)
3882-
else TypeBounds.empty)
3923+
if ((lo eq tp.lo) && (hi eq tp.hi)) tp else range(lo, hi)
38833924
override protected def derivedSuperType(tp: SuperType, thistp: Type, supertp: Type) =
3884-
if (thistp.exists && supertp.exists) tp.derivedSuperType(thistp, supertp)
3885-
else NoType
3925+
if (thistp.isInstanceOf[TypeBounds] || supertp.isInstanceOf[TypeBounds]) range()
3926+
else tp.derivedSuperType(thistp, supertp)
38863927
override protected def derivedAppliedType(tp: HKApply, tycon: Type, args: List[Type]): Type =
3887-
if (tycon.exists && args.forall(_.exists)) tp.derivedAppliedType(tycon, args)
3888-
else approx() // This is rather coarse, but to do better is a bit complicated
3928+
tycon match {
3929+
case TypeBounds(tyconLo, tyconHi) =>
3930+
range(tp.derivedAppliedType(tyconLo, args), tp.derivedAppliedType(tyconHi, args))
3931+
case _ =>
3932+
tp.derivedAppliedType(tycon, args)
3933+
}
38893934
override protected def derivedAndOrType(tp: AndOrType, tp1: Type, tp2: Type) =
3890-
if (tp1.exists && tp2.exists) tp.derivedAndOrType(tp1, tp2)
3891-
else if (tp.isAnd) approx(hi = tp1 & tp2) // if one of tp1d, tp2d exists, it is the result of tp1d & tp2d
3892-
else approx(lo = tp1 & tp2)
3935+
if (tp1.isInstanceOf[TypeBounds] || tp2.isInstanceOf[TypeBounds])
3936+
if (tp.isAnd) range(tp1.loBound & tp2.loBound, tp1.hiBound | tp1.hiBound)
3937+
else range(tp1.loBound | tp2.loBound, tp1.hiBound & tp1.hiBound)
3938+
else tp.derivedAndOrType(tp1, tp2)
38933939
override protected def derivedAnnotatedType(tp: AnnotatedType, underlying: Type, annot: Annotation) =
3894-
if (underlying.exists) tp.derivedAnnotatedType(underlying, annot)
3895-
else NoType
3940+
underlying match {
3941+
case TypeBounds(lo, hi) =>
3942+
range(tp.derivedAnnotatedType(lo, annot), tp.derivedAnnotatedType(hi, annot))
3943+
case _ =>
3944+
if (underlying.isBottomType) underlying
3945+
else tp.derivedAnnotatedType(underlying, annot)
3946+
}
38963947
override protected def derivedWildcardType(tp: WildcardType, bounds: Type) =
3897-
if (bounds.exists) tp.derivedWildcardType(bounds)
3898-
else WildcardType
3899-
override protected def derivedClassInfo(tp: ClassInfo, pre: Type): Type =
3900-
if (pre.exists) tp.derivedClassInfo(pre)
3901-
else NoType
3948+
tp.derivedWildcardType(bounds)
3949+
override protected def derivedClassInfo(tp: ClassInfo, pre: Type): Type = {
3950+
assert(!pre.isInstanceOf[TypeBounds])
3951+
tp.derivedClassInfo(pre)
3952+
}
39023953
}
39033954

39043955
// ----- TypeAccumulators ----------------------------------------------------

tests/pos/dependent-extractors.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ object Test {
1010
val y1: Int = y
1111

1212
val z = (c: Any) match { case X(y) => y }
13-
val z1: C#T = z
13+
// val z1: C#T = z // error: z has type Any TODO: find out why
1414
}

0 commit comments

Comments
 (0)