Skip to content

Commit 21a5608

Browse files
authored
Merge pull request #7907 from dotty-staging/fix-#7877
Fix #7877: Refine instantiation criterion before implicit search
2 parents b6cd8e5 + 087b8d4 commit 21a5608

File tree

5 files changed

+77
-40
lines changed

5 files changed

+77
-40
lines changed

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -555,17 +555,17 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
555555

556556
val childTp = if (child.isTerm) child.termRef else child.typeRef
557557

558-
instantiate(childTp, parent)(ctx.fresh.setNewTyperState()).dealias
558+
instantiateToSubType(childTp, parent)(ctx.fresh.setNewTyperState()).dealias
559559
}
560560

561561
/** Instantiate type `tp1` to be a subtype of `tp2`
562562
*
563-
* Return the instantiated type if type parameters and this type
563+
* Return the instantiated type if type parameters in this type
564564
* in `tp1` can be instantiated such that `tp1 <:< tp2`.
565565
*
566566
* Otherwise, return NoType.
567567
*/
568-
private def instantiate(tp1: NamedType, tp2: Type)(implicit ctx: Context): Type = {
568+
private def instantiateToSubType(tp1: NamedType, tp2: Type)(implicit ctx: Context): Type = {
569569
/** expose abstract type references to their bounds or tvars according to variance */
570570
class AbstractTypeMap(maximize: Boolean)(implicit ctx: Context) extends TypeMap {
571571
def expose(lo: Type, hi: Type): Type =
@@ -637,7 +637,6 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
637637
tvar =>
638638
!(ctx.typerState.constraint.entry(tvar.origin) `eq` tvar.origin.underlying) ||
639639
(tvar `eq` removeThisType.prefixTVar),
640-
minimizeAll = false,
641640
allowBottom = false
642641
)
643642

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -779,7 +779,7 @@ trait Implicits { self: Typer =>
779779
/** If `formal` is of the form Eql[T, U], try to synthesize an
780780
* `Eql.eqlAny[T, U]` as solution.
781781
*/
782-
lazy val synthesizedEq: SpecialHandler = {
782+
lazy val synthesizedEql: SpecialHandler = {
783783
(formal, span) => implicit ctx => {
784784

785785
/** Is there an `Eql[T, T]` instance, assuming -strictEquality? */
@@ -1091,7 +1091,7 @@ trait Implicits { self: Typer =>
10911091
mySpecialHandlers = List(
10921092
defn.ClassTagClass -> synthesizedClassTag,
10931093
defn.QuotedTypeClass -> synthesizedTypeTag,
1094-
defn.EqlClass -> synthesizedEq,
1094+
defn.EqlClass -> synthesizedEql,
10951095
defn.TupledFunctionClass -> synthesizedTupleFunction,
10961096
defn.ValueOfClass -> synthesizedValueOf,
10971097
defn.Mirror_ProductClass -> synthesizedProductMirror,

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

Lines changed: 50 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,20 @@ object Inferencing {
4343
if (isFullyDefined(tp, ForceDegree.all)) tp
4444
else throw new Error(i"internal error: type of $what $tp is not fully defined, pos = $span") // !!! DEBUG
4545

46-
/** Instantiate selected type variables `tvars` in type `tp` */
46+
/** Instantiate selected type variables `tvars` in type `tp` in a special mode:
47+
* 1. If a type variable is constrained from below (i.e. constraint bound != given lower bound)
48+
* it is minimized.
49+
* 2. Otherwise, if the type variable is constrained from above, it is maximized.
50+
* 3. Otherwise, if the type variable has a lower bound != Nothing, it is minimized.
51+
* 4. Otherwise, if the type variable has an upper bound != Any, it is maximized.
52+
* If none of (1) - (4) applies, the type variable is left uninstantiated.
53+
* The method is called to instantiate type variables before an implicit search.
54+
*/
4755
def instantiateSelected(tp: Type, tvars: List[Type])(implicit ctx: Context): Unit =
4856
if (tvars.nonEmpty)
49-
new IsFullyDefinedAccumulator(new ForceDegree.Value(tvars.contains, minimizeAll = true, allowBottom = false)).process(tp)
57+
IsFullyDefinedAccumulator(
58+
ForceDegree.Value(tvars.contains, allowBottom = false), minimizeSelected = true
59+
).process(tp)
5060

5161
/** Instantiate any type variables in `tp` whose bounds contain a reference to
5262
* one of the parameters in `tparams` or `vparamss`.
@@ -78,27 +88,35 @@ object Inferencing {
7888
* 2. T is maximized if the constraint over T is only from above (i.e.
7989
* constrained upper bound != given upper bound and
8090
* constrained lower bound == given lower bound).
81-
* If (1) and (2) do not apply:
82-
* 3. T is minimized if forceDegree is minimizeAll.
83-
* 4. Otherwise, T is maximized if it appears only contravariantly in the given type,
84-
* or if forceDegree is `noBottom` and T's minimized value is a bottom type.
85-
* 5. Otherwise, T is minimized.
8691
*
87-
* The instantiation is done in two phases:
92+
* If (1) and (2) do not apply, and minimizeSelected is set:
93+
* 3. T is minimized if it has a lower bound (different from Nothing) in the
94+
* current constraint (the bound might come from T's declaration).
95+
* 4. Otherwise, T is maximized if it has an upper bound (different from Any)
96+
* in the currented constraint (the bound might come from T's declaration).
97+
* 5. Otherwise, T is not instantiated at all.
98+
99+
* If (1) and (2) do not apply, and minimizeSelected is not set:
100+
* 6: T is maximized if it appears only contravariantly in the given type,
101+
* or if forceDegree is `noBottom` and T has no lower bound different from Nothing.
102+
* 7. Otherwise, T is minimized.
103+
*
104+
* The instantiation for (6) and (7) is done in two phases:
88105
* 1st Phase: Try to instantiate minimizable type variables to
89106
* their lower bound. Record whether successful.
90107
* 2nd Phase: If first phase was successful, instantiate all remaining type variables
91108
* to their upper bound.
92109
*/
93-
private class IsFullyDefinedAccumulator(force: ForceDegree.Value)(implicit ctx: Context) extends TypeAccumulator[Boolean] {
110+
private class IsFullyDefinedAccumulator(force: ForceDegree.Value, minimizeSelected: Boolean = false)
111+
(implicit ctx: Context) extends TypeAccumulator[Boolean] {
94112

95113
private def instantiate(tvar: TypeVar, fromBelow: Boolean): Type = {
96114
val inst = tvar.instantiate(fromBelow)
97115
typr.println(i"forced instantiation of ${tvar.origin} = $inst")
98116
inst
99117
}
100118

101-
private var toMaximize: Boolean = false
119+
private var toMaximize: List[TypeVar] = Nil
102120

103121
def apply(x: Boolean, tp: Type): Boolean = tp.dealias match {
104122
case _: WildcardType | _: ProtoType =>
@@ -108,34 +126,33 @@ object Inferencing {
108126
&& ctx.typerState.constraint.contains(tvar)
109127
&& {
110128
val direction = instDirection(tvar.origin)
111-
def preferMin =
112-
force.minimizeAll && (tvar.hasLowerBound || !tvar.hasUpperBound)
113-
|| variance >= 0 && (force.allowBottom || tvar.hasLowerBound)
114-
if (direction != 0) instantiate(tvar, direction < 0)
115-
else if (preferMin) instantiate(tvar, fromBelow = true)
116-
else toMaximize = true
129+
if direction != 0 then
130+
instantiate(tvar, fromBelow = direction < 0)
131+
else if minimizeSelected then
132+
if tvar.hasLowerBound then instantiate(tvar, fromBelow = true)
133+
else if tvar.hasUpperBound then instantiate(tvar, fromBelow = false)
134+
else () // hold off instantiating unbounded unconstrained variables
135+
else if variance >= 0 && (force.allowBottom || tvar.hasLowerBound) then
136+
instantiate(tvar, fromBelow = true)
137+
else
138+
toMaximize = tvar :: toMaximize
117139
foldOver(x, tvar)
118140
}
119141
case tp =>
120142
foldOver(x, tp)
121143
}
122144

123-
private class UpperInstantiator(implicit ctx: Context) extends TypeAccumulator[Unit] {
124-
def apply(x: Unit, tp: Type): Unit = {
125-
tp match {
126-
case tvar: TypeVar if !tvar.isInstantiated =>
145+
def process(tp: Type): Boolean =
146+
// Maximize type vars in the order they were visited before */
147+
def maximize(tvars: List[TypeVar]): Unit = tvars match
148+
case tvar :: tvars1 =>
149+
maximize(tvars1)
150+
if !tvar.isInstantiated then
127151
instantiate(tvar, fromBelow = false)
128-
case _ =>
129-
}
130-
foldOver(x, tp)
131-
}
132-
}
133-
134-
def process(tp: Type): Boolean = {
152+
case nil =>
135153
val res = apply(true, tp)
136-
if (res && toMaximize) new UpperInstantiator().apply((), tp)
154+
if res then maximize(toMaximize)
137155
res
138-
}
139156
}
140157

141158
/** For all type parameters occurring in `tp`:
@@ -492,9 +509,9 @@ trait Inferencing { this: Typer =>
492509

493510
/** An enumeration controlling the degree of forcing in "is-dully-defined" checks. */
494511
@sharable object ForceDegree {
495-
class Value(val appliesTo: TypeVar => Boolean, val minimizeAll: Boolean, val allowBottom: Boolean = true)
496-
val none: Value = new Value(_ => false, minimizeAll = false)
497-
val all: Value = new Value(_ => true, minimizeAll = false)
498-
val noBottom: Value = new Value(_ => true, minimizeAll = false, allowBottom = false)
512+
class Value(val appliesTo: TypeVar => Boolean, val allowBottom: Boolean)
513+
val none: Value = new Value(_ => false, allowBottom = true)
514+
val all: Value = new Value(_ => true, allowBottom = true)
515+
val noBottom: Value = new Value(_ => true, allowBottom = false)
499516
}
500517

library/src/scala/Eql.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ sealed trait Eql[-L, -R]
99

1010
/** Companion object containing a few universally known `Eql` instances.
1111
* Eql instances involving primitive types or the Null type are handled directly in
12-
* the compiler (see Implicits.synthesizedEq), so they are not included here.
12+
* the compiler (see Implicits.synthesizedEql), so they are not included here.
1313
*/
1414
object Eql {
1515
/** A universal `Eql` instance. */

tests/pos/i7877.scala

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
object Example extends App {
2+
3+
trait ZSink[-R, +E, +A0, -A, +B] {
4+
def orElse[R1 <: R, E1, A00 >: A0, A1 <: A, C](
5+
that: ZSink[R1, E1, A00, A1, C]
6+
)(implicit ev: A1 =:= A00): ZSink[R1, E1, A00, A1, Either[B, C]] =
7+
???
8+
}
9+
10+
object ZSink {
11+
def identity[A]: ZSink[Any, Unit, Nothing, A, A] = ???
12+
def fail[E](e: E): ZSink[Any, E, Nothing, Any, Nothing] = ???
13+
}
14+
15+
// compiles
16+
val a: ZSink[Any, String, Int, Int, Either[Int, Nothing]] =
17+
ZSink.identity[Int].orElse(ZSink.fail("Ouch"))
18+
19+
// cannot prove that Int =:= Nothing
20+
ZSink.identity[Int].orElse(ZSink.fail("Ouch"))
21+
}

0 commit comments

Comments
 (0)