From b12097715ee7d03f80fd58aa428341e770269910 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 17 Oct 2023 23:06:37 +0100 Subject: [PATCH 1/3] report: Trim down enriched error messages The implementation here comes from porting from nsc. But I think things were done differently enough with its Global state, that make it not translate nicely here. So I'm paring it down to what I know works (at least for me): versions, settings, compilation unit. Not the tree/symbol/site stuff. --- compiler/src/dotty/tools/dotc/report.scala | 39 ++-------------------- 1 file changed, 3 insertions(+), 36 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/report.scala b/compiler/src/dotty/tools/dotc/report.scala index 11ab38b3f5ae..247e67b5be36 100644 --- a/compiler/src/dotty/tools/dotc/report.scala +++ b/compiler/src/dotty/tools/dotc/report.scala @@ -136,19 +136,7 @@ object report: def formatExplain(pairs: List[(String, Any)]) = pairs.map((k, v) => f"$k%20s: $v").mkString("\n") val settings = ctx.settings.userSetSettings(ctx.settingsState).sortBy(_.name) - val tree = ctx.tree - val sym = tree.symbol - val pos = tree.sourcePos - val path = pos.source.path - val site = ctx.outersIterator.map(_.owner).filter(sym => !sym.exists || sym.isClass || sym.is(Method)).next() - - import untpd.* - extension (tree: Tree) def summaryString: String = tree match - case Literal(const) => s"Literal($const)" - case Ident(name) => s"Ident(${name.decode})" - case Select(qual, name) => s"Select(${qual.summaryString}, ${name.decode})" - case tree: NameTree => (if tree.isType then "type " else "") + tree.name.decode - case tree => s"${tree.className}${if tree.symbol.exists then s"(${tree.symbol})" else ""}" + def showSetting(s: Setting[?]): String = if s.value == "" then s"${s.name} \"\"" else s"${s.name} ${s.value}" val info1 = formatExplain(List( "while compiling" -> ctx.compilationUnit, @@ -156,26 +144,8 @@ object report: "mode" -> ctx.mode, "library version" -> scala.util.Properties.versionString, "compiler version" -> dotty.tools.dotc.config.Properties.versionString, - "settings" -> settings.map(s => if s.value == "" then s"${s.name} \"\"" else s"${s.name} ${s.value}").mkString(" "), + "settings" -> settings.map(showSetting).mkString(" "), )) - val symbolInfos = if sym eq NoSymbol then List("symbol" -> sym) else List( - "symbol" -> sym.showLocated, - "symbol definition" -> s"${sym.showDcl} (a ${sym.className})", - "symbol package" -> sym.enclosingPackageClass.fullName, - "symbol owners" -> sym.showExtendedLocation, - ) - val info2 = formatExplain(List( - "tree" -> tree.summaryString, - "tree position" -> (if pos.exists then s"$path:${pos.line + 1}:${pos.column}" else s"$path:"), - "tree type" -> tree.typeOpt.show, - ) ::: symbolInfos ::: List( - "call site" -> s"${site.showLocated} in ${site.enclosingPackageClass}" - )) - val context_s = try - s""" == Source file context for tree position == - | - |${messageRendering.messageAndPos(Diagnostic.Error("", pos))}""".stripMargin - catch case _: Exception => "" s""" | $errorMessage | @@ -184,9 +154,6 @@ object report: | https://github.com/lampepfl/dotty/issues/new/choose | |$info1 - | - |$info2 - | - |$context_s""".stripMargin + |""".stripMargin } catch case _: Throwable => errorMessage // don't introduce new errors trying to report errors, so swallow exceptions end report From e6fe32091a6c2d65d683d8dcb9bf95f458394771 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 17 Oct 2023 23:08:34 +0100 Subject: [PATCH 2/3] report: Add a Y-flag to disable enriched error messages --- .../src/dotty/tools/dotc/config/ScalaSettings.scala | 1 + compiler/src/dotty/tools/dotc/report.scala | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 925be029746e..1419b4195029 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -421,6 +421,7 @@ private sealed trait YSettings: val YshowVarBounds: Setting[Boolean] = BooleanSetting("-Yshow-var-bounds", "Print type variables with their bounds.") val YnoDecodeStacktraces: Setting[Boolean] = BooleanSetting("-Yno-decode-stacktraces", "Show raw StackOverflow stacktraces, instead of decoding them into triggering operations.") + val YnoEnrichErrorMessages: Setting[Boolean] = BooleanSetting("-Yno-enrich-error-messages", "Show raw error messages, instead of enriching them with contextual information.") val Yinstrument: Setting[Boolean] = BooleanSetting("-Yinstrument", "Add instrumentation code that counts allocations and closure creations.") val YinstrumentDefs: Setting[Boolean] = BooleanSetting("-Yinstrument-defs", "Add instrumentation code that counts method calls; needs -Yinstrument to be set, too.") diff --git a/compiler/src/dotty/tools/dotc/report.scala b/compiler/src/dotty/tools/dotc/report.scala index 247e67b5be36..75261fb6890e 100644 --- a/compiler/src/dotty/tools/dotc/report.scala +++ b/compiler/src/dotty/tools/dotc/report.scala @@ -132,7 +132,13 @@ object report: private object messageRendering extends MessageRendering // Should only be called from Run#enrichErrorMessage. - def enrichErrorMessage(errorMessage: String)(using Context): String = try { + def enrichErrorMessage(errorMessage: String)(using Context): String = + if ctx.settings.YnoEnrichErrorMessages.value then errorMessage + else try enrichErrorMessage1(errorMessage) + catch case _: Throwable => errorMessage // don't introduce new errors trying to report errors, so swallow exceptions + + private def enrichErrorMessage1(errorMessage: String)(using Context): String = { + import untpd.*, config.Settings.* def formatExplain(pairs: List[(String, Any)]) = pairs.map((k, v) => f"$k%20s: $v").mkString("\n") val settings = ctx.settings.userSetSettings(ctx.settingsState).sortBy(_.name) @@ -155,5 +161,5 @@ object report: | |$info1 |""".stripMargin - } catch case _: Throwable => errorMessage // don't introduce new errors trying to report errors, so swallow exceptions + } end report From 8a2773f0c6264ef0392d786e36d1beafc74ea222 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 17 Oct 2023 23:09:59 +0100 Subject: [PATCH 3/3] Fix widen types before checking an implicit view exists It's not possible to convert a method, such as a polymorphic method, such as `makeChurch`, into another type - we need to widen out the TermRef to discover the underlying PolyType which isn't a value type. Failing to do so will create a dummyTreeOfType, which will then be attempted to be applied, which eventually causes an assertion crash while building the tpd.TypeApply. --- .../dotty/tools/dotc/typer/Implicits.scala | 2 +- tests/neg/i18650.min.scala | 8 ++++++ tests/neg/i18650.min2.scala | 8 ++++++ tests/neg/i18650.scala | 26 +++++++++++++++++++ 4 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 tests/neg/i18650.min.scala create mode 100644 tests/neg/i18650.min2.scala create mode 100644 tests/neg/i18650.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 04aca960845e..5cba406a302e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -850,7 +850,7 @@ trait Implicits: && !to.isError && !ctx.isAfterTyper && ctx.mode.is(Mode.ImplicitsEnabled) - && from.isValueType + && from.widen.isValueType && ( from.isValueSubType(to) || inferView(dummyTreeOfType(from), to) (using ctx.fresh.addMode(Mode.ImplicitExploration).setExploreTyperState()).isSuccess diff --git a/tests/neg/i18650.min.scala b/tests/neg/i18650.min.scala new file mode 100644 index 000000000000..0756c05bfc25 --- /dev/null +++ b/tests/neg/i18650.min.scala @@ -0,0 +1,8 @@ +class Church[B]: + type Nat = Tuple1[B] + +class Test: + given makeChurch[C]: Church[C] = ??? // necessary to cause crash + + def churchTest(c: Church[Int]): Unit = + val res1 = summon[c.Nat =:= Int] // error (not a compiler crash) diff --git a/tests/neg/i18650.min2.scala b/tests/neg/i18650.min2.scala new file mode 100644 index 000000000000..43ea86492d54 --- /dev/null +++ b/tests/neg/i18650.min2.scala @@ -0,0 +1,8 @@ +class Church[B]: + type Nat = Tuple1[B] + +class Test2: + given makeChurch2[C](using DummyImplicit): Church[C] = ??? + + def churchTest2(c: Church[Int]): Unit = + val res2 = summon[c.Nat =:= Int] // error (not a compiler crash) diff --git a/tests/neg/i18650.scala b/tests/neg/i18650.scala new file mode 100644 index 000000000000..d627c6ea329b --- /dev/null +++ b/tests/neg/i18650.scala @@ -0,0 +1,26 @@ +trait Lam: + type F[_] + extension [A, B](f: F[A => B]) def apply(arg: F[A]): F[B] + def lam[A, B](f: F[A] => F[B]): F[A => B] + final def id[A]: F[A => A] = lam(identity[F[A]]) + +object LamInterpreter extends Lam: + type F[t] = t + def lam[A, B](f: F[A] => F[B]): F[A => B] = f + extension [A, B](f: F[A => B]) def apply(arg: F[A]): F[B] = f(arg) + + +class Church[A](using val l: Lam): + import l.* + type Nat = F[(A => A) => (A => A)] + def zero: Nat = id + extension (n: Nat) def suc: Nat = lam(f => lam(x => f(n(f)(x)))) + +given [A](using l: Lam): Church[A] = Church() + + +@main +def churchTest = + given Lam = LamInterpreter + val c: Church[Int] = summon + summon[c.Nat =:= ((Int => Int) => (Int => Int))] // error (not a compiler crash)