Skip to content

Commit

Permalink
Record constraint against tvar parameters
Browse files Browse the repository at this point in the history
Instead of using TypeVar#underlying, which uses Constraint#instType,
relying only on TypeVar#inst.  That way we can record constraints
against the original parameter and _then_ maximise the type, which
results in `Bar[String]` instead of `Bar[X]`, in both cases with `X :=
String` recorded in the GADT constraint.
  • Loading branch information
dwijnand committed Jul 25, 2022
1 parent fd38489 commit 746f50b
Show file tree
Hide file tree
Showing 4 changed files with 14 additions and 22 deletions.
11 changes: 3 additions & 8 deletions compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,7 @@ trait ConstraintHandling {
val bound = legalBound(param, rawBound, isUpper)
val oldBounds @ TypeBounds(lo, hi) = constraint.nonParamBounds(param)
val equalBounds = (if isUpper then lo else hi) eq bound
val isGadt = ctx.mode.is(Mode.GadtConstraintInference)
if equalBounds && !isGadt && !bound.existsPart(_ eq param, StopAt.Static) then
if equalBounds && !bound.existsPart(_ eq param, StopAt.Static) then
// The narrowed bounds are equal and not recursive,
// so we can remove `param` from the constraint.
constraint = constraint.replace(param, bound)
Expand All @@ -268,12 +267,8 @@ trait ConstraintHandling {
homogenizeArgs = Config.alignArgsInAnd
try
trustBounds = false
if isUpper then
if isGadt && !hi.isAny && hi.isValueTypeOrWildcard && bound.isValueTypeOrWildcard then oldBounds.derivedTypeBounds(lo, AndType(hi, bound))
else oldBounds.derivedTypeBounds(lo, hi & bound)
else
if isGadt && !lo.isNothingType && lo.isValueTypeOrWildcard && bound.isValueTypeOrWildcard then oldBounds.derivedTypeBounds(OrType(lo, bound, soft = true), hi)
else oldBounds.derivedTypeBounds(lo | bound, hi)
if isUpper then oldBounds.derivedTypeBounds(lo, hi & bound)
else oldBounds.derivedTypeBounds(lo | bound, hi)
finally
homogenizeArgs = savedHomogenizeArgs
trustBounds = savedTrustBounds
Expand Down
9 changes: 3 additions & 6 deletions compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala
Original file line number Diff line number Diff line change
Expand Up @@ -162,16 +162,13 @@ class OrderingConstraint(private val boundsMap: ParamBounds,

def contains(pt: TypeLambda): Boolean = boundsMap(pt) != null

def contains(param: TypeParamRef): Boolean = {
val entries = boundsMap(param.binder)
entries != null && isBounds(entries(param.paramNum))
}
def contains(param: TypeParamRef): Boolean = boundsMap(param.binder) != null

def contains(tvar: TypeVar): Boolean = {
val origin = tvar.origin
val entries = boundsMap(origin.binder)
val pnum = origin.paramNum
entries != null && isBounds(entries(pnum)) && (typeVar(entries, pnum) eq tvar)
entries != null && (typeVar(entries, pnum) eq tvar)
}

// ---------- Dependency handling ----------------------------------------------
Expand Down Expand Up @@ -548,7 +545,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,

def forallParams(p: TypeParamRef => Boolean): Boolean =
boundsMap.forallBinding { (poly, entries) =>
!0.until(paramCount(entries)).exists(i => isBounds(entries(i)) && !p(poly.paramRefs(i)))
(0 until paramCount(entries)).forall(i => p(poly.paramRefs(i)))
}

def foreachParam(p: (TypeLambda, Int) => Unit): Unit =
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
protected def gadtAddLowerBound(sym: Symbol, b: Type): Boolean = ctx.gadt.addBound(sym, b, isUpper = false)
protected def gadtAddUpperBound(sym: Symbol, b: Type): Boolean = ctx.gadt.addBound(sym, b, isUpper = true)

protected def typeVarInstance(tvar: TypeVar)(using Context): Type = tvar.underlying
protected def typeVarInstance(tvar: TypeVar)(using Context): Type = tvar.inst.orElse(tvar.origin)

// Subtype testing `<:<`

Expand Down Expand Up @@ -1201,7 +1201,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
def compareLower(tycon2bounds: TypeBounds, tyconIsTypeRef: Boolean): Boolean =
if ((tycon2bounds.lo `eq` tycon2bounds.hi) && !tycon2bounds.isInstanceOf[MatchAlias])
if (tyconIsTypeRef) recur(tp1, tp2.superTypeNormalized)
else recur(tp1, tycon2bounds.lo.applyIfParameterized(args2))
else isSubApproxHi(tp1, tycon2bounds.lo.applyIfParameterized(args2))
else
fallback(tycon2bounds.lo)

Expand Down
12 changes: 6 additions & 6 deletions tests/pos/i15289.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ class Test:
// max: Bar[X]

// what's more correct?
// * unapp arg tpe = Bar[X] gadt: X =:= String
// * unapp arg tpe = Bar[String] gadt: X =:= String
// * unapp arg tpe = Bar[X] gadt: X := String
// * unapp arg tpe = Bar[String] gadt: X := String

// after fix:
// ptc: Bar[C] aka Foo[C, C] vs Foo[X, String]
// ptc: C >: X ~> cstr: C >: X
// ptc: C <: X ~> cstr: C =:= X
// ptc: C >: String ~> cstr: C >: X | String <: X gadt: X >: String
// ptc: C <: String ~> cstr: C >: X | String <: X & String gadt: X =:= String
// ptc: C >: X ~> cstr: C >: X
// ptc: C <: X ~> cstr: C >: X <: X
// ptc: C >: String ~> cstr: C >: X | String <: X gadt: X >: String
// ptc: C <: String ~> cstr: C >: X | String <: String gadt: X := String
// ptc: Bar[C]
// max: Bar[String]

0 comments on commit 746f50b

Please sign in to comment.