Skip to content

Fix #6127: Drop followAlias #6294

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 14 additions & 15 deletions compiler/src/dotty/tools/dotc/typer/Inferencing.scala
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,10 @@ object Inferencing {
* constrained upper bound != given upper bound and
* constrained lower bound == given lower bound).
* If (1) and (2) do not apply:
* 3. T is maximized if it appears only contravariantly in the given type.
* 4. T is minimized in all other cases.
* 3. T is minimized if forceDegree is minimizeAll.
* 4. Otherwise, T is maximized if it appears only contravariantly in the given type,
* or if forceDegree is `noBottom` and T's minimized value is a bottom type.
* 5. Otherwise, T is minimized.
*
* The instantiation is done in two phases:
* 1st Phase: Try to instantiate minimizable type variables to
Expand All @@ -89,32 +91,29 @@ object Inferencing {
* to their upper bound.
*/
private class IsFullyDefinedAccumulator(force: ForceDegree.Value)(implicit ctx: Context) extends TypeAccumulator[Boolean] {

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

private[this] var toMaximize: Boolean = false

def apply(x: Boolean, tp: Type): Boolean = tp.dealias match {
case _: WildcardType | _: ProtoType =>
false
case tvar: TypeVar
if !tvar.isInstantiated && ctx.typerState.constraint.contains(tvar) =>
force.appliesTo(tvar) && {
val direction = instDirection(tvar.origin)
if (direction != 0) {
//if (direction > 0) println(s"inst $tvar dir = up")
instantiate(tvar, direction < 0)
}
else {
val minimize =
force.minimizeAll ||
variance >= 0 && !(
!force.allowBottom &&
defn.isBottomType(ctx.typeComparer.approximation(tvar.origin, fromBelow = true)))
if (minimize) instantiate(tvar, fromBelow = true)
else toMaximize = true
}
def avoidBottom =
!force.allowBottom &&
defn.isBottomType(ctx.typeComparer.approximation(tvar.origin, fromBelow = true))
def preferMin = force.minimizeAll || variance >= 0 && !avoidBottom
if (direction != 0) instantiate(tvar, direction < 0)
else if (preferMin) instantiate(tvar, fromBelow = true)
else toMaximize = true
foldOver(x, tvar)
}
case tp =>
Expand Down
28 changes: 1 addition & 27 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2535,32 +2535,6 @@ class Typer extends Namer
}
}

/** If `tp` is a TypeVar which is fully constrained (i.e. its upper bound `hi` conforms
* to its lower bound `lo`), replace `tp` by `hi`. This is necessary to
* keep the right constraints for some implicit search problems. The paradigmatic case
* is `implicitNums.scala`. Without the healing done in `followAlias`, we cannot infer
* implicitly[_3], where _2 is the typelevel number 3. The problem here is that if a
* prototype is, say, Succ[Succ[Zero]], we can infer that it's argument type is Succ[Zero].
* But if the prototype is N? >: Succ[Succ[Zero]] <: Succ[Succ[Zero]], the same
* decomposition does not work - we'd get a N?#M where M is the element type name of Succ
* instead.
*/
def followAlias(tp: Type)(implicit ctx: Context): Type = {
val constraint = ctx.typerState.constraint
def inst(tp: Type): Type = tp match {
case TypeBounds(lo, hi)
if (lo eq hi) || ctx.test(implicit ctx => hi <:< lo) =>
inst(lo)
case tp: TypeParamRef =>
constraint.typeVarOfParam(tp).orElse(tp)
case _ => tp
}
tp match {
case tp: TypeVar if constraint.contains(tp) => inst(constraint.entry(tp.origin))
case _ => tp
}
}

/** Reveal ignored parts of prototype when synthesizing the receiver
* of an extension method. This is necessary for pos/i5773a.scala
*/
Expand Down Expand Up @@ -2809,7 +2783,7 @@ class Typer extends Namer
}

def resultMatches(wtp: Type, pt: Type) =
constrainResult(tree.symbol, wtp, revealProtoOfExtMethod(followAlias(pt)))
constrainResult(tree.symbol, wtp, revealProtoOfExtMethod(pt))

def adaptNoArgs(wtp: Type): Tree = {
val ptNorm = underlyingApplied(pt)
Expand Down
18 changes: 18 additions & 0 deletions tests/pos/i6127.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import reflect.ClassTag
class Co[+S]
object Co {
def empty[X: ClassTag]: Co[X] = ???
}
class Contra[-S]
object Contra {
def empty[X: ClassTag]: Contra[X] = ???
}
class Foo[+FT](x: FT) {
def fooArray: Foo[Array[String]] = new Foo(Array.empty)
val y1: Array[String] = Array.empty
def fooCo: Foo[Co[String]] = new Foo(Co.empty)
val y2: Co[String] = Co.empty
def fooContra: Foo[Contra[String]] = new Foo(Contra.empty)
val y3: Contra[String] = Contra.empty
}