Skip to content

Commit 340f883

Browse files
committed
Downwards comparisons for implicit search and overloading resolution
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.
1 parent 4aceaaf commit 340f883

File tree

5 files changed

+60
-4
lines changed

5 files changed

+60
-4
lines changed

src/dotty/tools/dotc/core/Mode.scala

+3
Original file line numberDiff line numberDiff line change
@@ -81,5 +81,8 @@ object Mode {
8181
/** We are currently unpickling Scala2 info */
8282
val Scala2Unpickling = newMode(13, "Scala2Unpickling")
8383

84+
/** Use Scala2 scheme for overloading and implicit resolution */
85+
val OldOverloadingResolution = newMode(14, "OldOverloadingResolution")
86+
8487
val PatternOrType = Pattern | Type
8588
}

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

+43-2
Original file line numberDiff line numberDiff line change
@@ -915,13 +915,54 @@ trait Applications extends Compatibility { self: Typer =>
915915

916916
{
917917
implicit val ctx: Context = nestedCtx
918-
isCompatible(tp1, constrained(tp2).resultType)
918+
isAsSpecificValueType(tp1, constrained(tp2).resultType)
919919
}
920920
case _ => // (3b)
921-
isCompatible(tp1, tp2)
921+
isAsSpecificValueType(tp1, tp2)
922922
}
923923
}}
924924

925+
/** Test whether value type `tp1` is as specific as value type `tp2`.
926+
* Let's abbreviate this to `tp1 <:s tp2`.
927+
* Previously, `<:s` was the same as `<:`. This behavior is still
928+
* available under mode `Mode.OldOverloadingResolution`. The new behavior
929+
* is different, however. Here, `T <:s U` iff
930+
*
931+
* flip(T) <: flip(U)
932+
*
933+
* where `flip` changes top-level contravariant type aliases to covariant ones.
934+
* Intuitively `<:s` means subtyping `<:`, except that all top-level arguments
935+
* to contravariant parameters are compared as if they were covariant. E.g. given class
936+
*
937+
* class Cmp[-X]
938+
*
939+
* `Cmp[T] <:s Cmp[U]` if `T <: U`. On the other hand, nested occurrences
940+
* of parameters are not affected.
941+
* So `T <: U` would imply `List[Cmp[U]] <:s List[Cmp[T]]`, as usual.
942+
*
943+
* This relation might seem strange, but it models closely what happens for methods.
944+
* Indeed, if we integrate the existing rules for methods into `<:s` we have now that
945+
*
946+
* (T1)T2 <:s (U1)U2
947+
*
948+
* iff
949+
*
950+
* T1 => T2 <:s U1 => U2
951+
*/
952+
def isAsSpecificValueType(tp1: Type, tp2: Type)(implicit ctx: Context) =
953+
if (ctx.mode.is(Mode.OldOverloadingResolution))
954+
isCompatible(tp1, tp2)
955+
else {
956+
val flip = new TypeMap {
957+
def apply(t: Type) = t match {
958+
case t: TypeAlias if variance > 0 && t.variance < 0 => t.derivedTypeAlias(t.alias, 1)
959+
case t: TypeBounds => t
960+
case _ => mapOver(t)
961+
}
962+
}
963+
isCompatible(flip(tp1), flip(tp2))
964+
}
965+
925966
/** Drop any implicit parameter section */
926967
def stripImplicit(tp: Type): Type = tp match {
927968
case mt: ImplicitMethodType if !mt.isDependent =>

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

+13-1
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,19 @@ trait Implicits { self: Typer =>
439439
result
440440
case result: AmbiguousImplicits =>
441441
val deepPt = pt.deepenProto
442-
if (deepPt ne pt) inferImplicit(deepPt, argument, pos) else result
442+
if (deepPt ne pt) inferImplicit(deepPt, argument, pos)
443+
else if (ctx.scala2Mode && !ctx.mode.is(Mode.OldOverloadingResolution)) {
444+
inferImplicit(pt, argument, pos)(ctx.addMode(Mode.OldOverloadingResolution)) match {
445+
case altResult: SearchSuccess =>
446+
ctx.migrationWarning(
447+
s"According to new implicit resolution rules, this will be ambiguous:\n ${result.explanation}",
448+
pos)
449+
altResult
450+
case _ =>
451+
result
452+
}
453+
}
454+
else result
443455
case _ =>
444456
assert(prevConstr eq ctx.typerState.constraint)
445457
result

test/dotc/tests.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ class tests extends CompilerTest {
120120

121121
val negCustomArgs = negDir + "customArgs/"
122122
@Test def neg_typers() = compileFile(negCustomArgs, "typers")(allowDoubleBindings)
123-
@Test def neg_overrideClass = compileFile(negCustomArgs, "overrideClass", List("-language:Scala2"))
123+
@Test def neg_overrideClass = compileFile(negCustomArgs, "overrideClass", scala2mode)
124124
@Test def neg_autoTupling = compileFile(negCustomArgs, "autoTuplingTest", args = "-language:noAutoTupling" :: Nil)
125125
@Test def neg_i1050 = compileFile(negCustomArgs, "i1050", List("-strict"))
126126

File renamed without changes.

0 commit comments

Comments
 (0)