From fd31ee69b07b80cf1b04cee9d43301b470ded87a Mon Sep 17 00:00:00 2001 From: Anton Sviridov Date: Sat, 9 Jul 2022 15:43:57 +0100 Subject: [PATCH 1/7] Improve rendering of "none of overloaded alternatives" error --- .../tools/dotc/typer/ErrorReporting.scala | 91 ++++++++++++++++++- .../dotty/tools/dotc/util/MarginString.scala | 81 +++++++++++++++++ sandbox/scalajs/src/hello.scala | 67 +++++++++++--- 3 files changed, 225 insertions(+), 14 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/util/MarginString.scala diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index 215fe9bfa687..617add252233 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -15,6 +15,9 @@ import reporting._ import collection.mutable import scala.util.matching.Regex +import dotty.tools.dotc.core.Names.TypeName +import dotty.tools.dotc.util.MarginString +import dotty.tools.dotc.util.ChunkJoiner object ErrorReporting { @@ -91,9 +94,93 @@ object ErrorReporting { em"$kind $tpe" } - def overloadedAltsStr(alts: List[SingleDenotation]): String = + def overloadedAltsStr(alts: List[SingleDenotation])(using ctx: Context): String = { + val denots = Vector.newBuilder[String] + val fullNames = collection.mutable.Map.empty[Type, String] + val shortNames = collection.mutable.Map.empty[Type, String] + + inline def getShortName(tpe: Type) = + shortNames.getOrElseUpdate(tpe, { + val short = tpe match + case n: NamedType => + n.name.lastPart.show + case _ => tpe.show + + val alreadyPresent = shortNames.count((t, str) => str == short && t != tpe) + + alreadyPresent match + case 0 => short + case 1 => short + "¹" + case 2 => short + "²" + case 3 => short + "³" + case _ => tpe.show + } + ) + + end getShortName + + inline def renderParamList(names: List[String], types: List[Type], maxLength: Int = 40) = + val params = names.zip(types).map {case (name, tpe) => + fullNames.getOrElseUpdate(tpe, tpe.show) + MarginString.Chunk(s"$name: ${getShortName(tpe)}") + } + + MarginString.Group( + params.toVector, + ChunkJoiner.rightBreakable(", "), + prefix = Some(ChunkJoiner.rightBreakable("(")), + suffix = Some(ChunkJoiner(")")) + ) + end renderParamList + + alts.foreach { alt => + val info = alt.info.stripPoly + val name = alt.name.show + + val paramNamess = info.paramNamess.map(_.map(_.show)) + val paramTypes = info.paramInfoss + + val methodPrefix = " " + name + + val paramLists = + paramNamess.zip(paramTypes).map(t => + renderParamList(t._1, t._2) + ).toVector + + val lines = MarginString.Group( + paramLists, + ChunkJoiner.nonBreakable("") + ).lines(40).map(MarginString.Chunk.apply) + + MarginString.Group( + lines, + ChunkJoiner.nonBreakable("\n " + (" " * methodPrefix.size)), + prefix = Some(ChunkJoiner.nonBreakable(methodPrefix)) + ) + .lines(0) + .foreach(denots += _) + + } + + val allMentionedTypes = fullNames.toList.sortBy(_._2).map(_._1) + val maxShortLength = shortNames.maxByOption(_._2.length).map(_._2.length).getOrElse(0) + + denots += "" + denots += "where" + + import printing.Highlighting.* + + allMentionedTypes.foreach {tpe => + val shortName = shortNames(tpe) + val longName = fullNames(tpe) + if shortName != longName then + val paddedName = shortNames(tpe).padTo(maxShortLength, ' ') + denots += s" ${Cyan(paddedName).show} is $longName" + } + em"overloaded alternatives of ${denotStr(alts.head)} with types\n" + - em" ${alts map (_.info)}%\n %" + denots.result().mkString("\n") + } def denotStr(denot: Denotation): String = if (denot.isOverloaded) overloadedAltsStr(denot.alternatives) diff --git a/compiler/src/dotty/tools/dotc/util/MarginString.scala b/compiler/src/dotty/tools/dotc/util/MarginString.scala new file mode 100644 index 000000000000..1dc59288c77a --- /dev/null +++ b/compiler/src/dotty/tools/dotc/util/MarginString.scala @@ -0,0 +1,81 @@ +package dotty.tools.dotc.util + + +case class ChunkJoiner private (s: String, breakLeft: Boolean, breakRight: Boolean) +object ChunkJoiner: + def apply(s: String) = new ChunkJoiner(s, true, true) + def nonBreakable(s: String) = new ChunkJoiner(s, false, false) + def leftBreakable(s: String) = new ChunkJoiner(s, breakLeft = true, false) + def rightBreakable(s: String) = new ChunkJoiner(s, false, breakRight = true) + +enum MarginString: + case Chunk(s: String) + case Group(strings: Vector[MarginString], + joiner: ChunkJoiner, + prefix: Option[ChunkJoiner] = None, + suffix: Option[ChunkJoiner] = None) + + def lines(maxWidth: Int) = MarginString.lines(this, maxWidth) + +object MarginString: + private case object Breakpoint + + private def tokens(str: MarginString): Vector[String | Breakpoint.type] = + def go(ms: MarginString): Vector[String | Breakpoint.type] = + ms match + case Chunk(s) => Vector(s) + case g: Group => + import g.* + val b = Vector.newBuilder[String | Breakpoint.type] + + def renderJoiner(joiner: ChunkJoiner) = + if joiner.breakLeft then + b += Breakpoint + b += joiner.s + if joiner.breakRight then + b += Breakpoint + + + prefix.foreach(renderJoiner) + + strings.zipWithIndex.foreach {case (c, idx) => + b ++= go(c) + if idx != strings.size - 1 then renderJoiner(joiner) + } + + suffix.foreach(renderJoiner) + + b.result() + go(str) + end tokens + + def lines(str: MarginString, maxLength: Int): Vector[String] = + val nonBreakable = { + val chunks = List.newBuilder[String] + val sb = StringBuilder() + + tokens(str).foreach { + case str: String => sb.append(str) + case Breakpoint => + chunks += sb.result() + sb.clear() + } + + if sb.nonEmpty then chunks += sb.result() + + chunks.result() + } + + val lineBreaks = Vector.newBuilder[String] + val curLine = StringBuilder() + + nonBreakable.foreach { str => + if curLine.size >= maxLength then + lineBreaks += curLine.result() + curLine.clear() + + curLine.append(str) + } + if curLine.nonEmpty then lineBreaks += curLine.result() + + lineBreaks.result() diff --git a/sandbox/scalajs/src/hello.scala b/sandbox/scalajs/src/hello.scala index 5a2b6a2952c7..0d36f6ed91c1 100644 --- a/sandbox/scalajs/src/hello.scala +++ b/sandbox/scalajs/src/hello.scala @@ -1,15 +1,58 @@ -package hello +// package hello -import scala.scalajs.js +// import scala.scalajs.js -trait MyTrait { - val x = 5 - def foo(y: Int) = x -} +// trait MyTrait { +// val x = 5 +// def foo(y: Int) = x +// } -object HelloWorld extends MyTrait { - def main(args: Array[String]): Unit = { - println("hello dotty.js!") - println(foo(4)) - } -} +// object HelloWorld extends MyTrait { +// def main(args: Array[String]): Unit = { +// println("hello dotty.js!") +// println(foo(4)) +// } +// } + +object hello: + trait World + +object bye: + trait World + +object bla: + object longname: + object otherlongname: + trait HelloWorldFactory +import bla.longname.otherlongname.HelloWorldFactory + +def method( + p1: hello.World, + p2: bye.World, + p3: hello.World, + p4: HelloWorldFactory, + p5: HelloWorldFactory, + p6: HelloWorldFactory, + p7: HelloWorldFactory +) = 1 + +def method( + p1: hello.World, + p1_1: HelloWorldFactory, + p2: hello.World, + p3: hello.World, + p4: HelloWorldFactory +) = 1 + +def method( + p1: String, + p1_1: HelloWorldFactory, + p2: hello.World, + p3: hello.World, + p4: HelloWorldFactory +) = 1 + + +@main def test = + import hello.World + method(new World, new World, 25, new World, new HelloWorldFactory) From 07a8e5bd9c022d16c34cb0968815264ac953338c Mon Sep 17 00:00:00 2001 From: Anton Sviridov Date: Sat, 9 Jul 2022 19:34:41 +0100 Subject: [PATCH 2/7] Handle generics (sort of) --- .../tools/dotc/typer/ErrorReporting.scala | 58 +++++--------- .../dotty/tools/dotc/util/TypeRegistry.scala | 79 +++++++++++++++++++ sandbox/scalajs/src/hello.scala | 66 +++------------- tests/neg/overload-error-message.check | 28 +++++++ tests/neg/overload-error-message.scala | 47 +++++++++++ 5 files changed, 187 insertions(+), 91 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/util/TypeRegistry.scala create mode 100644 tests/neg/overload-error-message.check create mode 100644 tests/neg/overload-error-message.scala diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index 617add252233..0d8e3b7671f6 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -96,47 +96,26 @@ object ErrorReporting { def overloadedAltsStr(alts: List[SingleDenotation])(using ctx: Context): String = { val denots = Vector.newBuilder[String] - val fullNames = collection.mutable.Map.empty[Type, String] - val shortNames = collection.mutable.Map.empty[Type, String] - - inline def getShortName(tpe: Type) = - shortNames.getOrElseUpdate(tpe, { - val short = tpe match - case n: NamedType => - n.name.lastPart.show - case _ => tpe.show - - val alreadyPresent = shortNames.count((t, str) => str == short && t != tpe) - - alreadyPresent match - case 0 => short - case 1 => short + "¹" - case 2 => short + "²" - case 3 => short + "³" - case _ => tpe.show - } - ) - - end getShortName + val registry = TypeRegistry() inline def renderParamList(names: List[String], types: List[Type], maxLength: Int = 40) = val params = names.zip(types).map {case (name, tpe) => - fullNames.getOrElseUpdate(tpe, tpe.show) - MarginString.Chunk(s"$name: ${getShortName(tpe)}") + registry.getFullName(tpe) + MarginString.Chunk(s"$name: ${registry.getShortName(tpe).applied}") } MarginString.Group( params.toVector, ChunkJoiner.rightBreakable(", "), prefix = Some(ChunkJoiner.rightBreakable("(")), - suffix = Some(ChunkJoiner(")")) + suffix = Some(ChunkJoiner.rightBreakable(")")) ) end renderParamList alts.foreach { alt => - val info = alt.info.stripPoly + val info = alt.info val name = alt.name.show - + val paramNamess = info.paramNamess.map(_.map(_.show)) val paramTypes = info.paramInfoss @@ -150,7 +129,7 @@ object ErrorReporting { val lines = MarginString.Group( paramLists, ChunkJoiner.nonBreakable("") - ).lines(40).map(MarginString.Chunk.apply) + ).lines(60).map(MarginString.Chunk.apply) MarginString.Group( lines, @@ -159,23 +138,28 @@ object ErrorReporting { ) .lines(0) .foreach(denots += _) - } - val allMentionedTypes = fullNames.toList.sortBy(_._2).map(_._1) - val maxShortLength = shortNames.maxByOption(_._2.length).map(_._2.length).getOrElse(0) + val allMentionedTypes = registry.allShortNames + .toVector + .filter((tpe, shortName) => registry.getFullName(tpe) != shortName.ref) + .sortBy(_._2.ref) + .map(_._1) + val maxShortLength = registry.allShortNames.values.maxByOption(_.ref.length).map(_.ref.length).getOrElse(0) + denots += "" - denots += "where" + + if allMentionedTypes.nonEmpty then + denots += "where" import printing.Highlighting.* allMentionedTypes.foreach {tpe => - val shortName = shortNames(tpe) - val longName = fullNames(tpe) - if shortName != longName then - val paddedName = shortNames(tpe).padTo(maxShortLength, ' ') - denots += s" ${Cyan(paddedName).show} is $longName" + val shortName = registry.getShortName(tpe) + val longName = registry.getFullName(tpe) + val paddedName = shortName.ref.padTo(maxShortLength, ' ') + denots += s" ${Cyan(paddedName).show} is $longName" } em"overloaded alternatives of ${denotStr(alts.head)} with types\n" + diff --git a/compiler/src/dotty/tools/dotc/util/TypeRegistry.scala b/compiler/src/dotty/tools/dotc/util/TypeRegistry.scala new file mode 100644 index 000000000000..518e30d923e4 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/util/TypeRegistry.scala @@ -0,0 +1,79 @@ +package dotty.tools +package dotc + +import collection.mutable.Map as MMap + +import core.Types.* +import dotty.tools.dotc.core.Contexts.Context + +case class ShortTypeName(ref: String, applied: String, canonical: String) + +class TypeRegistry: + inline def getShortName(tpe: Type)(using Context) = computeShortName(tpe) + + inline def getFullName(tpe: Type)(using Context) = + fullNames.getOrElseUpdate(tpe, { + val concreteType = concrete(tpe) + + fullNames.getOrElseUpdate(concreteType, { + computeFullName(concreteType) + }) + }) + + + inline def allShortNames: Map[Type, ShortTypeName] = shortNames.toMap + + inline def allTypes: scala.collection.Set[Type] = shortNames.keySet + + private val fullNames = MMap.empty[Type, String] + private val shortNames = MMap.empty[Type, ShortTypeName] + private val resolvedNames = MMap.empty[ShortTypeName, String] + private val conflicts = MMap.empty[ShortTypeName, Int] + + private def concrete(tpe: Type) = + tpe match + case at: AppliedType => at.tycon + case _ => tpe + + private def nameIt(canonical: String, template: String, tpe: Type) = + val cnt = shortNames.count((t, r) => r.canonical == canonical && tpe != t) + val idx = cnt.toChar match + case 0 => "" + case 1 => "¹" + case 2 => "²" + case 3 => "³" + case 4 => "⁴" + case 5 => "⁵" + case 6 => "⁶" + case 7 => "⁷" + case 8 => "⁸" + case 9 => "⁹" + + template.replaceAll("", idx) + + private def computeShortName(tpe: Type)(using Context): ShortTypeName = + shortNames.getOrElseUpdate(concrete(tpe), { + val short = + tpe match + case n: NamedType => + val nm = n.name.lastPart.show + val nmTemplate = nm + "" + val value = nameIt(nm, nmTemplate, n) + ShortTypeName(value, value, nm) + + case at: AppliedType => + val ref = computeShortName(at.tycon).ref + val applied = ref + at.args.map(computeShortName(_).applied).mkString("[", ", ", "]") + + ShortTypeName(ref, applied, ref) + + case _ => ShortTypeName(tpe.show, tpe.show, tpe.show) + + short + + }) + + private inline def computeFullName(tpe: Type)(using Context) = + tpe.show + + diff --git a/sandbox/scalajs/src/hello.scala b/sandbox/scalajs/src/hello.scala index 0d36f6ed91c1..20282ecfd51d 100644 --- a/sandbox/scalajs/src/hello.scala +++ b/sandbox/scalajs/src/hello.scala @@ -1,58 +1,16 @@ -// package hello +package hello -// import scala.scalajs.js +import scala.scalajs.js -// trait MyTrait { -// val x = 5 -// def foo(y: Int) = x -// } +trait MyTrait { + val x = 5 + def foo(y: Int) = x +} -// object HelloWorld extends MyTrait { -// def main(args: Array[String]): Unit = { -// println("hello dotty.js!") -// println(foo(4)) -// } -// } +object HelloWorld extends MyTrait { + def main(args: Array[String]): Unit = { + println("hello dotty.js!") + println(foo(4)) + } +} -object hello: - trait World - -object bye: - trait World - -object bla: - object longname: - object otherlongname: - trait HelloWorldFactory -import bla.longname.otherlongname.HelloWorldFactory - -def method( - p1: hello.World, - p2: bye.World, - p3: hello.World, - p4: HelloWorldFactory, - p5: HelloWorldFactory, - p6: HelloWorldFactory, - p7: HelloWorldFactory -) = 1 - -def method( - p1: hello.World, - p1_1: HelloWorldFactory, - p2: hello.World, - p3: hello.World, - p4: HelloWorldFactory -) = 1 - -def method( - p1: String, - p1_1: HelloWorldFactory, - p2: hello.World, - p3: hello.World, - p4: HelloWorldFactory -) = 1 - - -@main def test = - import hello.World - method(new World, new World, 25, new World, new HelloWorldFactory) diff --git a/tests/neg/overload-error-message.check b/tests/neg/overload-error-message.check new file mode 100644 index 000000000000..8d608dc2e551 --- /dev/null +++ b/tests/neg/overload-error-message.check @@ -0,0 +1,28 @@ +-- [E134] Type Error: tests/neg/overload-error-message.scala:45:2 ------------------------------------------------------ +45 | method(new World, new World, 25, new World, new HelloWorldFactory) // error + | ^^^^^^ + |None of the overloaded alternatives of method method with types + | + | method(p1: String, p1_1: HelloWorldFactory, p2: World, p3: World, + | p4: HelloWorldFactory) + | + | method(p1: World, p1_1: HelloWorldFactory, p2: World, p3: World, p4: HelloWorldFactory) + | + | method(p1: World, p2: World¹, p3: World, p4: HelloWorldFactory, p5: HelloWorldFactory, + | p6: HelloWorldFactory, p7: HelloWorldFactory) + | + |where + | HelloWorldFactory is bla.longname.otherlongname.HelloWorldFactory + | World is hello.World + | World¹ is bye.World + |match arguments (hello.World, hello.World, (25 : Int), hello.World, bla.longname.otherlongname.HelloWorldFactory) +-- [E134] Type Error: tests/neg/overload-error-message.scala:46:2 ------------------------------------------------------ +46 | generic(25, false) // error + | ^^^^^^^ + | None of the overloaded alternatives of method generic with types + | + | generic(bla: Int)(x$2: Numeric[T1], x$3: Numeric[T1]) + | + | generic(bla: Int, a: String, h: List[Int])(x$4: Numeric[T1], x$5: Numeric[T1]) + | + | match arguments ((25 : Int), (false : Boolean)) diff --git a/tests/neg/overload-error-message.scala b/tests/neg/overload-error-message.scala new file mode 100644 index 000000000000..770ba069a899 --- /dev/null +++ b/tests/neg/overload-error-message.scala @@ -0,0 +1,47 @@ +object hello: + trait World + +object bye: + trait World + +object bla: + object longname: + object otherlongname: + trait HelloWorldFactory +import bla.longname.otherlongname.HelloWorldFactory + +def method( + p1: hello.World, + p2: bye.World, + p3: hello.World, + p4: HelloWorldFactory, + p5: HelloWorldFactory, + p6: HelloWorldFactory, + p7: HelloWorldFactory +) = 1 + +def method( + p1: hello.World, + p1_1: HelloWorldFactory, + p2: hello.World, + p3: hello.World, + p4: HelloWorldFactory +) = 1 + +def method( + p1: String, + p1_1: HelloWorldFactory, + p2: hello.World, + p3: hello.World, + p4: HelloWorldFactory +) = 1 + +def generic[T1, T2](bla: Int, a: String, h: List[Int])(using Numeric[T1], Numeric[T2]) = ??? +def generic[T1, T2](bla: Int)(using Numeric[T1], Numeric[T2]) = ??? + + +@main def test = + import hello.World + method(new World, new World, 25, new World, new HelloWorldFactory) // error + generic(25, false) // error + From a75f890695c38818f807795be2750c03e4c5e10a Mon Sep 17 00:00:00 2001 From: Anton Sviridov Date: Sat, 9 Jul 2022 20:29:20 +0100 Subject: [PATCH 3/7] Output type params on methods --- .../tools/dotc/typer/ErrorReporting.scala | 29 ++++++++------ .../explicit-nulls/neg/byname-nullables.check | 7 +++- tests/explicit-nulls/neg/i7883.check | 12 ++++-- tests/neg/bad-unapplies.check | 7 +++- tests/neg/i10901.check | 39 +++++++++++-------- tests/neg/i11544.check | 7 +++- tests/neg/i15000.check | 9 ++++- tests/neg/i6183.check | 14 +++++-- tests/neg/i8736.check | 10 +++-- tests/neg/indent-colons.check | 25 ++++++++---- tests/neg/overload-error-message.check | 10 ++--- 11 files changed, 110 insertions(+), 59 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index 0d8e3b7671f6..01deba565ed9 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -98,8 +98,8 @@ object ErrorReporting { val denots = Vector.newBuilder[String] val registry = TypeRegistry() - inline def renderParamList(names: List[String], types: List[Type], maxLength: Int = 40) = - val params = names.zip(types).map {case (name, tpe) => + inline def renderParamList(names: List[String], types: List[Type]) = + val params = names.zip(types).map { case (name, tpe) => registry.getFullName(tpe) MarginString.Chunk(s"$name: ${registry.getShortName(tpe).applied}") } @@ -118,23 +118,30 @@ object ErrorReporting { val paramNamess = info.paramNamess.map(_.map(_.show)) val paramTypes = info.paramInfoss + val resultType = info.finalResultType val methodPrefix = " " + name - val paramLists = - paramNamess.zip(paramTypes).map(t => - renderParamList(t._1, t._2) - ).toVector - - val lines = MarginString.Group( - paramLists, + val typeParamsSection = + info match + case pt: PolyType => + val typeParams = pt.paramNames + if typeParams.isEmpty then "" + else + typeParams.map(_.show).mkString("[", ", ", "]") + case _ => "" + + + val paramLists = MarginString.Group( + paramNamess.zip(paramTypes).map(t => renderParamList(t._1, t._2)).toVector, ChunkJoiner.nonBreakable("") ).lines(60).map(MarginString.Chunk.apply) MarginString.Group( - lines, + paramLists, ChunkJoiner.nonBreakable("\n " + (" " * methodPrefix.size)), - prefix = Some(ChunkJoiner.nonBreakable(methodPrefix)) + prefix = Some(ChunkJoiner.nonBreakable(methodPrefix + typeParamsSection)), + suffix = Some(ChunkJoiner.nonBreakable(": " + registry.getShortName(resultType).applied)) ) .lines(0) .foreach(denots += _) diff --git a/tests/explicit-nulls/neg/byname-nullables.check b/tests/explicit-nulls/neg/byname-nullables.check index 887e0267b6eb..dd492873ed4d 100644 --- a/tests/explicit-nulls/neg/byname-nullables.check +++ b/tests/explicit-nulls/neg/byname-nullables.check @@ -25,6 +25,9 @@ 81 | if x != null then f(byName(x), 1) // error: none of the overloaded methods match argument types | ^ | None of the overloaded alternatives of method f in object Test7 with types - | (x: => String, y: Int): String - | (x: String, y: String): String + | + | f(x: => String, y: Int): String + | + | f(x: String, y: String): String + | | match arguments (String | Null, (1 : Int)) diff --git a/tests/explicit-nulls/neg/i7883.check b/tests/explicit-nulls/neg/i7883.check index e37285332359..ec346be688db 100644 --- a/tests/explicit-nulls/neg/i7883.check +++ b/tests/explicit-nulls/neg/i7883.check @@ -2,9 +2,15 @@ 6 | case r(hd, tl) => Some((hd, tl)) // error // error // error | ^ | None of the overloaded alternatives of method unapplySeq in class Regex with types - | (m: scala.util.matching.Regex.Match): Option[List[String]] - | (c: Char): Option[List[Char]] - | (s: CharSequence): Option[List[String]] + | + | unapplySeq(m: Match): Option[List[String]] + | + | unapplySeq(c: Char): Option[List[String]] + | + | unapplySeq(s: CharSequence): Option[List[String]] + | + | where + | Match is scala.util.matching.Regex.Match | match arguments (String | Null) -- [E006] Not Found Error: tests/explicit-nulls/neg/i7883.scala:6:30 --------------------------------------------------- 6 | case r(hd, tl) => Some((hd, tl)) // error // error // error diff --git a/tests/neg/bad-unapplies.check b/tests/neg/bad-unapplies.check index 44633ca6950a..8c61665989e3 100644 --- a/tests/neg/bad-unapplies.check +++ b/tests/neg/bad-unapplies.check @@ -2,8 +2,11 @@ 22 | case A("2") => // error (cannot resolve overloading) | ^ | Ambiguous overload. The overloaded alternatives of method unapply in object A with types - | (x: B): Option[String] - | (x: A): Option[String] + | + | unapply(x: B): Option[String] + | + | unapply(x: A): Option[String] + | | both match arguments (C) | | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i10901.check b/tests/neg/i10901.check index 7f506628798e..f4c96d2953ec 100644 --- a/tests/neg/i10901.check +++ b/tests/neg/i10901.check @@ -1,21 +1,22 @@ -- [E008] Not Found Error: tests/neg/i10901.scala:45:38 ---------------------------------------------------------------- 45 | val pos1: Point2D[Int,Double] = x º y // error | ^^^ - | value º is not a member of object BugExp4Point2D.IntT. - | An extension method was tried, but could not be fully constructed: + | value º is not a member of object BugExp4Point2D.IntT. + | An extension method was tried, but could not be fully constructed: | - | º(x) failed with + | º(x) failed with | - | Ambiguous overload. The overloaded alternatives of method º in object dsl with types - | [T1, T2] - | (x: BugExp4Point2D.ColumnType[T1]) - | (y: BugExp4Point2D.ColumnType[T2]) - | (implicit evidence$7: Numeric[T1], evidence$8: Numeric[T2]): BugExp4Point2D.Point2D[T1, T2] - | [T1, T2] - | (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)) + | Ambiguous overload. The overloaded alternatives of method º in object dsl with types + | + | º[T1, T2](x: ColumnType[T1])(y: ColumnType[T1])(evidence$7: Numeric[T1], + | evidence$8: Numeric[T1]): Point2D[T1, T2] + | + | º[T1, T2](x: T1)(y: ColumnType[T1])(evidence$5: Numeric[T1], evidence$6: Numeric[T1]): Point2D[T1, T2] + | + | where + | ColumnType is BugExp4Point2D.ColumnType + | Point2D is BugExp4Point2D.Point2D + | both match arguments ((x : BugExp4Point2D.IntT.type)) -- [E008] Not Found Error: tests/neg/i10901.scala:48:38 ---------------------------------------------------------------- 48 | val pos4: Point2D[Int,Double] = x º 201.1 // error | ^^^ @@ -25,10 +26,14 @@ | º(x) failed with | | Ambiguous overload. The overloaded alternatives of method º in object dsl with types - | [T1, T2] - | (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] + | + | º[T1, T2](x: ColumnType[T1])(y: T2)(evidence$9: Numeric[T1], evidence$10: Numeric[T1]): Point2D[T1, T2] + | + | º[T1, T2](x: T1)(y: T2)(evidence$3: Numeric[T1], evidence$4: Numeric[T1]): Point2D[T1, T2] + | + | where + | ColumnType is BugExp4Point2D.ColumnType + | Point2D is BugExp4Point2D.Point2D | both match arguments ((x : BugExp4Point2D.IntT.type)) -- [E008] Not Found Error: tests/neg/i10901.scala:62:16 ---------------------------------------------------------------- 62 | val y = "abc".foo // error diff --git a/tests/neg/i11544.check b/tests/neg/i11544.check index 15469f0c37a5..8c4ee09d850c 100644 --- a/tests/neg/i11544.check +++ b/tests/neg/i11544.check @@ -2,8 +2,11 @@ 6 | val n = m1.foo(23) // error | ^^^^^^ | Ambiguous overload. The overloaded alternatives of method (str: String, int: Int): Int with types - | (str: String, int: Int): Int - | (arg: Int): Int + | + | (str: String, int: Int): Int + | + | (arg: Int): Int + | | both match arguments ((23 : Int)) | | Note: Overloaded definitions introduced by refinements cannot be resolved diff --git a/tests/neg/i15000.check b/tests/neg/i15000.check index c63866993103..4f3267258cb9 100644 --- a/tests/neg/i15000.check +++ b/tests/neg/i15000.check @@ -19,6 +19,11 @@ | apply(ExtensionMethodReproduction.c) failed with | | 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 + | + | apply(c: C)(x: Int, y: Int): String + | + | apply(c: C)(x: Int, y: String): String + | + | where + | C is ExtensionMethodReproduction.C | both match arguments (ExtensionMethodReproduction.c.type) diff --git a/tests/neg/i6183.check b/tests/neg/i6183.check index 70c1afaae621..24756b45a932 100644 --- a/tests/neg/i6183.check +++ b/tests/neg/i6183.check @@ -7,15 +7,21 @@ | render(42) failed with | | Ambiguous overload. The overloaded alternatives of method render in object Test with types - | [B](b: B)(using x$2: DummyImplicit): Char - | [A](a: A): String + | + | render[B](b: B)(x$2: DummyImplicit): Char + | + | render[A](a: A): String + | | both match arguments ((42 : Int)) -- [E051] Reference Error: tests/neg/i6183.scala:7:9 ------------------------------------------------------------------- 7 | Test.render(42) // error | ^^^^^^^^^^^ | Ambiguous overload. The overloaded alternatives of method render in object Test with types - | [B](b: B)(using x$2: DummyImplicit): Char - | [A](a: A): String + | + | render[B](b: B)(x$2: DummyImplicit): Char + | + | render[A](a: A): String + | | both match arguments ((42 : Int)) | | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i8736.check b/tests/neg/i8736.check index e7a0d62cb4af..ad211d46c21b 100644 --- a/tests/neg/i8736.check +++ b/tests/neg/i8736.check @@ -16,9 +16,13 @@ 31 | def res3: Boolean = rec.get("z") // error: ambiguous | ^^^^^^^ | Ambiguous overload. The overloaded alternatives of method (k: ("k" : String)): String with types - | (k: ("k" : String)): String - | (k: ("v" : String)): Int - | (k: ("z" : String)): Boolean + | + | (k: ("k" : String)): String + | + | (k: ("v" : String)): Int + | + | (k: ("z" : String)): Boolean + | | all match arguments (("z" : String)) | | Note: Overloaded definitions introduced by refinements cannot be resolved diff --git a/tests/neg/indent-colons.check b/tests/neg/indent-colons.check index 06bd7a31b079..177282c17b80 100644 --- a/tests/neg/indent-colons.check +++ b/tests/neg/indent-colons.check @@ -26,14 +26,23 @@ 23 | val x = 1.+ : // error | ^^^ | None of the overloaded alternatives of method + in class Int with types - | (x: Double): Double - | (x: Float): Float - | (x: Long): Long - | (x: Int): Int - | (x: Char): Int - | (x: Short): Int - | (x: Byte): Int - | (x: String): String + | + | +(x: Double): Double + | + | +(x: Float): Float + | + | +(x: Long): Long + | + | +(x: Int): Int + | + | +(x: Char): Int + | + | +(x: Short): Int + | + | +(x: Byte): Int + | + | +(x: String): String + | | match expected type (2 : Int) -- [E006] Not Found Error: tests/neg/indent-colons.scala:32:7 ---------------------------------------------------------- 32 | if file.isEmpty // error diff --git a/tests/neg/overload-error-message.check b/tests/neg/overload-error-message.check index 8d608dc2e551..453256ec1c69 100644 --- a/tests/neg/overload-error-message.check +++ b/tests/neg/overload-error-message.check @@ -4,12 +4,12 @@ |None of the overloaded alternatives of method method with types | | method(p1: String, p1_1: HelloWorldFactory, p2: World, p3: World, - | p4: HelloWorldFactory) + | p4: HelloWorldFactory): Int | - | method(p1: World, p1_1: HelloWorldFactory, p2: World, p3: World, p4: HelloWorldFactory) + | method(p1: World, p1_1: HelloWorldFactory, p2: World, p3: World, p4: HelloWorldFactory): Int | | method(p1: World, p2: World¹, p3: World, p4: HelloWorldFactory, p5: HelloWorldFactory, - | p6: HelloWorldFactory, p7: HelloWorldFactory) + | p6: HelloWorldFactory, p7: HelloWorldFactory): Int | |where | HelloWorldFactory is bla.longname.otherlongname.HelloWorldFactory @@ -21,8 +21,8 @@ | ^^^^^^^ | None of the overloaded alternatives of method generic with types | - | generic(bla: Int)(x$2: Numeric[T1], x$3: Numeric[T1]) + | generic[T1, T2](bla: Int)(x$2: Numeric[T1], x$3: Numeric[T1]): Nothing | - | generic(bla: Int, a: String, h: List[Int])(x$4: Numeric[T1], x$5: Numeric[T1]) + | generic[T1, T2](bla: Int, a: String, h: List[Int])(x$4: Numeric[T1], x$5: Numeric[T1]): Nothing | | match arguments ((25 : Int), (false : Boolean)) From 3a6a15c756b8202bd5c6fecf8febbb827e368cd3 Mon Sep 17 00:00:00 2001 From: Anton Sviridov Date: Sat, 9 Jul 2022 20:37:58 +0100 Subject: [PATCH 4/7] Some explicit nulls weirdness --- compiler/src/dotty/tools/dotc/util/TypeRegistry.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/util/TypeRegistry.scala b/compiler/src/dotty/tools/dotc/util/TypeRegistry.scala index 518e30d923e4..4478d4c799f5 100644 --- a/compiler/src/dotty/tools/dotc/util/TypeRegistry.scala +++ b/compiler/src/dotty/tools/dotc/util/TypeRegistry.scala @@ -56,7 +56,7 @@ class TypeRegistry: val short = tpe match case n: NamedType => - val nm = n.name.lastPart.show + val nm = n.name.lastPart.show.nn val nmTemplate = nm + "" val value = nameIt(nm, nmTemplate, n) ShortTypeName(value, value, nm) From 7235f315d14594c451376451ff0f05c08bd9b4d4 Mon Sep 17 00:00:00 2001 From: Anton Sviridov Date: Sat, 9 Jul 2022 21:40:46 +0100 Subject: [PATCH 5/7] Thou shall not null --- compiler/src/dotty/tools/dotc/util/TypeRegistry.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/util/TypeRegistry.scala b/compiler/src/dotty/tools/dotc/util/TypeRegistry.scala index 4478d4c799f5..b3913832e9ce 100644 --- a/compiler/src/dotty/tools/dotc/util/TypeRegistry.scala +++ b/compiler/src/dotty/tools/dotc/util/TypeRegistry.scala @@ -35,7 +35,7 @@ class TypeRegistry: case at: AppliedType => at.tycon case _ => tpe - private def nameIt(canonical: String, template: String, tpe: Type) = + private def nameIt(canonical: String, template: String, tpe: Type): String = val cnt = shortNames.count((t, r) => r.canonical == canonical && tpe != t) val idx = cnt.toChar match case 0 => "" @@ -49,14 +49,14 @@ class TypeRegistry: case 8 => "⁸" case 9 => "⁹" - template.replaceAll("", idx) + template.replaceAll("", idx).nn private def computeShortName(tpe: Type)(using Context): ShortTypeName = shortNames.getOrElseUpdate(concrete(tpe), { val short = tpe match case n: NamedType => - val nm = n.name.lastPart.show.nn + val nm = n.name.lastPart.show val nmTemplate = nm + "" val value = nameIt(nm, nmTemplate, n) ShortTypeName(value, value, nm) From b2e47061d8cdfe82ae8110fba435ec956d2cdd94 Mon Sep 17 00:00:00 2001 From: Anton Sviridov Date: Sun, 10 Jul 2022 13:19:07 +0100 Subject: [PATCH 6/7] Move things to util package --- .../tools/dotc/typer/ErrorReporting.scala | 17 ++-- .../dotty/tools/dotc/util/MarginString.scala | 83 ++++++++++--------- 2 files changed, 52 insertions(+), 48 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index 01deba565ed9..6785d8033fb4 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -17,7 +17,8 @@ import collection.mutable import scala.util.matching.Regex import dotty.tools.dotc.core.Names.TypeName import dotty.tools.dotc.util.MarginString -import dotty.tools.dotc.util.ChunkJoiner +import MarginString.Joiner +import dotty.tools.dotc.util.TypeRegistry object ErrorReporting { @@ -106,9 +107,9 @@ object ErrorReporting { MarginString.Group( params.toVector, - ChunkJoiner.rightBreakable(", "), - prefix = Some(ChunkJoiner.rightBreakable("(")), - suffix = Some(ChunkJoiner.rightBreakable(")")) + Joiner.rightBreakable(", "), + prefix = Some(Joiner.rightBreakable("(")), + suffix = Some(Joiner.rightBreakable(")")) ) end renderParamList @@ -134,14 +135,14 @@ object ErrorReporting { val paramLists = MarginString.Group( paramNamess.zip(paramTypes).map(t => renderParamList(t._1, t._2)).toVector, - ChunkJoiner.nonBreakable("") + Joiner.nonBreakable("") ).lines(60).map(MarginString.Chunk.apply) MarginString.Group( paramLists, - ChunkJoiner.nonBreakable("\n " + (" " * methodPrefix.size)), - prefix = Some(ChunkJoiner.nonBreakable(methodPrefix + typeParamsSection)), - suffix = Some(ChunkJoiner.nonBreakable(": " + registry.getShortName(resultType).applied)) + Joiner.nonBreakable("\n " + (" " * methodPrefix.size)), + prefix = Some(Joiner.nonBreakable(methodPrefix + typeParamsSection)), + suffix = Some(Joiner.nonBreakable(": " + registry.getShortName(resultType).applied)) ) .lines(0) .foreach(denots += _) diff --git a/compiler/src/dotty/tools/dotc/util/MarginString.scala b/compiler/src/dotty/tools/dotc/util/MarginString.scala index 1dc59288c77a..208a57082771 100644 --- a/compiler/src/dotty/tools/dotc/util/MarginString.scala +++ b/compiler/src/dotty/tools/dotc/util/MarginString.scala @@ -1,53 +1,23 @@ package dotty.tools.dotc.util - -case class ChunkJoiner private (s: String, breakLeft: Boolean, breakRight: Boolean) -object ChunkJoiner: - def apply(s: String) = new ChunkJoiner(s, true, true) - def nonBreakable(s: String) = new ChunkJoiner(s, false, false) - def leftBreakable(s: String) = new ChunkJoiner(s, breakLeft = true, false) - def rightBreakable(s: String) = new ChunkJoiner(s, false, breakRight = true) +import MarginString.Joiner enum MarginString: case Chunk(s: String) case Group(strings: Vector[MarginString], - joiner: ChunkJoiner, - prefix: Option[ChunkJoiner] = None, - suffix: Option[ChunkJoiner] = None) + joiner: Joiner, + prefix: Option[Joiner] = None, + suffix: Option[Joiner] = None) def lines(maxWidth: Int) = MarginString.lines(this, maxWidth) object MarginString: - private case object Breakpoint - - private def tokens(str: MarginString): Vector[String | Breakpoint.type] = - def go(ms: MarginString): Vector[String | Breakpoint.type] = - ms match - case Chunk(s) => Vector(s) - case g: Group => - import g.* - val b = Vector.newBuilder[String | Breakpoint.type] - - def renderJoiner(joiner: ChunkJoiner) = - if joiner.breakLeft then - b += Breakpoint - b += joiner.s - if joiner.breakRight then - b += Breakpoint - - - prefix.foreach(renderJoiner) - - strings.zipWithIndex.foreach {case (c, idx) => - b ++= go(c) - if idx != strings.size - 1 then renderJoiner(joiner) - } - - suffix.foreach(renderJoiner) - - b.result() - go(str) - end tokens + case class Joiner private (s: String, breakLeft: Boolean, breakRight: Boolean) + object Joiner: + def apply(s: String) = new Joiner(s, true, true) + def nonBreakable(s: String) = new Joiner(s, false, false) + def leftBreakable(s: String) = new Joiner(s, breakLeft = true, false) + def rightBreakable(s: String) = new Joiner(s, false, breakRight = true) def lines(str: MarginString, maxLength: Int): Vector[String] = val nonBreakable = { @@ -79,3 +49,36 @@ object MarginString: if curLine.nonEmpty then lineBreaks += curLine.result() lineBreaks.result() + end lines + + private case object Breakpoint + + private def tokens(str: MarginString): Vector[String | Breakpoint.type] = + def go(ms: MarginString): Vector[String | Breakpoint.type] = + ms match + case Chunk(s) => Vector(s) + case g: Group => + import g.* + val b = Vector.newBuilder[String | Breakpoint.type] + + def renderJoiner(joiner: Joiner) = + if joiner.breakLeft then + b += Breakpoint + b += joiner.s + if joiner.breakRight then + b += Breakpoint + + + prefix.foreach(renderJoiner) + + strings.zipWithIndex.foreach {case (c, idx) => + b ++= go(c) + if idx != strings.size - 1 then renderJoiner(joiner) + } + + suffix.foreach(renderJoiner) + + b.result() + go(str) + end tokens + From 9b3e978e4d8e3e40c0cfcbf30f75c6d694c15745 Mon Sep 17 00:00:00 2001 From: Anton Sviridov Date: Sun, 10 Jul 2022 13:19:21 +0100 Subject: [PATCH 7/7] Remove inlining as it breaks the compiler --- .../dotty/tools/dotc/util/TypeRegistry.scala | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/util/TypeRegistry.scala b/compiler/src/dotty/tools/dotc/util/TypeRegistry.scala index b3913832e9ce..5c4b1acf7d06 100644 --- a/compiler/src/dotty/tools/dotc/util/TypeRegistry.scala +++ b/compiler/src/dotty/tools/dotc/util/TypeRegistry.scala @@ -1,17 +1,16 @@ -package dotty.tools -package dotc +package dotty.tools.dotc.util import collection.mutable.Map as MMap -import core.Types.* +import dotty.tools.dotc.core.Types.* import dotty.tools.dotc.core.Contexts.Context case class ShortTypeName(ref: String, applied: String, canonical: String) -class TypeRegistry: - inline def getShortName(tpe: Type)(using Context) = computeShortName(tpe) +final class TypeRegistry: + def getShortName(tpe: Type)(using Context) = computeShortName(tpe) - inline def getFullName(tpe: Type)(using Context) = + def getFullName(tpe: Type)(using Context) = fullNames.getOrElseUpdate(tpe, { val concreteType = concrete(tpe) @@ -21,9 +20,9 @@ class TypeRegistry: }) - inline def allShortNames: Map[Type, ShortTypeName] = shortNames.toMap + def allShortNames: Map[Type, ShortTypeName] = shortNames.toMap - inline def allTypes: scala.collection.Set[Type] = shortNames.keySet + def allTypes: scala.collection.Set[Type] = shortNames.keySet private val fullNames = MMap.empty[Type, String] private val shortNames = MMap.empty[Type, ShortTypeName] @@ -73,7 +72,7 @@ class TypeRegistry: }) - private inline def computeFullName(tpe: Type)(using Context) = + private def computeFullName(tpe: Type)(using Context) = tpe.show