Skip to content

Commit

Permalink
Downwards comparisons for implicit search and overloading resolution
Browse files Browse the repository at this point in the history
Compare selected contravariant arguments as if they were covariant.
Which ones is explained in the doc comment for method `isAsSpecificValueType`
in Applications.scala.

This has the same motivation than what @paulp proposed around 2012. The solution is a bit
different from the one proposed then because it only affects top-level parameters.
  • Loading branch information
odersky committed May 23, 2016
1 parent 039e20f commit 8954026
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 5 deletions.
3 changes: 3 additions & 0 deletions src/dotty/tools/dotc/core/Mode.scala
Expand Up @@ -81,5 +81,8 @@ object Mode {
/** We are currently unpickling Scala2 info */
val Scala2Unpickling = newMode(13, "Scala2Unpickling")

/** Use Scala2 scheme for overloading and implicit resolution */
val OldOverloadingResolution = newMode(14, "OldOverloadingResolution")

val PatternOrType = Pattern | Type
}
45 changes: 43 additions & 2 deletions src/dotty/tools/dotc/typer/Applications.scala
Expand Up @@ -915,13 +915,54 @@ trait Applications extends Compatibility { self: Typer =>

{
implicit val ctx: Context = nestedCtx
isCompatible(tp1, constrained(tp2).resultType)
isAsSpecificValueType(tp1, constrained(tp2).resultType)
}
case _ => // (3b)
isCompatible(tp1, tp2)
isAsSpecificValueType(tp1, tp2)
}
}}

/** Test whether value type `tp1` is as specific as value type `tp2`.
* Let's abbreviate this to `tp1 <:s tp2`.
* Previously, `<:s` was the same as `<:`. This behavior is still
* available under mode `Mode.OldOverloadingResolution`. The new behavior
* is different, however. Here, `T <:s U` iff
*
* flip(T) <: flip(U)
*
* where `flip` changes top-level contravariant type aliases to covariant ones.
* Intuitively `<:s` means subtyping `<:`, except that all top-level arguments
* to contravariant parameters are compared as if they were covariant. E.g. given class
*
* class Cmp[-X]
*
* `Cmp[T] <:s Cmp[U]` if `T <: U`. On the other hand, nested occurrences
* of parameters are not affected.
* So `T <: U` would imply `List[Cmp[U]] <:s List[Cmp[T]]`, as usual.
*
* This relation might seem strange, but it models closely what happens for methods.
* Indeed, if we integrate the existing rules for methods into `<:s` we have now that
*
* (T1)T2 <:s (U1)U2
*
* iff
*
* T1 => T2 <:s U1 => U2
*/
def isAsSpecificValueType(tp1: Type, tp2: Type)(implicit ctx: Context) =
if (ctx.mode.is(Mode.OldOverloadingResolution))
isCompatible(tp1, tp2)
else {
val flip = new TypeMap {
def apply(t: Type) = t match {
case t: TypeAlias if variance > 0 && t.variance < 0 => t.derivedTypeAlias(t.alias, 1)
case t: TypeBounds => t
case _ => mapOver(t)
}
}
isCompatible(flip(tp1), flip(tp2))
}

/** Drop any implicit parameter section */
def stripImplicit(tp: Type): Type = tp match {
case mt: ImplicitMethodType if !mt.isDependent =>
Expand Down
14 changes: 13 additions & 1 deletion src/dotty/tools/dotc/typer/Implicits.scala
Expand Up @@ -439,7 +439,19 @@ trait Implicits { self: Typer =>
result
case result: AmbiguousImplicits =>
val deepPt = pt.deepenProto
if (deepPt ne pt) inferImplicit(deepPt, argument, pos) else result
if (deepPt ne pt) inferImplicit(deepPt, argument, pos)
else if (ctx.scala2Mode && !ctx.mode.is(Mode.OldOverloadingResolution)) {
inferImplicit(pt, argument, pos)(ctx.addMode(Mode.OldOverloadingResolution)) match {
case altResult: SearchSuccess =>
ctx.migrationWarning(
s"According to new implicit resolution rules, this will be ambiguous:\n ${result.explanation}",
pos)
altResult
case _ =>
result
}
}
else result
case _ =>
assert(prevConstr eq ctx.typerState.constraint)
result
Expand Down
4 changes: 2 additions & 2 deletions test/dotc/tests.scala
Expand Up @@ -119,8 +119,8 @@ class tests extends CompilerTest {
@Test def neg_typedIdents() = compileDir(negDir, "typedIdents")

val negCustomArgs = negDir + "customArgs/"
@Test def neg_typers = compileFile(negCustomArgs, "typers")(allowDoubleBindings)
@Test def neg_overrideClass = compileFile(negCustomArgs, "overrideClass", List("-language:Scala2"))
@Test def neg_typers() = compileFile(negCustomArgs, "typers")(allowDoubleBindings)
@Test def neg_overrideClass = compileFile(negCustomArgs, "overrideClass", scala2mode)
@Test def neg_autoTupling = compileFile(negCustomArgs, "autoTuplingTest", args = "-language:noAutoTupling" :: Nil)
@Test def neg_i1050 = compileFile(negCustomArgs, "i1050", List("-strict"))
@Test def neg_i1240 = compileFile(negCustomArgs, "i1240")(allowDoubleBindings)
Expand Down
File renamed without changes.

0 comments on commit 8954026

Please sign in to comment.