Skip to content

Commit a7ef3e2

Browse files
authored
Merge pull request #8822 from dotty-staging/fix-#8736
Fix #8736: Handle alternatives with same signature in overloading resolution
2 parents d436677 + a3decf6 commit a7ef3e2

File tree

6 files changed

+82
-17
lines changed

6 files changed

+82
-17
lines changed

compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import printing.Formatting
1515
import ErrorMessageID._
1616
import ast.Trees
1717
import config.{Feature, ScalaVersion}
18-
import typer.ErrorReporting.{Errors, err}
18+
import typer.ErrorReporting.err
1919
import typer.ProtoTypes.ViewProto
2020
import scala.util.control.NonFatal
2121
import StdNames.nme
@@ -1308,14 +1308,13 @@ object messages {
13081308

13091309
}
13101310

1311-
class AmbiguousOverload(tree: tpd.Tree, val alternatives: List[SingleDenotation], pt: Type)(
1312-
err: Errors)(
1311+
class AmbiguousOverload(tree: tpd.Tree, val alternatives: List[SingleDenotation], pt: Type, addendum: String = "")(
13131312
implicit ctx: Context)
13141313
extends ReferenceMsg(AmbiguousOverloadID) {
13151314
private def all = if (alternatives.length == 2) "both" else "all"
13161315
def msg =
1317-
s"""|Ambiguous overload. The ${err.overloadedAltsStr(alternatives)}
1318-
|$all match ${err.expectedTypeStr(pt)}""".stripMargin
1316+
em"""|Ambiguous overload. The ${err.overloadedAltsStr(alternatives)}
1317+
|$all match ${err.expectedTypeStr(pt)}$addendum""".stripMargin
13191318
def explain =
13201319
em"""|There are ${alternatives.length} methods that could be referenced as the compiler knows too little
13211320
|about the expected type.
@@ -1389,8 +1388,7 @@ object messages {
13891388
def explain = em"A fully applied type is expected but $tpe takes $numParams $parameters"
13901389
}
13911390

1392-
class DoesNotConformToBound(tpe: Type, which: String, bound: Type)(
1393-
err: Errors)(implicit ctx: Context)
1391+
class DoesNotConformToBound(tpe: Type, which: String, bound: Type)(implicit ctx: Context)
13941392
extends TypeMismatchMsg(DoesNotConformToBoundID) {
13951393
def msg = em"Type argument ${tpe} does not conform to $which bound $bound${err.whyNoMatchStr(tpe, bound)}"
13961394
def explain = ""
@@ -2141,8 +2139,7 @@ object messages {
21412139
|Refinements cannot contain overloaded definitions.""".stripMargin
21422140
}
21432141

2144-
class NoMatchingOverload(val alternatives: List[SingleDenotation], pt: Type)(
2145-
err: Errors)(using ctx: Context)
2142+
class NoMatchingOverload(val alternatives: List[SingleDenotation], pt: Type)(using ctx: Context)
21462143
extends TypeMismatchMsg(NoMatchingOverloadID) {
21472144
def msg =
21482145
em"""None of the ${err.overloadedAltsStr(alternatives)}

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1370,7 +1370,6 @@ trait Applications extends Compatibility {
13701370
*/
13711371
def compare(alt1: TermRef, alt2: TermRef)(using Context): Int = trace(i"compare($alt1, $alt2)", overload) {
13721372
record("compare")
1373-
assert(alt1 ne alt2)
13741373

13751374
/** Is alternative `alt1` with type `tp1` as specific as alternative
13761375
* `alt2` with type `tp2` ?

compiler/src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ object Checking {
7171
}
7272
for (arg, which, bound) <- ctx.boundsViolations(args, boundss, instantiate, app) do
7373
ctx.error(
74-
showInferred(DoesNotConformToBound(arg.tpe, which, bound)(err),
74+
showInferred(DoesNotConformToBound(arg.tpe, which, bound),
7575
app, tpt),
7676
arg.sourcePos.focus)
7777

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2796,8 +2796,9 @@ class Typer extends Namer
27962796

27972797
def adaptOverloaded(ref: TermRef) = {
27982798
val altDenots = ref.denot.alternatives
2799-
typr.println(i"adapt overloaded $ref with alternatives ${altDenots map (_.info)}%, %")
2800-
val alts = altDenots.map(TermRef(ref.prefix, ref.name, _))
2799+
typr.println(i"adapt overloaded $ref with alternatives ${altDenots map (_.info)}%\n\n %")
2800+
def altRef(alt: SingleDenotation) = TermRef(ref.prefix, ref.name, alt)
2801+
val alts = altDenots.map(altRef)
28012802
resolveOverloaded(alts, pt) match {
28022803
case alt :: Nil =>
28032804
readaptSimplified(tree.withType(alt))
@@ -2807,7 +2808,7 @@ class Typer extends Namer
28072808
// 2. If context is not an application, pick a alternative that does
28082809
// not take parameters.
28092810
def noMatches =
2810-
errorTree(tree, NoMatchingOverload(altDenots, pt)(err))
2811+
errorTree(tree, NoMatchingOverload(altDenots, pt))
28112812
def hasEmptyParams(denot: SingleDenotation) = denot.info.paramInfoss == ListOfNil
28122813
pt match {
28132814
case pt: FunOrPolyProto if !pt.isUsingApply =>
@@ -2823,11 +2824,20 @@ class Typer extends Namer
28232824
noMatches
28242825
}
28252826
}
2826-
case alts =>
2827+
case ambiAlts =>
28272828
if (tree.tpe.isErroneous || pt.isErroneous) tree.withType(UnspecifiedErrorType)
28282829
else {
2829-
val remainingDenots = alts map (_.denot.asInstanceOf[SingleDenotation])
2830-
errorTree(tree, AmbiguousOverload(tree, remainingDenots, pt)(err))
2830+
val remainingDenots = altDenots.filter(denot => ambiAlts.contains(altRef(denot)))
2831+
val addendum =
2832+
if ambiAlts.toSet.size != ambiAlts.size then
2833+
// Several variants have the same signature. This can happen for structural
2834+
// type selections. See i8736.scala
2835+
"""|
2836+
|
2837+
|Note: this happens because two or more alternatives have the same erasure,
2838+
| so they cannot be distinguished by overloading resolution""".stripMargin
2839+
else ""
2840+
errorTree(tree, AmbiguousOverload(tree, remainingDenots, pt, addendum))
28312841
}
28322842
}
28332843
}

tests/neg/i8736.check

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
-- [E007] Type Mismatch Error: tests/neg/i8736.scala:29:29 -------------------------------------------------------------
2+
29 | def res1: String = rec.get("k") // error: type mismatch
3+
| ^^^^^^^^^^^^
4+
| Found: Any
5+
| Required: String
6+
-- [E007] Type Mismatch Error: tests/neg/i8736.scala:30:29 -------------------------------------------------------------
7+
30 | def res2: Int = rec.get("v") // error: type mismatch
8+
| ^^^^^^^^^^^^
9+
| Found: Any
10+
| Required: Int
11+
-- [E051] Reference Error: tests/neg/i8736.scala:31:26 -----------------------------------------------------------------
12+
31 | def res3: Boolean = rec.get("z") // error: ambiguous
13+
| ^^^^^^^
14+
| Ambiguous overload. The overloaded alternatives of method (k: ("k" : String)): String with types
15+
| (k: ("k" : String)): String
16+
| (k: ("v" : String)): Int
17+
| (k: ("z" : String)): Boolean
18+
| all match arguments (("z" : String))
19+
|
20+
| Note: this happens because two or more alternatives have the same erasure,
21+
| so they cannot be distinguished by overloading resolution
22+
23+
longer explanation available when compiling with `-explain`

tests/neg/i8736.scala

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import reflect.Selectable.reflectiveSelectable
2+
object App extends App {
3+
4+
trait Rec0[K <: String] {
5+
private[App] val map: Map[String, Any]
6+
def get(k: K): Any
7+
}
8+
def Rec0(map0: Map[String, Any]) = new Rec0[String] {
9+
val map = map0
10+
def get(k: String): Any = map(k)
11+
}
12+
13+
type Rec[K <: String, V0] = Rec0[K] { def get(k: K): V0 }
14+
def field[V](s: String)(v: V): Rec[s.type, V] = Rec0(Map(s -> v)).asInstanceOf[Rec[s.type, V]]
15+
16+
implicit class RecOps[R <: Rec0[_]](has: R) {
17+
def +[K1 <: String, V1](that: Rec[K1, V1]): R with Rec[K1, V1] = Rec0(has.map ++ that.map).asInstanceOf[R with Rec[K1, V1]]
18+
}
19+
20+
def rec:
21+
Rec["k", String]
22+
with Rec["v", Int]
23+
with Rec["z", Boolean]
24+
= {
25+
field("k")("Str") +
26+
field("v")(0) +
27+
field("z")(true)
28+
}
29+
def res1: String = rec.get("k") // error: type mismatch
30+
def res2: Int = rec.get("v") // error: type mismatch
31+
def res3: Boolean = rec.get("z") // error: ambiguous
32+
33+
// def res4: Boolean = rec.get("nofield")
34+
35+
println((res1, res2, res3))
36+
}

0 commit comments

Comments
 (0)