Skip to content

Commit f959eef

Browse files
committed
Refactor TypeErrors
Let them take Messages instead of Strings. We now are in a situation where the `e` interpolator is only used within `messages` itself. This means we can hopefully roll its behavior into the `Message` logic.
1 parent 66da254 commit f959eef

15 files changed

+89
-82
lines changed

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
428428
else
429429
val res = Select(TypeTree(pre), tp)
430430
if needLoad && !res.symbol.isStatic then
431-
throw new TypeError(e"cannot establish a reference to $res")
431+
throw TypeError(em"cannot establish a reference to $res")
432432
res
433433

434434
def ref(sym: Symbol)(using Context): Tree =

compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,8 @@ trait ConstraintHandling {
152152
return param
153153
LevelAvoidMap(0, maxLevel)(param) match
154154
case freshVar: TypeVar => freshVar.origin
155-
case _ => throw new TypeError(
156-
i"Could not decrease the nesting level of ${param} from ${nestingLevel(param)} to $maxLevel in $constraint")
155+
case _ => throw TypeError(
156+
em"Could not decrease the nesting level of ${param} from ${nestingLevel(param)} to $maxLevel in $constraint")
157157

158158
def nonParamBounds(param: TypeParamRef)(using Context): TypeBounds = constraint.nonParamBounds(param)
159159

compiler/src/dotty/tools/dotc/core/Decorators.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,10 @@ object Decorators {
269269
catch
270270
case ex: CyclicReference => "... (caught cyclic reference) ..."
271271
case NonFatal(ex)
272-
if !ctx.mode.is(Mode.PrintShowExceptions) && !ctx.settings.YshowPrintErrors.value =>
273-
val msg = ex match { case te: TypeError => te.toMessage case _ => ex.getMessage }
272+
if !ctx.mode.is(Mode.PrintShowExceptions) && !ctx.settings.YshowPrintErrors.value =>
273+
val msg = ex match
274+
case te: TypeError => te.toMessage.message
275+
case _ => ex.getMessage
274276
s"[cannot display due to $msg, raw string = $x]"
275277
case _ => String.valueOf(x).nn
276278

compiler/src/dotty/tools/dotc/core/Denotations.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -300,9 +300,9 @@ object Denotations {
300300
case NoDenotation | _: NoQualifyingRef | _: MissingRef =>
301301
def argStr = if (args.isEmpty) "" else i" matching ($args%, %)"
302302
val msg =
303-
if (site.exists) i"$site does not have a member $kind $name$argStr"
304-
else i"missing: $kind $name$argStr"
305-
throw new TypeError(msg)
303+
if site.exists then em"$site does not have a member $kind $name$argStr"
304+
else em"missing: $kind $name$argStr"
305+
throw TypeError(msg)
306306
case denot =>
307307
denot.symbol
308308
}
@@ -1268,9 +1268,9 @@ object Denotations {
12681268
if sd1.exists then
12691269
if sd2.exists then
12701270
throw TypeError(
1271-
e"""Failure to disambiguate overloaded reference with
1272-
| ${denot1.symbol.showLocated}: ${denot1.info} and
1273-
| ${denot2.symbol.showLocated}: ${denot2.info}""")
1271+
em"""Failure to disambiguate overloaded reference with
1272+
| ${denot1.symbol.showLocated}: ${denot1.info} and
1273+
| ${denot2.symbol.showLocated}: ${denot2.info}""")
12741274
else sd1
12751275
else sd2
12761276
}

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,8 @@ object SymDenotations {
168168
}
169169
}
170170
else {
171-
if (myFlags.is(Touched)) throw CyclicReference(this)
171+
if (myFlags.is(Touched))
172+
throw CyclicReference(this)(using ctx.withOwner(symbol))
172173
myFlags |= Touched
173174
atPhase(validFor.firstPhaseId)(completer.complete(this))
174175
}
@@ -2438,13 +2439,13 @@ object SymDenotations {
24382439
val youngest = assocFiles.filter(_.lastModified == lastModDate)
24392440
val chosen = youngest.head
24402441
def ambiguousFilesMsg(f: AbstractFile) =
2441-
e"""Toplevel definition $name is defined in
2442+
i"""Toplevel definition $name is defined in
24422443
| $chosen
24432444
|and also in
24442445
| $f"""
24452446
if youngest.size > 1 then
2446-
throw TypeError(i"""${ambiguousFilesMsg(youngest.tail.head)}
2447-
|One of these files should be removed from the classpath.""")
2447+
throw TypeError(em"""${ambiguousFilesMsg(youngest.tail.head)}
2448+
|One of these files should be removed from the classpath.""")
24482449

24492450
// Warn if one of the older files comes from a different container.
24502451
// In that case picking the youngest file is not necessarily what we want,

compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ object SymbolLoaders {
8888
return NoSymbol
8989
}
9090
else
91-
throw new TypeError(
92-
i"""$owner contains object and package with same name: $pname
91+
throw TypeError(
92+
em"""$owner contains object and package with same name: $pname
9393
|one of them needs to be removed from classpath""")
9494
newModuleSymbol(owner, pname, PackageCreationFlags, PackageCreationFlags,
9595
completer).entered

compiler/src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3164,7 +3164,7 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
31643164
tp
31653165
case Nil =>
31663166
val casesText = MatchTypeTrace.noMatchesText(scrut, cases)
3167-
throw new TypeError(s"Match type reduction $casesText")
3167+
throw TypeError(em"Match type reduction $casesText")
31683168

31693169
inFrozenConstraint {
31703170
// Empty types break the basic assumption that if a scrutinee and a

compiler/src/dotty/tools/dotc/core/TypeErrors.scala

Lines changed: 46 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -14,37 +14,44 @@ import reporting._
1414
import ast.untpd
1515
import config.Printers.cyclicErrors
1616

17-
class TypeError(msg: String) extends Exception(msg) {
18-
def this() = this("")
19-
final def toMessage(using Context): Message =
20-
withMode(Mode.Printing)(produceMessage)
21-
def produceMessage(using Context): Message = super.getMessage.nn.toMessage
22-
override def getMessage: String = super.getMessage.nn
23-
}
24-
25-
class MalformedType(pre: Type, denot: Denotation, absMembers: Set[Name]) extends TypeError {
26-
override def produceMessage(using Context): Message =
27-
i"malformed type: $pre is not a legal prefix for $denot because it contains abstract type member${if (absMembers.size == 1) "" else "s"} ${absMembers.mkString(", ")}"
28-
.toMessage
29-
}
30-
31-
class MissingType(pre: Type, name: Name) extends TypeError {
17+
abstract class TypeError(using creationContext: Context) extends Exception(""):
18+
19+
/** Convert to message. This uses an additional Context, so that we
20+
* use the context when the message is first produced, i.e. when the TypeError
21+
* is handled. This makes a difference for CyclicErrors since we need to know
22+
* the context where the completed symbol is referenced, but the creation
23+
* context of the CyclicReference is the completion context for the symbol.
24+
* See i2887b for a test case, where we want to see
25+
* "recursive or overloaded method needs result type".
26+
*/
27+
def toMessage(using Context): Message
28+
29+
/** Uses creation context to produce the message */
30+
override def getMessage: String = toMessage.message
31+
32+
object TypeError:
33+
def apply(msg: Message)(using Context) = new TypeError:
34+
def toMessage(using Context) = msg
35+
end TypeError
36+
37+
class MalformedType(pre: Type, denot: Denotation, absMembers: Set[Name])(using Context) extends TypeError:
38+
def toMessage(using Context) = em"malformed type: $pre is not a legal prefix for $denot because it contains abstract type member${if (absMembers.size == 1) "" else "s"} ${absMembers.mkString(", ")}"
39+
40+
class MissingType(pre: Type, name: Name)(using Context) extends TypeError:
3241
private def otherReason(pre: Type)(using Context): String = pre match {
3342
case pre: ThisType if pre.cls.givenSelfType.exists =>
3443
i"\nor the self type of $pre might not contain all transitive dependencies"
3544
case _ => ""
3645
}
3746

38-
override def produceMessage(using Context): Message = {
39-
if (ctx.debug) printStackTrace()
40-
i"""cannot resolve reference to type $pre.$name
41-
|the classfile defining the type might be missing from the classpath${otherReason(pre)}"""
42-
.toMessage
43-
}
44-
}
47+
override def toMessage(using Context): Message =
48+
if ctx.debug then printStackTrace()
49+
em"""cannot resolve reference to type $pre.$name
50+
|the classfile defining the type might be missing from the classpath${otherReason(pre)}"""
51+
end MissingType
4552

46-
class RecursionOverflow(val op: String, details: => String, val previous: Throwable, val weight: Int)
47-
extends TypeError {
53+
class RecursionOverflow(val op: String, details: => String, val previous: Throwable, val weight: Int)(using Context)
54+
extends TypeError:
4855

4956
def explanation: String = s"$op $details"
5057

@@ -71,19 +78,18 @@ extends TypeError {
7178
(rs.map(_.explanation): List[String]).mkString("\n ", "\n| ", "")
7279
}
7380

74-
override def produceMessage(using Context): Message = NoExplanation {
81+
override def toMessage(using Context): Message =
7582
val mostCommon = recursions.groupBy(_.op).toList.maxBy(_._2.map(_.weight).sum)._2.reverse
76-
s"""Recursion limit exceeded.
77-
|Maybe there is an illegal cyclic reference?
78-
|If that's not the case, you could also try to increase the stacksize using the -Xss JVM option.
79-
|For the unprocessed stack trace, compile with -Yno-decode-stacktraces.
80-
|A recurring operation is (inner to outer):
81-
|${opsString(mostCommon)}""".stripMargin
82-
}
83+
em"""Recursion limit exceeded.
84+
|Maybe there is an illegal cyclic reference?
85+
|If that's not the case, you could also try to increase the stacksize using the -Xss JVM option.
86+
|For the unprocessed stack trace, compile with -Yno-decode-stacktraces.
87+
|A recurring operation is (inner to outer):
88+
|${opsString(mostCommon).stripMargin}"""
8389

8490
override def fillInStackTrace(): Throwable = this
8591
override def getStackTrace(): Array[StackTraceElement] = previous.getStackTrace().asInstanceOf
86-
}
92+
end RecursionOverflow
8793

8894
/** Post-process exceptions that might result from StackOverflow to add
8995
* tracing information while unwalking the stack.
@@ -111,10 +117,10 @@ object handleRecursive {
111117
* so it requires knowing denot already.
112118
* @param denot
113119
*/
114-
class CyclicReference private (val denot: SymDenotation) extends TypeError {
120+
class CyclicReference private (val denot: SymDenotation)(using Context) extends TypeError:
115121
var inImplicitSearch: Boolean = false
116122

117-
override def produceMessage(using Context): Message = {
123+
override def toMessage(using Context): Message =
118124
val cycleSym = denot.symbol
119125

120126
// cycleSym.flags would try completing denot and would fail, but here we can use flagsUNSAFE to detect flags
@@ -151,19 +157,16 @@ class CyclicReference private (val denot: SymDenotation) extends TypeError {
151157
CyclicReferenceInvolving(denot)
152158

153159
errorMsg(ctx)
154-
}
155-
}
160+
end toMessage
156161

157-
object CyclicReference {
158-
def apply(denot: SymDenotation)(using Context): CyclicReference = {
162+
object CyclicReference:
163+
def apply(denot: SymDenotation)(using Context): CyclicReference =
159164
val ex = new CyclicReference(denot)
160-
if (!(ctx.mode is Mode.CheckCyclic) || ctx.settings.Ydebug.value) {
165+
if !(ctx.mode is Mode.CheckCyclic) || ctx.settings.Ydebug.value then
161166
cyclicErrors.println(s"Cyclic reference involving! $denot")
162167
val sts = ex.getStackTrace.asInstanceOf[Array[StackTraceElement]]
163168
for (elem <- sts take 200)
164169
cyclicErrors.println(elem.toString)
165-
}
166170
ex
167-
}
168-
}
171+
end CyclicReference
169172

compiler/src/dotty/tools/dotc/core/TypeEval.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ object TypeEval:
9191
val result =
9292
try op
9393
catch case e: Throwable =>
94-
throw new TypeError(e.getMessage.nn)
94+
throw TypeError(em"${e.getMessage.nn}")
9595
ConstantType(Constant(result))
9696

9797
def constantFold1[T](extractor: Type => Option[T], op: T => Any): Option[Type] =

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2414,12 +2414,12 @@ object Types {
24142414
}
24152415
else {
24162416
if (!ctx.reporter.errorsReported)
2417-
throw new TypeError(
2418-
i"""bad parameter reference $this at ${ctx.phase}
2419-
|the parameter is ${param.showLocated} but the prefix $prefix
2420-
|does not define any corresponding arguments.
2421-
|idx = $idx, args = $args%, %,
2422-
|constraint = ${ctx.typerState.constraint}""")
2417+
throw TypeError(
2418+
em"""bad parameter reference $this at ${ctx.phase}
2419+
|the parameter is ${param.showLocated} but the prefix $prefix
2420+
|does not define any corresponding arguments.
2421+
|idx = $idx, args = $args%, %,
2422+
|constraint = ${ctx.typerState.constraint}""")
24232423
NoDenotation
24242424
}
24252425
}

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ class TreePickler(pickler: TastyPickler) {
451451
withLength {
452452
pickleTree(qual);
453453
if (!mix.isEmpty) {
454-
// mixinType being a TypeRef when mix is non-empty is enforced by TreeChecker#checkSuper
454+
// mixinType being a TypeRef when mix is non-empty is enforced by TreeChecker#checkSuper
455455
val SuperType(_, mixinType: TypeRef) = tree.tpe: @unchecked
456456
pickleTree(mix.withType(mixinType))
457457
}

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,10 @@ class TreeUnpickler(reader: TastyReader,
127127
if f == null then "" else s" in $f"
128128
if ctx.settings.YdebugUnpickling.value then throw ex
129129
else throw TypeError(
130-
e"""Could not read definition of $denot$where
131-
|An exception was encountered:
132-
| $ex
133-
|Run with -Ydebug-unpickling to see full stack trace.""")
130+
em"""Could not read definition of $denot$where
131+
|An exception was encountered:
132+
| $ex
133+
|Run with -Ydebug-unpickling to see full stack trace.""")
134134
treeAtAddr(currentAddr) =
135135
try
136136
atPhaseBeforeTransforms {

compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Erasure.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ object Scala2Erasure:
3939
case RefinedType(parent, _, _) =>
4040
checkSupported(parent)
4141
case AnnotatedType(parent, _) if parent.dealias.isInstanceOf[Scala2RefinedType] =>
42-
throw new TypeError(i"Unsupported Scala 2 type: Component $parent of intersection is annotated.")
42+
throw TypeError(em"Unsupported Scala 2 type: Component $parent of intersection is annotated.")
4343
case tp @ TypeRef(prefix, _) if !tp.symbol.exists && prefix.dealias.isInstanceOf[Scala2RefinedType] =>
44-
throw new TypeError(i"Unsupported Scala 2 type: Prefix $prefix of intersection component is an intersection or refinement.")
44+
throw TypeError(em"Unsupported Scala 2 type: Prefix $prefix of intersection component is an intersection or refinement.")
4545
case _ =>
4646

4747
/** A type that would be represented as a RefinedType in Scala 2.

compiler/src/dotty/tools/dotc/quoted/Interpreter.scala

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import dotty.tools.dotc.quoted._
2828
import dotty.tools.dotc.transform.TreeMapWithStages._
2929
import dotty.tools.dotc.typer.ImportInfo.withRootImports
3030
import dotty.tools.dotc.util.SrcPos
31+
import dotty.tools.dotc.reporting.Message
3132
import dotty.tools.repl.AbstractFileClassLoader
3233

3334
/** Tree interpreter for metaprogramming constructs */
@@ -60,7 +61,7 @@ abstract class Interpreter(pos: SrcPos, classLoader: ClassLoader)(using Context)
6061
case tree: Ident if tree.symbol.is(Inline, butNot = Method) =>
6162
tree.tpe.widenTermRefExpr match
6263
case ConstantType(c) => c.value.asInstanceOf[Object]
63-
case _ => throw new StopInterpretation(e"${tree.symbol} could not be inlined", tree.srcPos)
64+
case _ => throw new StopInterpretation(em"${tree.symbol} could not be inlined", tree.srcPos)
6465

6566
// TODO disallow interpreted method calls as arguments
6667
case Call(fn, args) =>
@@ -190,7 +191,7 @@ abstract class Interpreter(pos: SrcPos, classLoader: ClassLoader)(using Context)
190191
}
191192

192193
private def unexpectedTree(tree: Tree)(implicit env: Env): Object =
193-
throw new StopInterpretation("Unexpected tree could not be interpreted: " + tree, tree.srcPos)
194+
throw new StopInterpretation(em"Unexpected tree could not be interpreted: ${tree.toString}", tree.srcPos)
194195

195196
private def loadModule(sym: Symbol): Object =
196197
if (sym.owner.is(Package)) {
@@ -231,7 +232,7 @@ abstract class Interpreter(pos: SrcPos, classLoader: ClassLoader)(using Context)
231232
try clazz.getMethod(name.toString, paramClasses: _*)
232233
catch {
233234
case _: NoSuchMethodException =>
234-
val msg = e"Could not find method ${clazz.getCanonicalName}.$name with parameters ($paramClasses%, %)"
235+
val msg = em"Could not find method ${clazz.getCanonicalName}.$name with parameters ($paramClasses%, %)"
235236
throw new StopInterpretation(msg, pos)
236237
case MissingClassDefinedInCurrentRun(sym) if ctx.compilationUnit.isSuspendable =>
237238
if (ctx.settings.XprintSuspension.value)
@@ -249,7 +250,7 @@ abstract class Interpreter(pos: SrcPos, classLoader: ClassLoader)(using Context)
249250
sw.write("\n")
250251
ex.printStackTrace(new PrintWriter(sw))
251252
sw.write("\n")
252-
throw new StopInterpretation(sw.toString, pos)
253+
throw new StopInterpretation(sw.toString.toMessage, pos)
253254
case ex: InvocationTargetException =>
254255
ex.getTargetException match {
255256
case ex: scala.quoted.runtime.StopMacroExpansion =>
@@ -270,7 +271,7 @@ abstract class Interpreter(pos: SrcPos, classLoader: ClassLoader)(using Context)
270271
}
271272
targetException.printStackTrace(new PrintWriter(sw))
272273
sw.write("\n")
273-
throw new StopInterpretation(sw.toString, pos)
274+
throw new StopInterpretation(sw.toString.toMessage, pos)
274275
}
275276
}
276277

@@ -344,7 +345,7 @@ end Interpreter
344345

345346
object Interpreter:
346347
/** Exception that stops interpretation if some issue is found */
347-
class StopInterpretation(val msg: String, val pos: SrcPos) extends Exception
348+
class StopInterpretation(val msg: Message, val pos: SrcPos) extends Exception
348349

349350
object Call:
350351
import tpd._

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1096,7 +1096,7 @@ trait Applications extends Compatibility {
10961096
/** Overridden in ReTyper to handle primitive operations that can be generated after erasure */
10971097
protected def handleUnexpectedFunType(tree: untpd.Apply, fun: Tree)(using Context): Tree =
10981098
if ctx.reporter.errorsReported then
1099-
throw TypeError(i"unexpected function type: ${methPart(fun).tpe}")
1099+
throw TypeError(em"unexpected function type: ${methPart(fun).tpe}")
11001100
else
11011101
throw Error(i"unexpected type.\n fun = $fun,\n methPart(fun) = ${methPart(fun)},\n methPart(fun).tpe = ${methPart(fun).tpe},\n tpe = ${fun.tpe}")
11021102

0 commit comments

Comments
 (0)