Skip to content

Commit f3f75ca

Browse files
committed
Sharpen prototypes of implicit methods.
Necessary to make implicit resolution of type-level peano numbers work. The current commit makes takes the inimal steps to make this happen. We could also consider sharpening using followAlias every type we constrain a result, or every time we adapt a type.
1 parent bb90c84 commit f3f75ca

File tree

2 files changed

+42
-1
lines changed

2 files changed

+42
-1
lines changed

src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1279,10 +1279,36 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
12791279
}
12801280
}
12811281

1282+
/** If `tp` is a TypeVar which is fully constrained (i.e. its upper bound `hi` conforms
1283+
* to its lower bound `lo`), replace `tp` by `hi`. This is necessary to
1284+
* keep the right constraints for some implicit search problems. The paradigmatic case
1285+
* is `implicitNums.scala`. Without the healing done in `followAlias`, we cannot infer
1286+
* implicitly[_3], where _2 is the typelevel number 3. The problem here is that if a
1287+
* prototype is, say, Succ[Succ[Zero]], we can infer that it's argument type is Succ[Zero].
1288+
* But if the prototype is N? >: Succ[Succ[Zero]] <: Succ[Succ[Zero]], the same
1289+
* decomposition does not work - we'd get a N?#M where M is the element type name of Succ
1290+
* instead.
1291+
*/
1292+
def followAlias(tp: Type)(implicit ctx: Context): Type = {
1293+
val constraint = ctx.typerState.constraint
1294+
def inst(tp: Type): Type = tp match {
1295+
case TypeBounds(lo, hi) =>
1296+
if ((lo eq hi) || (hi <:< lo)(ctx.fresh.setExploreTyperState)) inst(lo) else NoType
1297+
case tp: PolyParam =>
1298+
var tvar1 = constraint.typeVarOfParam(tp)
1299+
if (tvar1.exists) tvar1 else tp
1300+
case _ => tp
1301+
}
1302+
tp match {
1303+
case tp: TypeVar if constraint.contains(tp) => inst(constraint.entry(tp.origin))
1304+
case _ => tp
1305+
}
1306+
}
1307+
12821308
def adaptNoArgs(wtp: Type): Tree = wtp match {
12831309
case wtp: ExprType =>
12841310
adaptInterpolated(tree.withType(wtp.resultType), pt, original)
1285-
case wtp: ImplicitMethodType if constrainResult(wtp, pt) =>
1311+
case wtp: ImplicitMethodType if constrainResult(wtp, followAlias(pt)) =>
12861312
val constr = ctx.typerState.constraint
12871313
def addImplicitArgs = {
12881314
def implicitArgError(msg: => String): Tree = {

tests/pos/implicitNums.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
object Test {
2+
3+
trait Number
4+
trait Zero extends Number
5+
trait Succ[N <: Number](n: N) extends Number
6+
7+
implicit def succ[N <: Number](implicit n: N): Succ[N] = new Succ[N](n) {}
8+
implicit def zero: Zero = new Zero{}
9+
10+
implicitly[Zero]
11+
implicitly[Succ[Zero]]
12+
implicitly[Succ[Succ[Zero]]]
13+
implicitly[Succ[Succ[Succ[Zero]]]]
14+
15+
}

0 commit comments

Comments
 (0)