Skip to content

Commit 5fc0861

Browse files
committed
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
1 parent aadc10f commit 5fc0861

File tree

3 files changed

+59
-9
lines changed

3 files changed

+59
-9
lines changed

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

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,10 @@ object Types {
429429
/** Is this a higher-kinded type lambda with given parameter variances? */
430430
def isDeclaredVarianceLambda: Boolean = false
431431

432+
/** Does this type contain wildcard types? */
433+
final def containsWildcardTypes(using Context) =
434+
existsPart(_.isInstanceOf[WildcardType], stopAtStatic = true)
435+
432436
// ----- Higher-order combinators -----------------------------------
433437

434438
/** Returns true if there is a part of this type that satisfies predicate `p`.
@@ -4461,13 +4465,30 @@ object Types {
44614465
def instantiate(fromBelow: Boolean)(using Context): Type =
44624466
instantiateWith(avoidCaptures(TypeComparer.instanceType(origin, fromBelow)))
44634467

4468+
/** For uninstantiated type variables: the entry in the constraint (either bounds or
4469+
* provisional instance value)
4470+
*/
4471+
private def currentEntry(using Context): Type = ctx.typerState.constraint.entry(origin)
4472+
44644473
/** For uninstantiated type variables: Is the lower bound different from Nothing? */
4465-
def hasLowerBound(using Context): Boolean =
4466-
!ctx.typerState.constraint.entry(origin).loBound.isExactlyNothing
4474+
def hasLowerBound(using Context): Boolean = !currentEntry.loBound.isExactlyNothing
44674475

44684476
/** For uninstantiated type variables: Is the upper bound different from Any? */
4469-
def hasUpperBound(using Context): Boolean =
4470-
!ctx.typerState.constraint.entry(origin).hiBound.isRef(defn.AnyClass)
4477+
def hasUpperBound(using Context): Boolean = !currentEntry.hiBound.isRef(defn.AnyClass)
4478+
4479+
/** For uninstantiated type variables: Is the lower bound different from Nothing and
4480+
* does it not contain wildcard types?
4481+
*/
4482+
def hasNonWildcardLowerBound(using Context): Boolean =
4483+
val lo = currentEntry.loBound
4484+
!lo.isExactlyNothing && !lo.containsWildcardTypes
4485+
4486+
/** For uninstantiated type variables: Is the upper bound different from Any and
4487+
* does it not contain wildcard types?
4488+
*/
4489+
def hasNonWildcardUpperBound(using Context): Boolean =
4490+
val hi = currentEntry.hiBound
4491+
!hi.isRef(defn.AnyClass) && !hi.containsWildcardTypes
44714492

44724493
/** Unwrap to instance (if instantiated) or origin (if not), until result
44734494
* is no longer a TypeVar

compiler/src/dotty/tools/dotc/typer/Inferencing.scala

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -173,12 +173,14 @@ object Inferencing {
173173
&& ctx.typerState.constraint.contains(tvar)
174174
&& {
175175
val direction = instDirection(tvar.origin)
176-
if direction != 0 then
176+
if minimizeSelected then
177+
if direction <= 0 && tvar.hasNonWildcardLowerBound then
178+
instantiate(tvar, fromBelow = true)
179+
else if direction >= 0 && tvar.hasNonWildcardUpperBound then
180+
instantiate(tvar, fromBelow = false)
181+
// else hold off instantiating unbounded unconstrained variable
182+
else if direction != 0 then
177183
instantiate(tvar, fromBelow = direction < 0)
178-
else if minimizeSelected then
179-
if tvar.hasLowerBound then instantiate(tvar, fromBelow = true)
180-
else if tvar.hasUpperBound then instantiate(tvar, fromBelow = false)
181-
else () // hold off instantiating unbounded unconstrained variables
182184
else if variance >= 0 && (force.ifBottom == IfBottom.ok || tvar.hasLowerBound) then
183185
instantiate(tvar, fromBelow = true)
184186
else if variance >= 0 && force.ifBottom == IfBottom.fail then

tests/pos/i12247.scala

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
sealed abstract class CtorType
2+
object CtorType {
3+
final class Props extends CtorType
4+
sealed trait Summoner { type CT <: CtorType }
5+
implicit def summonP: Summoner {type CT = Props} = ???
6+
}
7+
8+
final case class Builder() {
9+
def build(using ctorType: CtorType.Summoner): Component[ctorType.CT] = ???
10+
}
11+
12+
final class Component[CT <: CtorType]
13+
14+
object Test {
15+
16+
def assertTypeOf[A](a: => A) = new TestDsl[A]
17+
class TestDsl[A] {
18+
def is[B](implicit ev: A =:= B): Unit = ()
19+
}
20+
21+
type Expect = Component[CtorType.Props]
22+
23+
assertTypeOf( Builder().build ).is[Expect] // error
24+
25+
val x = Builder().build
26+
assertTypeOf(x).is[Expect] // ok
27+
}

0 commit comments

Comments
 (0)