diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 15b0b00ed0f3..d2fc225ff19d 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -4781,7 +4781,7 @@ object Types { def hasLowerBound(using Context): Boolean = !currentEntry.loBound.isExactlyNothing /** For uninstantiated type variables: Is the upper bound different from Any? */ - def hasUpperBound(using Context): Boolean = !currentEntry.hiBound.isRef(defn.AnyClass) + def hasUpperBound(using Context): Boolean = !currentEntry.hiBound.finalResultType.isExactlyAny /** Unwrap to instance (if instantiated) or origin (if not), until result * is no longer a TypeVar diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index be74cb0fa2ec..af4174423cbc 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -183,7 +183,7 @@ object Inferencing { // else hold off instantiating unbounded unconstrained variable else if direction != 0 then instantiate(tvar, fromBelow = direction < 0) - else if variance >= 0 && (force.ifBottom == IfBottom.ok || tvar.hasLowerBound) then + else if variance >= 0 && (force.ifBottom == IfBottom.ok && !tvar.hasUpperBound || tvar.hasLowerBound) then instantiate(tvar, fromBelow = true) else if variance >= 0 && force.ifBottom == IfBottom.fail then return false diff --git a/compiler/test/dotty/tools/dotc/typer/InstantiateModel.scala b/compiler/test/dotty/tools/dotc/typer/InstantiateModel.scala new file mode 100644 index 000000000000..b08062913dac --- /dev/null +++ b/compiler/test/dotty/tools/dotc/typer/InstantiateModel.scala @@ -0,0 +1,57 @@ +package dotty.tools +package dotc +package typer + +// Modelling the decision in IsFullyDefined +object InstantiateModel: + enum LB { case NN; case LL; case L1 }; import LB.* + enum UB { case AA; case UU; case U1 }; import UB.* + enum Var { case V; case NotV }; import Var.* + enum MSe { case M; case NotM }; import MSe.* + enum Bot { case Fail; case Ok; case Flip }; import Bot.* + enum Act { case Min; case Max; case ToMax; case Skip; case False }; import Act.* + + // NN/AA = Nothing/Any + // LL/UU = the original bounds, on the type parameter + // L1/U1 = the constrained bounds, on the type variable + // V = variance >= 0 ("non-contravariant") + // MSe = minimisedSelected + // Bot = IfBottom + // ToMax = delayed maximisation, via addition to toMaximize + // Skip = minimisedSelected "hold off instantiating" + // False = return false + + // there are 9 combinations: + // # | LB | UB | d | // d = direction + // --+----+----+---+ + // 1 | L1 | AA | - | L1 <: T + // 2 | L1 | UU | - | L1 <: T <: UU + // 3 | LL | U1 | + | LL <: T <: U1 + // 4 | NN | U1 | + | T <: U1 + // 5 | L1 | U1 | 0 | L1 <: T <: U1 + // 6 | LL | UU | 0 | LL <: T <: UU + // 7 | LL | AA | 0 | LL <: T + // 8 | NN | UU | 0 | T <: UU + // 9 | NN | AA | 0 | T + + def decide(lb: LB, ub: UB, v: Var, bot: Bot, m: MSe): Act = (lb, ub) match + case (L1, AA) => Min + case (L1, UU) => Min + case (LL, U1) => Max + case (NN, U1) => Max + + case (L1, U1) => if m==M || v==V then Min else ToMax + case (LL, UU) => if m==M || v==V then Min else ToMax + case (LL, AA) => if m==M || v==V then Min else ToMax + + case (NN, UU) => bot match + case _ if m==M => Max + //case Ok if v==V => Min // removed, i14218 fix + case Fail if v==V => False + case _ => ToMax + + case (NN, AA) => bot match + case _ if m==M => Skip + case Ok if v==V => Min + case Fail if v==V => False + case _ => ToMax diff --git a/tests/neg/i15525.scala b/tests/neg/i15525.scala index 0813d7c82435..b14c244b43c8 100644 --- a/tests/neg/i15525.scala +++ b/tests/neg/i15525.scala @@ -37,7 +37,7 @@ def element22( transmittable20.Type / transmittable21.Type } = ??? -def test22 = +def test22 = // error Resolution( element22( Resolution(element0), Resolution(element0), // error // error diff --git a/tests/pos/i14218.http4s.scala b/tests/pos/i14218.http4s.scala new file mode 100644 index 000000000000..774a5432177e --- /dev/null +++ b/tests/pos/i14218.http4s.scala @@ -0,0 +1,22 @@ +// A minimisation from http4s, +// which broke while implementing the fix for i14218. + +final class Bar[+F[_]] +object Bar: + def empty[F[_]]: Bar[F] = new Bar[Nothing] + +final class Foo[+F[_]] + +object Foo: + def apply[F[_]](bar: Bar[F] = Bar.empty): Foo[F] = new Foo + +class Test: + def test[F[_]]: Foo[F] = Foo[F]() + +//-- [E007] Type Mismatch Error +//12 | def test[F[_]]: Foo[F] = Foo[F]() +// | ^^^^^^ +// | Found: Bar[[_] =>> Any] +// | Required: Bar[F] +// | +// | where: F is a type in method t1 with bounds <: [_] =>> Any diff --git a/tests/pos/i14218.scala b/tests/pos/i14218.scala new file mode 100644 index 000000000000..e35aec94b3bd --- /dev/null +++ b/tests/pos/i14218.scala @@ -0,0 +1,15 @@ +class Pet +class Cat extends Pet + +class Z1[ S1 <: Pet](val fn: S1 => Unit) +class Z2[ S2 ](val fn: S2 => Unit) +class Z3[-S3 <: Pet](val fn: S3 => Unit) + +abstract class Test: + def test = + val r1 = new Z1((_: Pet) => ()); eat[Z1[Pet]](r1) // the case: using the parameter bound in situ infers Z[Nothing] + val r2 = new Z2((_: Pet) => ()); eat[Z2[Pet]](r2) // counter-example: infers as desired without an upper bound + val r3 = new Z3((_: Pet) => ()); eat[Z3[Pet]](r3) // workaround: declare it contravariant + val r4 = new Z1((_: Cat) => ()); eat[Z1[Cat]](r4) // counter-example: infers as desired with a subtype + + def eat[T](x: T): Unit