Skip to content

Commit e06e52a

Browse files
committed
Integrate prune logic in addOneBound
New cyclic bounds can also appear as a consequence of unification. The latter goes through addOneBound, but not through addConstraint. So the prune logic that avoids cycles should go to addOneBound. ExprType checking could logically stay with addConstraint but it was simpler to move it as well.
1 parent f876537 commit e06e52a

File tree

1 file changed

+56
-71
lines changed

1 file changed

+56
-71
lines changed

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

Lines changed: 56 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -87,55 +87,85 @@ trait ConstraintHandling[AbstractContext] {
8787
nonParamBounds(param).derivedTypeBounds(fullLowerBound(param), fullUpperBound(param))
8888

8989
protected def addOneBound(param: TypeParamRef, rawBound: Type, isUpper: Boolean)(implicit actx: AbstractContext): Boolean =
90-
!constraint.contains(param) || {
91-
def avoidCycle(tp: Type): Type = tp match
92-
case tp: AndOrType =>
93-
tp.derivedAndOrType(avoidCycle(tp.tp1), avoidCycle(tp.tp2))
94-
case tp: TypeParamRef =>
95-
if tp eq param then
96-
//println(i"stripping $tp from $rawBound, upper = $isUpper in $constraint")
97-
if isUpper then defn.AnyType else defn.NothingType
98-
else constraint.entry(tp) match
99-
case TypeBounds(lo, hi) => if lo eq hi then avoidCycle(lo) else tp
100-
case inst => avoidCycle(inst)
101-
case tp: TypeVar =>
102-
val underlying1 = avoidCycle(tp.underlying)
103-
if underlying1 eq tp.underlying then tp else underlying1
104-
case _ =>
105-
tp
90+
91+
/** Normalize the bound `tp` in the following ways:
92+
*
93+
* 1. Any toplevel occurrences of the compared parameter `param` are
94+
* replaced by `Nothing` if bound is from below or `Any` otherwise.
95+
* 2. Toplevel occurrences of TypeVars over TypeRefs in the current
96+
* constraint are dereferenced.
97+
* 3. Toplevel occurrences of TypeRefs that are instantiated in the current
98+
* constraint are also dereferenced.
99+
* 4. Toplevel occurrences of ExprTypes lead to a `NoType` return, which
100+
* causes the addOneBound operation to fail.
101+
*
102+
* An occurrence is toplevel if it is the bound itself,
103+
* or the bound is a union or intersection, and the ocurrence is
104+
* toplevel in one of the operands of the `&` or `|`.
105+
*/
106+
def avoidCycle(tp: Type): Type = tp match
107+
case tp: AndOrType =>
108+
val p1 = avoidCycle(tp.tp1)
109+
val p2 = avoidCycle(tp.tp2)
110+
if p1.exists && p2.exists then tp.derivedAndOrType(p1, p2) else NoType
111+
case tp: TypeParamRef =>
112+
if tp eq param then
113+
//println(i"stripping $tp from $rawBound, upper = $isUpper in $constraint")
114+
if isUpper then defn.AnyType else defn.NothingType
115+
else constraint.entry(tp) match
116+
case TypeBounds(lo, hi) => if lo eq hi then avoidCycle(lo) else tp
117+
case inst => avoidCycle(inst)
118+
case tp: TypeVar =>
119+
val underlying1 = avoidCycle(tp.underlying)
120+
if underlying1 eq tp.underlying then tp else underlying1
121+
case tp: ExprType =>
122+
// ExprTypes are not value types, so type parameters should not
123+
// be instantiated to ExprTypes. A scenario where such an attempted
124+
// instantiation can happen is if we unify (=> T) => () with A => ()
125+
// where A is a TypeParamRef. See the comment on EtaExpansion.etaExpand
126+
// why types such as (=> T) => () can be constructed and i7969.scala
127+
// as a test where this happens.
128+
// Note that scalac by contrast allows such instantiations. But letting
129+
// type variables be ExprTypes has its own problems (e.g. you can't write
130+
// the resulting types down) and is largely unknown terrain.
131+
NoType
132+
case _ =>
133+
tp
134+
135+
if !constraint.contains(param) then false
136+
else
106137
val bound = avoidCycle(rawBound)
107138

108139
val oldBounds @ TypeBounds(lo, hi) = constraint.nonParamBounds(param)
109140
val equalBounds = isUpper && (lo eq bound) || !isUpper && (bound eq hi)
110-
if (equalBounds &&
111-
!bound.existsPart(bp => bp.isInstanceOf[WildcardType] || (bp eq param))) {
141+
if !bound.exists then false
142+
else if equalBounds
143+
&& !bound.existsPart(bp => bp.isInstanceOf[WildcardType] || (bp eq param))
144+
then
112145
// The narrowed bounds are equal and do not contain wildcards,
113146
// so we can remove `param` from the constraint.
114147
// (Handling wildcards requires choosing a bound, but we don't know which
115148
// bound to choose here, this is handled in `ConstraintHandling#approximation`)
116149
constraint = constraint.replace(param, bound)
117150
true
118-
}
119-
else {
151+
else
120152
// Narrow one of the bounds of type parameter `param`
121153
// If `isUpper` is true, ensure that `param <: `bound`, otherwise ensure
122154
// that `param >: bound`.
123-
val narrowedBounds = {
155+
val narrowedBounds =
124156
val saved = homogenizeArgs
125157
homogenizeArgs = Config.alignArgsInAnd
126158
try
127159
if (isUpper) oldBounds.derivedTypeBounds(lo, hi & bound)
128160
else oldBounds.derivedTypeBounds(lo | bound, hi)
129161
finally homogenizeArgs = saved
130-
}
131162
val c1 = constraint.updateEntry(param, narrowedBounds)
132163
(c1 eq constraint) || {
133164
constraint = c1
134165
val TypeBounds(lo, hi) = constraint.entry(param)
135166
isSubType(lo, hi)
136167
}
137-
}
138-
}
168+
end addOneBound
139169

140170
private def location(implicit ctx: Context) = "" // i"in ${ctx.typerState.stateChainStr}" // use for debugging
141171

@@ -201,7 +231,6 @@ trait ConstraintHandling[AbstractContext] {
201231
up.forall(addOneBound(_, lo, isUpper = false))
202232
}
203233

204-
205234
protected def isSubType(tp1: Type, tp2: Type, whenFrozen: Boolean)(implicit actx: AbstractContext): Boolean =
206235
if (whenFrozen)
207236
isSubTypeWhenFrozen(tp1, tp2)
@@ -461,49 +490,6 @@ trait ConstraintHandling[AbstractContext] {
461490
if (fromBelow) isSubType(bound, tp) else isSubType(tp, bound)
462491
}
463492

464-
/** Normalize the bound `bnd` in the following ways:
465-
*
466-
* 1. Any toplevel occurrences of the compared parameter `param` are
467-
* replaced by `Nothing` if bound is from below or `Any` otherwise.
468-
* 2. Toplevel occurrences of TypeVars over TypeRefs in the current
469-
* constraint are dereferenced.
470-
* 3. Toplevel occurrences of TypeRefs that are instantiated in the current
471-
* constraint are also referenced.
472-
* 4. Toplevel occurrences of ExprTypes lead to a `NoType` return, which
473-
* causes the addConstraint operation to fail.
474-
*
475-
* An occurrence is toplevel if it is the bound itself,
476-
* or the bound is a union or intersection, and the ocurrence is
477-
* toplevel in one of the operands of the `&` or `|`.
478-
*/
479-
def prune(bnd: Type): Type = bnd match
480-
case bnd: AndOrType =>
481-
val p1 = prune(bnd.tp1)
482-
val p2 = prune(bnd.tp2)
483-
if (p1.exists && p2.exists) bnd.derivedAndOrType(p1, p2)
484-
else NoType
485-
case bnd: TypeVar if constraint contains bnd.origin => // (2)
486-
prune(bnd.underlying)
487-
case bnd: TypeParamRef =>
488-
if bnd eq param then // (1)
489-
if fromBelow then defn.NothingType else defn.AnyType
490-
else constraint.entry(bnd) match
491-
case TypeBounds(lo, hi) => if lo eq hi then prune(lo) else bnd
492-
case inst => prune(inst) // (3)
493-
case bnd: ExprType => // (4)
494-
// ExprTypes are not value types, so type parameters should not
495-
// be instantiated to ExprTypes. A scenario where such an attempted
496-
// instantiation can happen is if we unify (=> T) => () with A => ()
497-
// where A is a TypeParamRef. See the comment on EtaExpansion.etaExpand
498-
// why types such as (=> T) => () can be constructed and i7969.scala
499-
// as a test where this happens.
500-
// Note that scalac by contrast allows such instantiations. But letting
501-
// type variables be ExprTypes has its own problems (e.g. you can't write
502-
// the resulting types down) and is largely unknown terrain.
503-
NoType
504-
case _ =>
505-
bnd
506-
507493
def kindCompatible(tp1: Type, tp2: Type): Boolean =
508494
val tparams1 = tp1.typeParams
509495
val tparams2 = tp2.typeParams
@@ -521,9 +507,8 @@ trait ConstraintHandling[AbstractContext] {
521507
case bound: TypeParamRef if constraint contains bound =>
522508
addParamBound(bound)
523509
case _ =>
524-
val pbound = prune(pruneLambdaParams(bound))
525-
pbound.exists
526-
&& kindCompatible(param, pbound)
510+
val pbound = pruneLambdaParams(bound)
511+
kindCompatible(param, pbound)
527512
&& (if fromBelow then addLowerBound(param, pbound) else addUpperBound(param, pbound))
528513
finally addConstraintInvocations -= 1
529514
}

0 commit comments

Comments
 (0)