Skip to content

Commit 38272aa

Browse files
authored
Merge pull request #9012 from dotty-staging/fix-instantiation
Fix handling of recursive bounds in constraints
2 parents 43e4bfa + 9ac5a5b commit 38272aa

File tree

3 files changed

+25
-13
lines changed

3 files changed

+25
-13
lines changed

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

+13-13
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ trait ConstraintHandling[AbstractContext] {
8787

8888
protected def addOneBound(param: TypeParamRef, bound: Type, isUpper: Boolean)(using AbstractContext): Boolean =
8989
if !constraint.contains(param) then true
90+
else if !isUpper && param.occursIn(bound)
91+
// We don't allow recursive lower bounds when defining a type,
92+
// so we shouldn't allow them as constraints either.
93+
false
9094
else
9195
val oldBounds @ TypeBounds(lo, hi) = constraint.nonParamBounds(param)
9296
val equalBounds = (if isUpper then lo else hi) eq bound
@@ -238,25 +242,20 @@ trait ConstraintHandling[AbstractContext] {
238242

239243
/** Solve constraint set for given type parameter `param`.
240244
* If `fromBelow` is true the parameter is approximated by its lower bound,
241-
* otherwise it is approximated by its upper bound. However, any occurrences
242-
* of the parameter in a refinement somewhere in the bound are removed. Also
243-
* 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.
244249
* (Such occurrences can arise for F-bounded types).
245250
* The constraint is left unchanged.
246251
* @return the instantiating type
247252
* @pre `param` is in the constraint's domain.
248253
*/
249254
final def approximation(param: TypeParamRef, fromBelow: Boolean)(implicit actx: AbstractContext): Type = {
250-
val avoidParam = new TypeMap {
255+
val replaceWildcards = new TypeMap {
251256
override def stopAtStatic = true
252-
def avoidInArg(arg: Type): Type =
253-
if (param.occursIn(arg)) TypeBounds.empty else arg
254257
def apply(tp: Type) = mapOver {
255258
tp match {
256-
case tp @ AppliedType(tycon, args) =>
257-
tp.derivedAppliedType(tycon, args.mapConserve(avoidInArg))
258-
case tp: RefinedType if param occursIn tp.refinedInfo =>
259-
tp.parent
260259
case tp: WildcardType =>
261260
val bounds = tp.optBounds.orElse(TypeBounds.empty).bounds
262261
// Try to instantiate the wildcard to a type that is known to conform to it.
@@ -283,9 +282,10 @@ trait ConstraintHandling[AbstractContext] {
283282
}
284283
}
285284
constraint.entry(param) match {
286-
case _: TypeBounds =>
287-
val bound = if (fromBelow) fullLowerBound(param) else fullUpperBound(param)
288-
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)
289289
typr.println(s"approx ${param.show}, from below = $fromBelow, bound = ${bound.show}, inst = ${inst.show}")
290290
inst
291291
case inst =>

tests/neg/i8976.scala

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
trait Cons[X, Y]
2+
3+
def solve[X, Y](using Cons[X, Y] =:= Cons[1, Cons[2, Y]]) = ()
4+
5+
@main def main = solve // error
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class Foo[F <: Foo[F]]
2+
class Bar extends Foo[Bar]
3+
4+
class A {
5+
def foo[T <: Foo[T], U >: Foo[T] <: T](x: T): T = x
6+
foo(new Bar) // error
7+
}

0 commit comments

Comments
 (0)