Skip to content

Commit

Permalink
Honour hard unions in lubbing and param replacing (#18680)
Browse files Browse the repository at this point in the history
  • Loading branch information
smarter authored Nov 13, 2023
2 parents 8cb4945 + cc55175 commit 41279ac
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 13 deletions.
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Constraint.scala
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ abstract class Constraint extends Showable {
/** The same as this constraint, but with `tv` marked as hard. */
def withHard(tv: TypeVar)(using Context): This

/** Mark toplevel type vars in `tp` as hard. */
def hardenTypeVars(tp: Type)(using Context): This

/** Gives for each instantiated type var that does not yet have its `inst` field
* set, the instance value stored in the constraint. Storing instances in constraints
* is done only in a temporary way for contexts that may be retracted
Expand Down
9 changes: 9 additions & 0 deletions compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala
Original file line number Diff line number Diff line change
Expand Up @@ -750,9 +750,18 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
}
if isRemovable(param.binder) then current = current.remove(param.binder)
current.dropDeps(param)
replacedTypeVar match
case replacedTypeVar: TypeVar if isHard(replacedTypeVar) => current = current.hardenTypeVars(replacement)
case _ =>
current.checkWellFormed()
end replace

def hardenTypeVars(tp: Type)(using Context): OrderingConstraint = tp.dealiasKeepRefiningAnnots match
case tp: TypeVar if contains(tp.origin) => withHard(tp)
case tp: TypeParamRef if contains(tp) => hardenTypeVars(typeVarOfParam(tp))
case tp: AndOrType => hardenTypeVars(tp.tp1).hardenTypeVars(tp.tp2)
case _ => this

def remove(pt: TypeLambda)(using Context): This = {
def removeFromOrdering(po: ParamOrdering) = {
def removeFromBoundss(key: TypeLambda, bndss: Array[List[TypeParamRef]]): Array[List[TypeParamRef]] = {
Expand Down
15 changes: 2 additions & 13 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -501,17 +501,6 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
false
}

/** Mark toplevel type vars in `tp2` as hard in the current constraint */
def hardenTypeVars(tp2: Type): Unit = tp2.dealiasKeepRefiningAnnots match
case tvar: TypeVar if constraint.contains(tvar.origin) =>
constraint = constraint.withHard(tvar)
case tp2: TypeParamRef if constraint.contains(tp2) =>
hardenTypeVars(constraint.typeVarOfParam(tp2))
case tp2: AndOrType =>
hardenTypeVars(tp2.tp1)
hardenTypeVars(tp2.tp2)
case _ =>

val res = widenOK || joinOK
|| recur(tp11, tp2) && recur(tp12, tp2)
|| containsAnd(tp1)
Expand All @@ -534,7 +523,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
// is marked so that it converts all soft unions in its lower bound to hard unions
// before it is instantiated. The reason is that the variable's instance type will
// be a supertype of (decomposed and reconstituted) `tp1`.
hardenTypeVars(tp2)
constraint = constraint.hardenTypeVars(tp2)

res

Expand Down Expand Up @@ -2375,7 +2364,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
case Atoms.Range(lo2, hi2) =>
if hi1.subsetOf(lo2) then return tp2
if hi2.subsetOf(lo1) then return tp1
if (hi1 & hi2).isEmpty then return orType(tp1, tp2)
if (hi1 & hi2).isEmpty then return orType(tp1, tp2, isSoft = isSoft)
case none =>
case none =>
val t1 = mergeIfSuper(tp1, tp2, canConstrain)
Expand Down
14 changes: 14 additions & 0 deletions tests/pos/i18626.min1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
sealed trait Animal
object Cat extends Animal
object Dog extends Animal

type Mammal = Cat.type | Dog.type

class Test:
def t1 =
val mammals: List[Mammal] = ???
val result = mammals.head
val mammal: Mammal = result // was: Type Mismatch Error:
// Found: (result : Animal)
// Required: Mammal
()
32 changes: 32 additions & 0 deletions tests/pos/i18626.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
trait Random[F1[_]]:
def element[T1](list: Seq[T1]): F1[T1] = ???

trait Monad[F2[_]]:
def map[A1, B1](fa: F2[A1])(f: A1 => B1): F2[B1]

object Monad:
extension [F3[_]: Monad, A3](fa: F3[A3])
def map[B3](f: A3 => B3): F3[B3] = ???

sealed trait Animal
object Cat extends Animal
object Dog extends Animal

type Mammal = Cat.type | Dog.type
val mammals: List[Mammal] = ???

class Work[F4[_]](random: Random[F4])(using mf: Monad[F4]):
def result1: F4[Mammal] =
mf.map(fa = random.element(mammals))(a => a)

def result2: F4[Mammal] = Monad.map(random.element(mammals))(a => a)

import Monad.*

def result3: F4[Mammal] = random
.element(mammals)
.map { a =>
a // was: Type Mismatch Error:
// Found: (a : Animal)
// Required: Cat.type | Dog.type
}

0 comments on commit 41279ac

Please sign in to comment.