Skip to content

Fix performance degradation: make message fields lazy #8603

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

Merged
merged 13 commits into from
Mar 26, 2020
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
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import NameKinds.{UniqueName, EvidenceParamName, DefaultGetterName}
import typer.{FrontEnd, Namer}
import util.{Property, SourceFile, SourcePosition}
import collection.mutable.ListBuffer
import reporting.diagnostic.messages._
import reporting.messages._
import reporting.trace
import annotation.constructorOnly
import printing.Formatting.hl
Expand Down Expand Up @@ -981,7 +981,7 @@ object desugar {
var name = mdef.name
if (name.isEmpty) name = name.likeSpaced(inventGivenOrExtensionName(impl))
if (ctx.owner == defn.ScalaPackageClass && defn.reservedScalaClassNames.contains(name.toTypeName)) {
def kind = if (name.isTypeName) "class" else "object"
val kind = if (name.isTypeName) "class" else "object"
ctx.error(IllegalRedefinitionOfStandardKind(kind, name), mdef.sourcePos)
name = name.errorName
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Comments.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import util.Spans._
import util.CommentParsing._
import util.Property.Key
import parsing.Parsers.Parser
import reporting.diagnostic.messages.ProperDefinitionNotFound
import reporting.messages.ProperDefinitionNotFound

object Comments {
val ContextDoc: Key[ContextDocstrings] = new Key[ContextDocstrings]
Expand Down
9 changes: 4 additions & 5 deletions compiler/src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import Implicits.ContextualImplicits
import config.Settings._
import config.Config
import reporting._
import reporting.diagnostic.Message
import io.{AbstractFile, NoAbstractFile, PlainFile, Path}
import scala.io.Codec
import collection.mutable
Expand Down Expand Up @@ -750,11 +749,11 @@ object Contexts {
* of underlying during a controlled operation exists. */
private[core] val pendingUnderlying: mutable.HashSet[Type] = new mutable.HashSet[Type]

/** A map from ErrorType to associated message computation. We use this map
* instead of storing message computations directly in ErrorTypes in order
* to avoid space leaks - the message computation usually captures a context.
/** A map from ErrorType to associated message. We use this map
* instead of storing messages directly in ErrorTypes in order
* to avoid space leaks - the message usually captures a context.
*/
private[core] val errorTypeMsg: mutable.Map[Types.ErrorType, () => Message] = mutable.Map()
private[core] val errorTypeMsg: mutable.Map[Types.ErrorType, Message] = mutable.Map()

// Phases state

Expand Down
9 changes: 4 additions & 5 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ import util.Stats
import java.util.WeakHashMap
import scala.util.control.NonFatal
import config.Config
import reporting.diagnostic.Message
import reporting.diagnostic.messages.BadSymbolicReference
import reporting.trace
import reporting.{Message, trace}
import reporting.messages.BadSymbolicReference
import collection.mutable
import transform.TypeUtils._

Expand Down Expand Up @@ -2368,7 +2367,7 @@ object SymDenotations {
/** A completer for missing references */
class StubInfo() extends LazyType {

def initializeToDefaults(denot: SymDenotation, errMsg: => Message)(implicit ctx: Context): Unit = {
def initializeToDefaults(denot: SymDenotation, errMsg: Message)(implicit ctx: Context): Unit = {
denot.info = denot match {
case denot: ClassDenotation =>
ClassInfo(denot.owner.thisType, denot.classSymbol, Nil, EmptyScope)
Expand All @@ -2380,7 +2379,7 @@ object SymDenotations {

def complete(denot: SymDenotation)(implicit ctx: Context): Unit = {
val sym = denot.symbol
def errMsg = BadSymbolicReference(denot)
val errMsg = BadSymbolicReference(denot)
ctx.error(errMsg, sym.sourcePos)
if (ctx.debug) throw new scala.Error()
initializeToDefaults(denot, errMsg)
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/core/Symbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import tpd.{Tree, TreeProvider, TreeOps}
import ast.TreeTypeMap
import Constants.Constant
import Variances.{Variance, varianceFromInt}
import reporting.diagnostic.Message
import reporting.Message
import collection.mutable
import io.AbstractFile
import language.implicitConversions
Expand Down Expand Up @@ -300,7 +300,7 @@ trait Symbols { this: Context =>
*/
def newSkolem(tp: Type): TermSymbol = newSymbol(defn.RootClass, nme.SKOLEM, SyntheticArtifact | NonMember | Permanent, tp)

def newErrorSymbol(owner: Symbol, name: Name, msg: => Message): Symbol = {
def newErrorSymbol(owner: Symbol, name: Name, msg: Message): Symbol = {
val errType = ErrorType(msg)
newSymbol(owner, name, SyntheticArtifact,
if (name.isTypeName) TypeAlias(errType) else errType)
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/core/TypeErrors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import Contexts._
import SymDenotations._
import Denotations._
import Decorators._
import reporting.diagnostic.Message
import reporting.diagnostic.messages._
import reporting.{Message, NoExplanation}
import reporting.messages._
import ast.untpd
import config.Printers.cyclicErrors

Expand Down Expand Up @@ -69,7 +69,7 @@ class RecursionOverflow(val op: String, details: => String, val previous: Throwa
(rs.map(_.explanation): List[String]).mkString("\n ", "\n| ", "")
}

override def produceMessage(implicit ctx: Context): Message = {
override def produceMessage(implicit ctx: Context): Message = NoExplanation {
val mostCommon = recursions.groupBy(_.op).toList.maxBy(_._2.map(_.weight).sum)._2.reverse
s"""Recursion limit exceeded.
|Maybe there is an illegal cyclic reference?
Expand Down
5 changes: 2 additions & 3 deletions compiler/src/dotty/tools/dotc/core/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ import Decorators._
import StdNames._
import collection.mutable
import ast.tpd._
import reporting.trace
import reporting.diagnostic.Message
import reporting.{trace, Message}
import config.Printers.{gadts, typr}
import typer.Applications._
import typer.ProtoTypes._
Expand Down Expand Up @@ -516,7 +515,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
def dynamicsEnabled: Boolean =
featureEnabled(nme.dynamics)

def testScala2CompatMode(msg: => Message, pos: SourcePosition, replace: => Unit = ()): Boolean = {
def testScala2CompatMode(msg: Message, pos: SourcePosition, replace: => Unit = ()): Boolean = {
if (scala2CompatMode) {
migrationWarning(msg, pos)
replace
Expand Down
22 changes: 9 additions & 13 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import CheckRealizable._
import Variances.{Variance, varianceFromInt, varianceToInt, setStructuralVariances, Invariant}
import util.Stats._
import util.SimpleIdentitySet
import reporting.diagnostic.Message
import ast.tpd._
import ast.TreeTypeMap
import printing.Texts._
Expand All @@ -34,7 +33,7 @@ import annotation.{tailrec, constructorOnly}
import language.implicitConversions
import scala.util.hashing.{ MurmurHash3 => hashing }
import config.Printers.{core, typr}
import reporting.trace
import reporting.{trace, Message}
import java.lang.ref.WeakReference

import scala.annotation.internal.sharable
Expand Down Expand Up @@ -4650,19 +4649,16 @@ object Types {
def msg(implicit ctx: Context): Message
}

object ErrorType {
def apply(msg: => Message)(implicit ctx: Context): ErrorType = {
val et = new ErrorType {
def msg(implicit ctx: Context): Message =
ctx.base.errorTypeMsg.get(this) match {
case Some(msgFun) => msgFun()
object ErrorType:
def apply(m: Message)(implicit ctx: Context): ErrorType =
val et = new ErrorType:
def msg(using ctx: Context): Message =
ctx.base.errorTypeMsg.get(this) match
case Some(m) => m
case None => "error message from previous run no longer available"
}
}
ctx.base.errorTypeMsg(et) = () => msg
ctx.base.errorTypeMsg(et) = m
et
}
}
end ErrorType

object UnspecifiedErrorType extends ErrorType {
override def msg(implicit ctx: Context): Message = "unspecified error"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import scala.collection.mutable
import scala.collection.mutable.ListBuffer
import scala.annotation.switch
import reporting.trace
import dotty.tools.dotc.reporting.diagnostic.messages.FailureToEliminateExistential
import dotty.tools.dotc.reporting.messages.FailureToEliminateExistential

object Scala2Unpickler {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import ast.{Trees, tpd}
import core._, core.Decorators._
import Contexts._, Names._, NameOps._, Symbols._, SymDenotations._, Trees._, Types._
import classpath._
import reporting._, reporting.diagnostic.MessageContainer
import reporting._
import util._

/** A Driver subclass designed to be used from IDEs */
Expand Down Expand Up @@ -138,9 +138,9 @@ class InteractiveDriver(val settings: List[String]) extends Driver {
(fromSource ++ fromClassPath).distinct
}

def run(uri: URI, sourceCode: String): List[MessageContainer] = run(uri, toSource(uri, sourceCode))
def run(uri: URI, sourceCode: String): List[Diagnostic] = run(uri, toSource(uri, sourceCode))

def run(uri: URI, source: SourceFile): List[MessageContainer] = {
def run(uri: URI, source: SourceFile): List[Diagnostic] = {
val previousCtx = myCtx
try {
val reporter =
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import Symbols._
import ast.Trees._
import Decorators._
import StdNames._
import dotty.tools.dotc.reporting.diagnostic.messages.IdentifierExpected
import dotty.tools.dotc.reporting.messages.IdentifierExpected
import dotty.tools.dotc.util.SourceFile
import util.Spans._
import scala.collection.mutable.ListBuffer
Expand Down
18 changes: 9 additions & 9 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ import rewrites.Rewrites.{patch, overlapsPatch}
object Parsers {

import ast.untpd._
import reporting.diagnostic.Message
import reporting.diagnostic.messages._
import reporting.Message
import reporting.messages._

case class OpInfo(operand: Tree, operator: Ident, offset: Offset)

Expand Down Expand Up @@ -131,7 +131,7 @@ object Parsers {
/** Issue an error at given offset if beyond last error offset
* and update lastErrorOffset.
*/
def syntaxError(msg: => Message, offset: Int = in.offset): Unit =
def syntaxError(msg: Message, offset: Int = in.offset): Unit =
if (offset > lastErrorOffset) {
val length = if (offset == in.offset && in.name != null) in.name.show.length else 0
syntaxError(msg, Span(offset, offset + length))
Expand All @@ -141,7 +141,7 @@ object Parsers {
/** Unconditionally issue an error at given span, without
* updating lastErrorOffset.
*/
def syntaxError(msg: => Message, span: Span): Unit =
def syntaxError(msg: Message, span: Span): Unit =
ctx.error(msg, source.atSpan(span))

def unimplementedExpr(implicit ctx: Context): Select =
Expand Down Expand Up @@ -308,23 +308,23 @@ object Parsers {
}
}

def warning(msg: => Message, sourcePos: SourcePosition): Unit =
def warning(msg: Message, sourcePos: SourcePosition): Unit =
ctx.warning(msg, sourcePos)

def warning(msg: => Message, offset: Int = in.offset): Unit =
def warning(msg: Message, offset: Int = in.offset): Unit =
ctx.warning(msg, source.atSpan(Span(offset)))

def deprecationWarning(msg: => Message, offset: Int = in.offset): Unit =
def deprecationWarning(msg: Message, offset: Int = in.offset): Unit =
ctx.deprecationWarning(msg, source.atSpan(Span(offset)))

/** Issue an error at current offset that input is incomplete */
def incompleteInputError(msg: => Message): Unit =
def incompleteInputError(msg: Message): Unit =
ctx.incompleteInputError(msg, source.atSpan(Span(in.offset)))

/** If at end of file, issue an incompleteInputError.
* Otherwise issue a syntax error and skip to next safe point.
*/
def syntaxErrorOrIncomplete(msg: => Message, offset: Int = in.offset): Unit =
def syntaxErrorOrIncomplete(msg: Message, offset: Int = in.offset): Unit =
if (in.token == EOF) incompleteInputError(msg)
else {
syntaxError(msg, offset)
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/printing/Formatting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Texts._, Types._, Flags._, Symbols._, Contexts._
import collection.mutable
import Decorators._
import scala.util.control.NonFatal
import reporting.diagnostic.MessageContainer
import reporting.Message
import util.DiffUtil
import Highlighting._

Expand Down Expand Up @@ -88,7 +88,7 @@ object Formatting {
}

private def wrapNonSensical(arg: Any, str: String)(implicit ctx: Context): String = {
import MessageContainer._
import Message._
def isSensical(arg: Any): Boolean = arg match {
case tpe: Type =>
tpe.exists && !tpe.isErroneous
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ class PlainPrinter(_ctx: Context) extends Printer {
(" <: " ~ toText(bound) provided !bound.isAny)
}.close
case tp: ErrorType =>
s"<error ${tp.msg.msg}>"
s"<error ${tp.msg.rawMessage}>"
case tp: WildcardType =>
if (tp.optBounds.exists) "<?" ~ toTextRHS(tp.bounds) ~ ">" else "<?>"
case NoType =>
Expand Down
25 changes: 12 additions & 13 deletions compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ package reporting

import core.Contexts._
import java.io.{ BufferedReader, PrintWriter }
import diagnostic.MessageContainer
import diagnostic.messages.{ Error, ConditionalWarning }
import Diagnostic.{ Error, ConditionalWarning }

/**
* This class implements a Reporter that displays messages on a text console
Expand All @@ -15,28 +14,28 @@ class ConsoleReporter(
writer: PrintWriter = new PrintWriter(Console.err, true)
) extends AbstractReporter {

import MessageContainer._
import Diagnostic._

/** Prints the message. */
def printMessage(msg: String): Unit = { writer.print(msg + "\n"); writer.flush() }

/** Prints the message with the given position indication. */
def doReport(m: MessageContainer)(implicit ctx: Context): Unit = {
val didPrint = m match {
case m: Error =>
printMessage(messageAndPos(m.contained, m.pos, diagnosticLevel(m)))
def doReport(dia: Diagnostic)(implicit ctx: Context): Unit = {
val didPrint = dia match {
case dia: Error =>
printMessage(messageAndPos(dia.msg, dia.pos, diagnosticLevel(dia)))
if (ctx.settings.Xprompt.value) Reporter.displayPrompt(reader, writer)
true
case m: ConditionalWarning if !m.enablingOption.value =>
case dia: ConditionalWarning if !dia.enablingOption.value =>
false
case m =>
printMessage(messageAndPos(m.contained, m.pos, diagnosticLevel(m)))
case dia =>
printMessage(messageAndPos(dia.msg, dia.pos, diagnosticLevel(dia)))
true
}

if (didPrint && ctx.shouldExplain(m))
printMessage(explanation(m.contained))
else if (didPrint && m.contained.explanation.nonEmpty)
if (didPrint && shouldExplain(dia))
printMessage(explanation(dia.msg))
else if (didPrint && dia.msg.explanation.nonEmpty)
printMessage("\nlonger explanation available when compiling with `-explain`")
}

Expand Down
Loading