Skip to content

Commit

Permalink
Tweak lambda/tvar cleanup, to avoid mis-instantiating
Browse files Browse the repository at this point in the history
  • Loading branch information
dwijnand committed Jan 31, 2025
1 parent 5dda2a3 commit 18e6a95
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 12 deletions.
13 changes: 2 additions & 11 deletions compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala
Original file line number Diff line number Diff line change
Expand Up @@ -436,17 +436,8 @@ trait ConstraintHandling {

val level1 = nestingLevel(p1)
val level2 = nestingLevel(p2)
val p1Wins = if level1 == level2 then
// If the nesting levels match, then we would prefer to unify to the outer most parameter.
// For instance in pos/i21981, while running `normalizedCompatible` against `.map2`,
// we want to unify to B over K, to allow easily removing K by just instantiating it.
def preferP1(ctx: Context): Boolean =
val c = ctx.typerState.constraint
!c.contains(p2) || c.contains(p1) && preferP1(ctx.outer)
preferP1(ctx)
else level1 <= level2
val pKept = if p1Wins then p1 else p2
val pRemoved = if p1Wins then p2 else p1
val pKept = if level1 <= level2 then p1 else p2
val pRemoved = if level1 <= level2 then p2 else p1

val down = constraint.exclusiveLower(p2, p1)
val up = constraint.exclusiveUpper(p1, p2)
Expand Down
10 changes: 9 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,15 @@ object ProtoTypes {
for tvar <- newctx.typerState.ownedVars do
inContext(newctx):
if !tvar.isInstantiated then
tvar.instantiate(fromBelow = false) // any direction
// Filter out any tvar that instantiating would further constrain the current constraint
// Similar to filterByDeps in interpolateTypeVars.
val excluded = ctx.typerState.ownedVars.filter(!_.isInstantiated)
val aboveOK = !ctx.typerState.constraint.dependsOn(tvar, excluded, co = true)
val belowOK = !ctx.typerState.constraint.dependsOn(tvar, excluded, co = false)
if aboveOK then
tvar.instantiate(fromBelow = true)
else if belowOK then
tvar.instantiate(fromBelow = false)

// commit any remaining changes in typer state
newctx.typerState.commit()
Expand Down
28 changes: 28 additions & 0 deletions tests/pos/i21981.alt.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
trait Ops[F[_], A]:
def map0[B](f0: A => B): F[B] = ???

trait Functor1[G[_]]

trait Functor2[H[_]]:
extension [C1, C2](hc: H[(C1, C2)])
def map2[D](f1: (C1, C2) => D): H[D]

trait Ref[I[_], +E]

final class Cov[+F]

class Test:
given [J[_]](using J: Functor1[J]): Functor2[J] with
extension [K1, K2](jk: J[(K1, K2)])
def map2[L](f2: (K1, K2) => L): J[L] = ???

def t1[
M[_[t]],
N[_],
](using N: Functor1[N]): Unit =

val x3: Ops[N, M[[t] =>> Ref[N, t]]] = ???

val x2: N[(M[N], M[[t] =>> Ref[N, t]])] = x3
.map0 { refs => (???, refs) }
.map2 { case (not, refs) => (???, refs) }
29 changes: 29 additions & 0 deletions tests/pos/i21981.orig.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
object internal:
trait Functor[F[_]] {
extension [T](ft: F[T]) def map[T1](f: T => T1): F[T1]
}

object cats:
trait Functor[F[_]]
object Functor:
trait Ops[F[_], A]:
def map[B](f: A => B): F[B] = ???
def toAllFunctorOps[F[_], A](target: F[A])(using Functor[F]): Ops[F, A] = ???

given [F[_]](using cf: cats.Functor[F]): internal.Functor[F] with {
extension [T](ft: F[T]) def map[T1](f: T => T1): F[T1] = ???
}

trait Ref[F[_], +T]
class MemoizingEvaluator[Input[_[_]], Output[_[_]], F[_]: cats.Functor] {
type OptionRef[T] = Ref[F, Option[T]]

def sequence[CaseClass[_[_]], G[_], H[_]](instance: CaseClass[[t] =>> G[H[t]]]): G[CaseClass[H]] = ???
def collectValues(input: Input[F]): F[(Input[F], Input[OptionRef])] = {
val refsF: Input[[t] =>> F[OptionRef[t]]] = ???
for {
refs <- cats.Functor.toAllFunctorOps(sequence[Input, F, OptionRef](refsF))
updating = ???
} yield (updating, refs)
}
}

0 comments on commit 18e6a95

Please sign in to comment.