Skip to content

Commit

Permalink
Have a better error message when healing types (#21711)
Browse files Browse the repository at this point in the history
Closes #21696
  • Loading branch information
hamzaremmal authored Oct 7, 2024
2 parents d2b418c + c3f1307 commit 6ceaab5
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
case TailrecNestedCallID //errorNumber: 199
case FinalLocalDefID // errorNumber: 200
case NonNamedArgumentInJavaAnnotationID // errorNumber: 201
case QuotedTypeMissingID // errorNumber: 202

def errorNumber = ordinal - 1

Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/reporting/MessageKind.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ enum MessageKind:
case Compatibility
case PotentialIssue
case UnusedSymbol
case Staging

/** Human readable message that will end up being shown to the user.
* NOTE: This is only used in the situation where you have multiple words
Expand All @@ -39,5 +40,6 @@ enum MessageKind:
case MatchCaseUnreachable => "Match case Unreachable"
case PotentialIssue => "Potential Issue"
case UnusedSymbol => "Unused Symbol"
case Staging => "Staging Issue"
case kind => kind.toString
end MessageKind
20 changes: 20 additions & 0 deletions compiler/src/dotty/tools/dotc/reporting/messages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ end CyclicMsg
abstract class ReferenceMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId):
def kind = MessageKind.Reference

abstract class StagingMessage(errorId: ErrorMessageID)(using Context) extends Message(errorId):
override final def kind = MessageKind.Staging

abstract class EmptyCatchOrFinallyBlock(tryBody: untpd.Tree, errNo: ErrorMessageID)(using Context)
extends SyntaxMsg(errNo) {
def explain(using Context) = {
Expand Down Expand Up @@ -3323,3 +3326,20 @@ class NonNamedArgumentInJavaAnnotation(using Context) extends SyntaxMsg(NonNamed
"""

end NonNamedArgumentInJavaAnnotation

final class QuotedTypeMissing(tpe: Type)(using Context) extends StagingMessage(QuotedTypeMissingID):

private def witness = defn.QuotedTypeClass.typeRef.appliedTo(tpe)

override protected def msg(using Context): String =
i"Reference to $tpe within quotes requires a given ${witness} in scope"

override protected def explain(using Context): String =
i"""Referencing `$tpe` inside a quoted expression requires a `${witness}` to be in scope.
|Since Scala is subject to erasure at runtime, the type information will be missing during the execution of the code.
|`${witness}` is therefore needed to carry `$tpe`'s type information into the quoted code.
|Without an implicit `${witness}`, the type `$tpe` cannot be properly referenced within the expression.
|To resolve this, ensure that a `${witness}` is available, either through a context-bound or explicitly.
|"""

end QuotedTypeMissing
26 changes: 13 additions & 13 deletions compiler/src/dotty/tools/dotc/staging/HealType.scala
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
package dotty.tools.dotc
package staging

import dotty.tools.dotc.core.Contexts.*
import dotty.tools.dotc.core.Decorators.*
import dotty.tools.dotc.core.Flags.*
import dotty.tools.dotc.core.StdNames.*
import dotty.tools.dotc.core.Symbols.*
import dotty.tools.dotc.core.Types.*
import dotty.tools.dotc.staging.StagingLevel.*
import dotty.tools.dotc.staging.QuoteTypeTags.*
import reporting.*

import dotty.tools.dotc.typer.Implicits.SearchFailureType
import dotty.tools.dotc.util.SrcPos
import core.Contexts.*
import core.Decorators.*
import core.Flags.*
import core.StdNames.*
import core.Symbols.*
import core.Types.*
import StagingLevel.*
import QuoteTypeTags.*

import typer.Implicits.SearchFailureType
import util.SrcPos

class HealType(pos: SrcPos)(using Context) extends TypeMap {

Expand Down Expand Up @@ -98,9 +100,7 @@ class HealType(pos: SrcPos)(using Context) extends TypeMap {
pos)
tp
case _ =>
report.error(em"""Reference to $tp within quotes requires a given $reqType in scope.
|
|""", pos)
report.error(QuotedTypeMissing(tp), pos)
tp
}

Expand Down
13 changes: 13 additions & 0 deletions tests/neg/i21696.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
-- [E202] Staging Issue Error: tests/neg/i21696.scala:7:52 -------------------------------------------------------------
7 |def foo[T](using Quotes): Expr[Thing[T]] = '{ Thing[T]() } // error
| ^
| Reference to T within quotes requires a given scala.quoted.Type[T] in scope
|---------------------------------------------------------------------------------------------------------------------
| Explanation (enabled by `-explain`)
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| Referencing `T` inside a quoted expression requires a `scala.quoted.Type[T]` to be in scope.
| Since Scala is subject to erasure at runtime, the type information will be missing during the execution of the code.
| `scala.quoted.Type[T]` is therefore needed to carry `T`'s type information into the quoted code.
| Without an implicit `scala.quoted.Type[T]`, the type `T` cannot be properly referenced within the expression.
| To resolve this, ensure that a `scala.quoted.Type[T]` is available, either through a context-bound or explicitly.
---------------------------------------------------------------------------------------------------------------------
7 changes: 7 additions & 0 deletions tests/neg/i21696.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//> using options -explain

import scala.quoted.{Expr, Quotes}

case class Thing[T]()

def foo[T](using Quotes): Expr[Thing[T]] = '{ Thing[T]() } // error

0 comments on commit 6ceaab5

Please sign in to comment.