Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix widen types before checking an implicit view exists #18719

Merged
merged 3 commits into from
Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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.")
Expand Down
49 changes: 11 additions & 38 deletions compiler/src/dotty/tools/dotc/report.scala
Original file line number Diff line number Diff line change
Expand Up @@ -132,50 +132,26 @@ 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)
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,
"during phase" -> ctx.phase.megaPhase,
"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(" "),
))
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:<unknown>"),
"tree type" -> tree.typeOpt.show,
) ::: symbolInfos ::: List(
"call site" -> s"${site.showLocated} in ${site.enclosingPackageClass}"
"settings" -> settings.map(showSetting).mkString(" "),
))
val context_s = try
s""" == Source file context for tree position ==
|
|${messageRendering.messageAndPos(Diagnostic.Error("", pos))}""".stripMargin
catch case _: Exception => "<Cannot read source file>"
s"""
| $errorMessage
|
Expand All @@ -184,9 +160,6 @@ object report:
| https://github.com/lampepfl/dotty/issues/new/choose
|
|$info1
|
|$info2
|
|$context_s""".stripMargin
} catch case _: Throwable => errorMessage // don't introduce new errors trying to report errors, so swallow exceptions
|""".stripMargin
}
end report
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -850,7 +850,7 @@ trait Implicits:
&& !to.isError
&& !ctx.isAfterTyper
&& ctx.mode.is(Mode.ImplicitsEnabled)
&& from.isValueType
&& from.widen.isValueType
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Counter-example:

  given foo[A]: (Ctx => Int) = _ => 42

  val works = get[Int](using summon[Ctx => Int])
  val fails = get[Int]

using the example from #16453 - with this change (or !imp.underlying.isInstanceOf[PolyType] in ignoredConvertibleImplicits) the suggestion of foo will be dropped, even though it's a correct suggestion.

&& ( from.isValueSubType(to)
|| inferView(dummyTreeOfType(from), to)
(using ctx.fresh.addMode(Mode.ImplicitExploration).setExploreTyperState()).isSuccess
Expand Down
8 changes: 8 additions & 0 deletions tests/neg/i18650.min.scala
Original file line number Diff line number Diff line change
@@ -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)
8 changes: 8 additions & 0 deletions tests/neg/i18650.min2.scala
Original file line number Diff line number Diff line change
@@ -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)
26 changes: 26 additions & 0 deletions tests/neg/i18650.scala
Original file line number Diff line number Diff line change
@@ -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)
Loading