aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2015-06-17 18:00:21 +0200
committerMartin Odersky <odersky@gmail.com>2015-06-19 12:58:49 +0200
commit54835b6fb19ab0758c7503fb6f0e990ee4c25491 (patch)
treeec0ddb885e7f114aca70cfcb6488eb2bd1d156b7 /src/dotty/tools
parent1b31f068a1a01619ba2ee2635d5f3c90162bf1d1 (diff)
downloaddotty-54835b6fb19ab0758c7503fb6f0e990ee4c25491.tar.gz
dotty-54835b6fb19ab0758c7503fb6f0e990ee4c25491.tar.bz2
dotty-54835b6fb19ab0758c7503fb6f0e990ee4c25491.zip
Take expected result type into account more often for overloading resolution
Previously, the expected result type of a FunProto type was ignored and taken into account only in case of ambiguities. arrayclone-new.scala shows that this is not enough. In a case like val x: Array[Byte] = Array(1, 2) we typed 1, 2 to be Int, so overloading resulution would give the Array.apply of type (Int, Int*)Array[Int]. But that's a dead end, since Array[Int] is not a subtype of Array[Byte]. This commit proposes the following modified rule for overloading resulution: A method alternative is applicable if ... (as before), and if its result type is copmpatible with the expected type of the method application. The commit does not pre-select alternatives based on comparing with the expected result type. I tried that but it slowed down typechecking by a factor of at least 4. Instead, we proceed as usual, ignoring the result type except in case of ambiguities, but check whether the result of overloading resolution has a compatible result type. If that's not the case, we filter all alternatives for result type compatibility and try again.
Diffstat (limited to 'src/dotty/tools')
-rw-r--r--src/dotty/tools/dotc/typer/Applications.scala40
-rw-r--r--src/dotty/tools/dotc/typer/ProtoTypes.scala2
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala2
3 files changed, 42 insertions, 2 deletions
diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala
index 9e9acf97a..c162abd5f 100644
--- a/src/dotty/tools/dotc/typer/Applications.scala
+++ b/src/dotty/tools/dotc/typer/Applications.scala
@@ -1039,6 +1039,46 @@ trait Applications extends Compatibility { self: Typer =>
}
}
+ /** If the `chosen` alternative has a result type incompatible with the expected result
+ * type `pt`, run overloading resolution again on all alternatives that do match `pt`.
+ * If the latter succeeds with a single alternative, return it, otherwise
+ * fallback to `chosen`.
+ */
+ def adaptByResult(alts: List[TermRef], chosen: TermRef, pt: Type)(implicit ctx: Context) =
+ if (ctx.isAfterTyper) chosen
+ else {
+ def nestedCtx = ctx.fresh.setExploreTyperState
+ pt match {
+ case pt: FunProto if !resultConforms(chosen, pt.resultType)(nestedCtx) =>
+ alts.filter(alt =>
+ (alt ne chosen) && resultConforms(alt, pt.resultType)(nestedCtx)) match {
+ case Nil => chosen
+ case alt2 :: Nil => alt2
+ case alts2 =>
+ resolveOverloaded(alts2, pt) match {
+ case alt2 :: Nil => alt2
+ case _ => chosen
+ }
+ }
+ case _ => chosen
+ }
+ }
+
+ /** Is `alt` a method or polytype whose result type after the first value parameter
+ * section conforms to the expected type `resultType`? If `resultType`
+ * is a `IgnoredProto`, pick the underlying type instead.
+ */
+ private def resultConforms(alt: Type, resultType: Type)(implicit ctx: Context): Boolean = resultType match {
+ case IgnoredProto(ignored) => resultConforms(alt, ignored)
+ case _: ValueType =>
+ alt.widen match {
+ case tp: PolyType => resultConforms(constrained(tp).resultType, resultType)
+ case tp: MethodType => constrainResult(tp.resultType, resultType)
+ case _ => true
+ }
+ case _ => true
+ }
+
private def harmonizeWith[T <: AnyRef](ts: List[T])(tpe: T => Type, adapt: (T, Type) => T)(implicit ctx: Context): List[T] = {
def numericClasses(ts: List[T], acc: Set[Symbol]): Set[Symbol] = ts match {
case t :: ts1 =>
diff --git a/src/dotty/tools/dotc/typer/ProtoTypes.scala b/src/dotty/tools/dotc/typer/ProtoTypes.scala
index c7efe45b7..9a012c30e 100644
--- a/src/dotty/tools/dotc/typer/ProtoTypes.scala
+++ b/src/dotty/tools/dotc/typer/ProtoTypes.scala
@@ -46,7 +46,7 @@ object ProtoTypes {
* fits the given expected result type.
*/
def constrainResult(mt: Type, pt: Type)(implicit ctx: Context): Boolean = pt match {
- case _: FunProto =>
+ case pt: FunProto =>
mt match {
case mt: MethodType =>
mt.isDependent || constrainResult(mt.resultType, pt.resultType)
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index 2bdd0d197..ce0a181c3 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -1238,7 +1238,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
def expectedStr = err.expectedTypeStr(pt)
resolveOverloaded(alts, pt) match {
case alt :: Nil =>
- adapt(tree.withType(alt), pt, original)
+ adapt(tree.withType(adaptByResult(alts, alt, pt)), pt, original)
case Nil =>
def noMatches =
errorTree(tree,