diff options
author | Martin Odersky <odersky@gmail.com> | 2015-06-25 09:31:34 +0200 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2015-06-25 09:37:13 +0200 |
commit | f3f75cad600d616db43138b09b01fcea93affd58 (patch) | |
tree | 89f2625d5cfa853b3ac7d272a2d0c032dd3b8898 | |
parent | bb90c8457ab91e3c4cd707fa1a68e75e6dd96128 (diff) | |
download | dotty-f3f75cad600d616db43138b09b01fcea93affd58.tar.gz dotty-f3f75cad600d616db43138b09b01fcea93affd58.tar.bz2 dotty-f3f75cad600d616db43138b09b01fcea93affd58.zip |
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.
-rw-r--r-- | src/dotty/tools/dotc/typer/Typer.scala | 28 | ||||
-rw-r--r-- | tests/pos/implicitNums.scala | 15 |
2 files changed, 42 insertions, 1 deletions
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index af6adb6a6..02b740dd3 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -1279,10 +1279,36 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } } + /** 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) || (hi <:< lo)(ctx.fresh.setExploreTyperState)) inst(lo) else NoType + case tp: PolyParam => + var tvar1 = constraint.typeVarOfParam(tp) + if (tvar1.exists) tvar1 else tp + case _ => tp + } + tp match { + case tp: TypeVar if constraint.contains(tp) => inst(constraint.entry(tp.origin)) + case _ => tp + } + } + def adaptNoArgs(wtp: Type): Tree = wtp match { case wtp: ExprType => adaptInterpolated(tree.withType(wtp.resultType), pt, original) - case wtp: ImplicitMethodType if constrainResult(wtp, pt) => + case wtp: ImplicitMethodType if constrainResult(wtp, followAlias(pt)) => val constr = ctx.typerState.constraint def addImplicitArgs = { def implicitArgError(msg: => String): Tree = { diff --git a/tests/pos/implicitNums.scala b/tests/pos/implicitNums.scala new file mode 100644 index 000000000..48c5302f7 --- /dev/null +++ b/tests/pos/implicitNums.scala @@ -0,0 +1,15 @@ +object Test { + + trait Number + trait Zero extends Number + trait Succ[N <: Number](n: N) extends Number + + implicit def succ[N <: Number](implicit n: N): Succ[N] = new Succ[N](n) {} + implicit def zero: Zero = new Zero{} + + implicitly[Zero] + implicitly[Succ[Zero]] + implicitly[Succ[Succ[Zero]]] + implicitly[Succ[Succ[Succ[Zero]]]] + +} |