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

Refine error messages for early promotion #15263

Merged
merged 11 commits into from
May 25, 2022
73 changes: 31 additions & 42 deletions compiler/src/dotty/tools/dotc/transform/init/Errors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,21 @@ import util.SourcePosition
import Decorators._, printing.SyntaxHighlighting
import Types._, Symbols._, Contexts._

object Errors {
type Errors = Seq[Error]
val empty: Errors = Nil

def show(errs: Errors)(using Context): String =
errs.map(_.show).mkString(", ")
import scala.collection.mutable

object Errors:
sealed trait Error {
def source: Tree
def trace: Seq[Tree]
def show(using Context): String

def issue(using Context): Unit =
report.warning(show + stacktrace, source.srcPos)
def pos(using Context): SourcePosition = trace.last.sourcePos

def toErrors: Errors = this :: Nil
def issue(using Context): Unit =
report.warning(show + stacktrace, this.pos)

def stacktrace(using Context): String = if (trace.isEmpty) "" else " Calling trace:\n" + {
var last: String = ""
val sb = new StringBuilder
def stacktrace(using Context): String = if trace.isEmpty then "" else " Calling trace:\n" + {
var lastLineNum = -1
var lines: mutable.ArrayBuffer[String] = new mutable.ArrayBuffer
trace.foreach { tree =>
val pos = tree.sourcePos
val prefix = "-> "
Expand All @@ -44,10 +39,16 @@ object Errors {
positionMarker(pos)
else ""

if (last != line) sb.append(prefix + line + "\n" + positionMarkerLine )
// always use the more precise trace location
if lastLineNum == pos.line then
lines.dropRightInPlace(1)

last = line
lines += (prefix + line + "\n" + positionMarkerLine)

lastLineNum = pos.line
}
val sb = new StringBuilder
for line <- lines do sb.append(line)
sb.toString
}

Expand All @@ -65,13 +66,6 @@ object Errors {
s"$padding$carets\n"
}

/** Flatten UnsafePromotion errors
*/
def flatten: Errors = this match {
case unsafe: UnsafePromotion => unsafe.errors.flatMap(_.flatten)
case _ => this :: Nil
}

override def toString() = this.getClass.getName.nn
}

Expand All @@ -81,42 +75,37 @@ object Errors {
def show(using Context): String =
"Access non-initialized " + field.show + "."

override def issue(using Context): Unit =
report.warning(show + stacktrace, field.srcPos)
override def pos(using Context): SourcePosition = field.sourcePos
}

/** Promote `this` under initialization to fully-initialized */
case class PromoteError(msg: String, source: Tree, trace: Seq[Tree]) extends Error {
def show(using Context): String = "Cannot prove that the value is fully initialized. " + msg + "."
/** Promote a value under initialization to fully-initialized */
case class PromoteError(msg: String, trace: Seq[Tree]) extends Error {
def show(using Context): String = msg
}

case class AccessCold(field: Symbol, source: Tree, trace: Seq[Tree]) extends Error {
case class AccessCold(field: Symbol, trace: Seq[Tree]) extends Error {
def show(using Context): String =
"Access field " + source.show + " on a value with an unknown initialization status."
"Access field on a value with an unknown initialization status."
}

case class CallCold(meth: Symbol, source: Tree, trace: Seq[Tree]) extends Error {
case class CallCold(meth: Symbol, trace: Seq[Tree]) extends Error {
def show(using Context): String =
"Call method " + source.show + " on a value with an unknown initialization" + "."
"Call method on a value with an unknown initialization" + "."
}

case class CallUnknown(meth: Symbol, source: Tree, trace: Seq[Tree]) extends Error {
case class CallUnknown(meth: Symbol, trace: Seq[Tree]) extends Error {
def show(using Context): String =
val prefix = if meth.is(Flags.Method) then "Calling the external method " else "Accessing the external field"
prefix + meth.show + " may cause initialization errors" + "."
}

/** Promote a value under initialization to fully-initialized */
case class UnsafePromotion(msg: String, source: Tree, trace: Seq[Tree], errors: Errors) extends Error {
assert(errors.nonEmpty)
case class UnsafePromotion(msg: String, trace: Seq[Tree], error: Error) extends Error {
override def issue(using Context): Unit =
report.warning(show, source.srcPos)
report.warning(show, this.pos)

def show(using Context): String = {
var index = 0
"Cannot prove that the value is fully initialized. " + msg + ".\n" + stacktrace +
"\nThe unsafe promotion may cause the following problem:\n" +
errors.head.show + errors.head.stacktrace
}
def show(using Context): String =
msg + stacktrace + "\n" +
"Promoting the value to fully initialized failed due to the following problem:\n" +
error.show + error.stacktrace
}
}
Loading