Skip to content

Commit 9ac5a5b

Browse files
committed
Fix unsound type variable instantiation with recursive bounds
`approximation` used to replace recursive occurences of the type variable being instantiated by `TypeBounds.empty`, but this is not sound in general since the approximation might be outside the bounds of the type variable. We could try to do something fancy with ApproximatingTypeMap but it seems complicated (and expensive) and there's an easier solution: since last commit, only the upper bound is allowed to be recursive, so we can just instantiate the type variable to its lower bound to avoid the problem in a sound and cheap way.
1 parent a1067eb commit 9ac5a5b

File tree

1 file changed

+9
-13
lines changed

1 file changed

+9
-13
lines changed

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

+9-13
Original file line numberDiff line numberDiff line change
@@ -242,25 +242,20 @@ trait ConstraintHandling[AbstractContext] {
242242

243243
/** Solve constraint set for given type parameter `param`.
244244
* If `fromBelow` is true the parameter is approximated by its lower bound,
245-
* otherwise it is approximated by its upper bound. However, any occurrences
246-
* of the parameter in a refinement somewhere in the bound are removed. Also
247-
* wildcard types in bounds are approximated by their upper or lower bounds.
245+
* otherwise it is approximated by its upper bound, unless the upper bound
246+
* contains a reference to the parameter itself (`addOneBound` ensures that
247+
* such reference never occur in the lower bound).
248+
* Wildcard types in bounds are approximated by their upper or lower bounds.
248249
* (Such occurrences can arise for F-bounded types).
249250
* The constraint is left unchanged.
250251
* @return the instantiating type
251252
* @pre `param` is in the constraint's domain.
252253
*/
253254
final def approximation(param: TypeParamRef, fromBelow: Boolean)(implicit actx: AbstractContext): Type = {
254-
val avoidParam = new TypeMap {
255+
val replaceWildcards = new TypeMap {
255256
override def stopAtStatic = true
256-
def avoidInArg(arg: Type): Type =
257-
if (param.occursIn(arg)) TypeBounds.empty else arg
258257
def apply(tp: Type) = mapOver {
259258
tp match {
260-
case tp @ AppliedType(tycon, args) =>
261-
tp.derivedAppliedType(tycon, args.mapConserve(avoidInArg))
262-
case tp: RefinedType if param occursIn tp.refinedInfo =>
263-
tp.parent
264259
case tp: WildcardType =>
265260
val bounds = tp.optBounds.orElse(TypeBounds.empty).bounds
266261
// Try to instantiate the wildcard to a type that is known to conform to it.
@@ -287,9 +282,10 @@ trait ConstraintHandling[AbstractContext] {
287282
}
288283
}
289284
constraint.entry(param) match {
290-
case _: TypeBounds =>
291-
val bound = if (fromBelow) fullLowerBound(param) else fullUpperBound(param)
292-
val inst = avoidParam(bound)
285+
case entry: TypeBounds =>
286+
val useLowerBound = fromBelow || param.occursIn(entry.hi)
287+
val bound = if (useLowerBound) fullLowerBound(param) else fullUpperBound(param)
288+
val inst = replaceWildcards(bound)
293289
typr.println(s"approx ${param.show}, from below = $fromBelow, bound = ${bound.show}, inst = ${inst.show}")
294290
inst
295291
case inst =>

0 commit comments

Comments
 (0)