From 5fc0861acf66e830ac9682bdc0ac7f80ac63caa6 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 30 Apr 2021 12:13:34 +0200 Subject: [PATCH] Refrain from instantiating type variables to undetermined types When instantiating type variables before implicit search, refrain instantiating them with bounds that are either Nothing or Any or that contain wildcard types. Fixes #12247 --- .../src/dotty/tools/dotc/core/Types.scala | 29 ++++++++++++++++--- .../dotty/tools/dotc/typer/Inferencing.scala | 12 ++++---- tests/pos/i12247.scala | 27 +++++++++++++++++ 3 files changed, 59 insertions(+), 9 deletions(-) create mode 100644 tests/pos/i12247.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 8a0b0f12c167..739aff14d4b2 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -429,6 +429,10 @@ object Types { /** Is this a higher-kinded type lambda with given parameter variances? */ def isDeclaredVarianceLambda: Boolean = false + /** Does this type contain wildcard types? */ + final def containsWildcardTypes(using Context) = + existsPart(_.isInstanceOf[WildcardType], stopAtStatic = true) + // ----- Higher-order combinators ----------------------------------- /** Returns true if there is a part of this type that satisfies predicate `p`. @@ -4461,13 +4465,30 @@ object Types { def instantiate(fromBelow: Boolean)(using Context): Type = instantiateWith(avoidCaptures(TypeComparer.instanceType(origin, fromBelow))) + /** For uninstantiated type variables: the entry in the constraint (either bounds or + * provisional instance value) + */ + private def currentEntry(using Context): Type = ctx.typerState.constraint.entry(origin) + /** For uninstantiated type variables: Is the lower bound different from Nothing? */ - def hasLowerBound(using Context): Boolean = - !ctx.typerState.constraint.entry(origin).loBound.isExactlyNothing + def hasLowerBound(using Context): Boolean = !currentEntry.loBound.isExactlyNothing /** For uninstantiated type variables: Is the upper bound different from Any? */ - def hasUpperBound(using Context): Boolean = - !ctx.typerState.constraint.entry(origin).hiBound.isRef(defn.AnyClass) + def hasUpperBound(using Context): Boolean = !currentEntry.hiBound.isRef(defn.AnyClass) + + /** For uninstantiated type variables: Is the lower bound different from Nothing and + * does it not contain wildcard types? + */ + def hasNonWildcardLowerBound(using Context): Boolean = + val lo = currentEntry.loBound + !lo.isExactlyNothing && !lo.containsWildcardTypes + + /** For uninstantiated type variables: Is the upper bound different from Any and + * does it not contain wildcard types? + */ + def hasNonWildcardUpperBound(using Context): Boolean = + val hi = currentEntry.hiBound + !hi.isRef(defn.AnyClass) && !hi.containsWildcardTypes /** 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 59ee126f382d..04a3d3e7493e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -173,12 +173,14 @@ object Inferencing { && ctx.typerState.constraint.contains(tvar) && { val direction = instDirection(tvar.origin) - if direction != 0 then + if minimizeSelected then + if direction <= 0 && tvar.hasNonWildcardLowerBound then + instantiate(tvar, fromBelow = true) + else if direction >= 0 && tvar.hasNonWildcardUpperBound then + instantiate(tvar, fromBelow = false) + // else hold off instantiating unbounded unconstrained variable + else if direction != 0 then instantiate(tvar, fromBelow = direction < 0) - else if minimizeSelected then - if tvar.hasLowerBound then instantiate(tvar, fromBelow = true) - else if tvar.hasUpperBound then instantiate(tvar, fromBelow = false) - else () // hold off instantiating unbounded unconstrained variables else if variance >= 0 && (force.ifBottom == IfBottom.ok || tvar.hasLowerBound) then instantiate(tvar, fromBelow = true) else if variance >= 0 && force.ifBottom == IfBottom.fail then diff --git a/tests/pos/i12247.scala b/tests/pos/i12247.scala new file mode 100644 index 000000000000..b2b522f36044 --- /dev/null +++ b/tests/pos/i12247.scala @@ -0,0 +1,27 @@ +sealed abstract class CtorType +object CtorType { + final class Props extends CtorType + sealed trait Summoner { type CT <: CtorType } + implicit def summonP: Summoner {type CT = Props} = ??? +} + +final case class Builder() { + def build(using ctorType: CtorType.Summoner): Component[ctorType.CT] = ??? +} + +final class Component[CT <: CtorType] + +object Test { + + def assertTypeOf[A](a: => A) = new TestDsl[A] + class TestDsl[A] { + def is[B](implicit ev: A =:= B): Unit = () + } + + type Expect = Component[CtorType.Props] + + assertTypeOf( Builder().build ).is[Expect] // error + + val x = Builder().build + assertTypeOf(x).is[Expect] // ok +}