diff options
author | Martin Odersky <odersky@gmail.com> | 2015-06-17 18:00:21 +0200 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2015-06-19 12:58:49 +0200 |
commit | 54835b6fb19ab0758c7503fb6f0e990ee4c25491 (patch) | |
tree | ec0ddb885e7f114aca70cfcb6488eb2bd1d156b7 /src/dotty/tools | |
parent | 1b31f068a1a01619ba2ee2635d5f3c90162bf1d1 (diff) | |
download | dotty-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.scala | 40 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/ProtoTypes.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Typer.scala | 2 |
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, |