Skip to content

Commit

Permalink
Improve error messages for overloading errors
Browse files Browse the repository at this point in the history
We need to show argument lists after the first one if they were
used for overloading resolution.
  • Loading branch information
odersky committed Sep 12, 2022
1 parent 903e6e5 commit aba158c
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 17 deletions.
33 changes: 23 additions & 10 deletions compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,28 @@ object ErrorReporting {
"\n(Note that variables need to be initialized to be defined)"
else ""

/** Reveal arguments in FunProtos that are proteted by an IgnoredProto but were
* revealed during type inference. This gives clearer error messages for overloading
* resolution errors that need to show argument lists after the first. We do not
* reveal other kinds of ignored prototypes since these might be misleading because
* there might be a possible implicit conversion on the result.
*/
def revealDeepenedArgs(tp: Type): Type = tp match
case tp @ IgnoredProto(deepTp: FunProto) if tp.wasDeepened => deepTp
case _ => tp

def expectedTypeStr(tp: Type): String = tp match {
case tp: PolyProto =>
em"type arguments [${tp.targs.tpes}%, %] and ${expectedTypeStr(tp.resultType)}"
em"type arguments [${tp.targs.tpes}%, %] and ${expectedTypeStr(revealDeepenedArgs(tp.resultType))}"
case tp: FunProto =>
val result = tp.resultType match {
case _: WildcardType | _: IgnoredProto => ""
case tp => em" and expected result type $tp"
}
em"arguments (${tp.typedArgs().tpes}%, %)$result"
def argStr(tp: FunProto): String =
val result = revealDeepenedArgs(tp.resultType) match {
case restp: FunProto => argStr(restp)
case _: WildcardType | _: IgnoredProto => ""
case tp => em" and expected result type $tp"
}
em"(${tp.typedArgs().tpes}%, %)$result"
s"arguments ${argStr(tp)}"
case _ =>
em"expected type $tp"
}
Expand Down Expand Up @@ -125,25 +138,25 @@ object ErrorReporting {
def typeMismatch(tree: Tree, pt: Type, implicitFailure: SearchFailureType = NoMatchingImplicits): Tree = {
val normTp = normalize(tree.tpe, pt)
val normPt = normalize(pt, pt)

def contextFunctionCount(tp: Type): Int = tp.stripped match
case defn.ContextFunctionType(_, restp, _) => 1 + contextFunctionCount(restp)
case _ => 0
def strippedTpCount = contextFunctionCount(tree.tpe) - contextFunctionCount(normTp)
def strippedPtCount = contextFunctionCount(pt) - contextFunctionCount(normPt)

val (treeTp, expectedTp) =
if normTp <:< normPt || strippedTpCount != strippedPtCount
then (tree.tpe, pt)
else (normTp, normPt)
// use normalized types if that also shows an error, and both sides stripped
// the same number of context functions. Use original types otherwise.

def missingElse = tree match
case If(_, _, elsep @ Literal(Constant(()))) if elsep.span.isSynthetic =>
"\nMaybe you are missing an else part for the conditional?"
case _ => ""

errorTree(tree, TypeMismatch(treeTp, expectedTp, Some(tree), implicitFailure.whyNoConversion, missingElse))
}

Expand Down
10 changes: 9 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,18 @@ object ProtoTypes {

/** A class marking ignored prototypes that can be revealed by `deepenProto` */
abstract case class IgnoredProto(ignored: Type) extends CachedGroundType with MatchAlways:
private var myWasDeepened = false
override def revealIgnored = ignored
override def deepenProto(using Context): Type = ignored
override def deepenProto(using Context): Type =
myWasDeepened = true
ignored
override def deepenProtoTrans(using Context): Type = ignored.deepenProtoTrans

/** Did someone look inside via deepenProto? Used for error deagniostics
* to give a more extensive expected type.
*/
def wasDeepened: Boolean = myWasDeepened

override def computeHash(bs: Hashable.Binders): Int = doHash(bs, ignored)

override def eql(that: Type): Boolean = that match
Expand Down
4 changes: 2 additions & 2 deletions tests/neg/i10901.check
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
| (x: T1)
| (y: BugExp4Point2D.ColumnType[T2])
| (implicit evidence$5: Numeric[T1], evidence$6: Numeric[T2]): BugExp4Point2D.Point2D[T1, T2]
| both match arguments ((x : BugExp4Point2D.IntT.type))
| both match arguments ((x : BugExp4Point2D.IntT.type))((y : BugExp4Point2D.DoubleT.type))
-- [E008] Not Found Error: tests/neg/i10901.scala:48:38 ----------------------------------------------------------------
48 | val pos4: Point2D[Int,Double] = x º 201.1 // error
| ^^^
Expand All @@ -29,7 +29,7 @@
| (x: BugExp4Point2D.ColumnType[T1])
| (y: T2)(implicit evidence$9: Numeric[T1], evidence$10: Numeric[T2]): BugExp4Point2D.Point2D[T1, T2]
| [T1, T2](x: T1)(y: T2)(implicit evidence$3: Numeric[T1], evidence$4: Numeric[T2]): BugExp4Point2D.Point2D[T1, T2]
| both match arguments ((x : BugExp4Point2D.IntT.type))
| both match arguments ((x : BugExp4Point2D.IntT.type))((201.1d : Double))
-- [E008] Not Found Error: tests/neg/i10901.scala:62:16 ----------------------------------------------------------------
62 | val y = "abc".foo // error
| ^^^^^^^^^
Expand Down
2 changes: 1 addition & 1 deletion tests/neg/i15000.check
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@
| Ambiguous overload. The overloaded alternatives of method apply in object ExtensionMethodReproduction with types
| (c: ExtensionMethodReproduction.C)(x: Int, y: Int): String
| (c: ExtensionMethodReproduction.C)(x: Int, y: String): String
| both match arguments (ExtensionMethodReproduction.c.type)
| both match arguments (ExtensionMethodReproduction.c.type)((ExtensionMethodReproduction.x : Int), <error Not found: barY>)
7 changes: 7 additions & 0 deletions tests/neg/i15287.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
-- [E134] Type Error: tests/neg/i15287.scala:4:19 ----------------------------------------------------------------------
4 |@main def Test() = f('c')(2) // error
| ^
| None of the overloaded alternatives of method f with types
| (x: Char)(min: Boolean, max: Int): String
| (x: Char)(someParam: String): String
| match arguments (('c' : Char))((2 : Int))
4 changes: 4 additions & 0 deletions tests/neg/i15287.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
def f(x: Char)(someParam: String): String = "a"
def f(x: Char)(min: Boolean, max: Int = 4): String = "b"

@main def Test() = f('c')(2) // error
11 changes: 8 additions & 3 deletions tests/run/i15287.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
def f(x: Char)(someParam: String): String = "a"
def f(x: Char)(min: Boolean, max: Int = 4): String = "b"
extension (x: Char)
def f(someParam: String): String = "a"
def f(min: Boolean, max: Int = 4): String = "b"

@main def Test() =
f('c')(false)
'c'.f(true)
'c'.f("a")

@main def Test() = f('c')(false)

0 comments on commit aba158c

Please # to comment.