Skip to content

Commit e005227

Browse files
oderskymichelou
authored andcommitted
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 scala#12247
1 parent c67ffb0 commit e005227

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
@@ -433,6 +433,10 @@ object Types {
433433
/** Is this a higher-kinded type lambda with given parameter variances? */
434434
def isDeclaredVarianceLambda: Boolean = false
435435

436+
/** Does this type contain wildcard types? */
437+
final def containsWildcardTypes(using Context) =
438+
existsPart(_.isInstanceOf[WildcardType], stopAtStatic = true)
439+
436440
// ----- Higher-order combinators -----------------------------------
437441

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

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

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

44764497
/** Unwrap to instance (if instantiated) or origin (if not), until result
44774498
* 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)