Skip to content

Commit 1975856

Browse files
committed
Merge -explain-types behavior into -explain behavior
Eliminate -explain-types option. Error messages that can show subtype traces now do so under the -explain mechanism.
1 parent 086d1a8 commit 1975856

File tree

11 files changed

+111
-90
lines changed

11 files changed

+111
-90
lines changed

compiler/src/dotty/tools/dotc/config/ScalaSettings.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,6 @@ class ScalaSettings extends Settings.SettingGroup with CommonScalaSettings {
103103
val semanticdbTarget: Setting[String] = PathSetting("-semanticdb-target", "Specify an alternative output directory for SemanticDB files.", "")
104104

105105
val deprecation: Setting[Boolean] = BooleanSetting("-deprecation", "Emit warning and location for usages of deprecated APIs.", aliases = List("--deprecation"))
106-
val explainTypes: Setting[Boolean] = BooleanSetting("-explain-types", "Explain type errors in more detail.", aliases = List("--explain-types"))
107106
val explain: Setting[Boolean] = BooleanSetting("-explain", "Explain errors in more detail.", aliases = List("--explain"))
108107
val feature: Setting[Boolean] = BooleanSetting("-feature", "Emit warning and location for usages of features that should be imported explicitly.", aliases = List("--feature"))
109108
val release: Setting[String] = ChoiceSetting("-release", "release", "Compile code with classes specific to the given version of the Java platform available on the classpath and emit bytecode for this version.", supportedReleaseVersions, "", aliases = List("--release"))

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,4 +168,7 @@ abstract class Constraint extends Showable {
168168

169169
/** Check that constraint only refers to TypeParamRefs bound by itself */
170170
def checkClosed()(using Context): Unit
171+
172+
/** A string describing the constraint's contents without a header or trailer */
173+
def contentsToString(using Context): String
171174
}

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

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -648,7 +648,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
648648

649649
// ---------- toText -----------------------------------------------------
650650

651-
override def toText(printer: Printer): Text = {
651+
private def contentsToText(printer: Printer): Text =
652652
//Printer.debugPrintUnique = true
653653
def entryText(tp: Type) = tp match {
654654
case tp: TypeBounds =>
@@ -657,20 +657,19 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
657657
" := " ~ tp.toText(printer)
658658
}
659659
val indent = 3
660-
val header: Text = "Constraint("
661-
val uninstVarsText = " uninstVars = " ~
662-
Text(uninstVars map (_.toText(printer)), ", ") ~ ";"
660+
val uninstVarsText = " uninstantiated variables: " ~
661+
Text(uninstVars.map(_.toText(printer)), ", ")
663662
val constrainedText =
664-
" constrained types = " ~ Text(domainLambdas map (_.toText(printer)), ", ")
663+
" constrained types: " ~ Text(domainLambdas map (_.toText(printer)), ", ")
665664
val boundsText =
666-
" bounds = " ~ {
665+
" bounds: " ~ {
667666
val assocs =
668667
for (param <- domainParams)
669668
yield (" " * indent) ~ param.toText(printer) ~ entryText(entry(param))
670669
Text(assocs, "\n")
671670
}
672671
val orderingText =
673-
" ordering = " ~ {
672+
" ordering: " ~ {
674673
val deps =
675674
for {
676675
param <- domainParams
@@ -683,8 +682,13 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
683682
Text(deps, "\n")
684683
}
685684
//Printer.debugPrintUnique = false
686-
Text.lines(List(header, uninstVarsText, constrainedText, boundsText, orderingText, ")"))
687-
}
685+
Text.lines(List(uninstVarsText, constrainedText, boundsText, orderingText))
686+
687+
override def toText(printer: Printer): Text =
688+
Text.lines(List("Constraint(", contentsToText(printer), ")"))
689+
690+
def contentsToString(using Context): String =
691+
contentsToText(ctx.printer).show
688692

689693
override def toString: String = {
690694
def entryText(tp: Type): String = tp match {

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

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
206206
* code would have two extra parameters for each of the many calls that go from
207207
* one sub-part of isSubType to another.
208208
*/
209-
protected def recur(tp1: Type, tp2: Type): Boolean = trace(s"isSubType ${traceInfo(tp1, tp2)} ${approx.show}", subtyping) {
209+
protected def recur(tp1: Type, tp2: Type): Boolean = trace(s"isSubType ${traceInfo(tp1, tp2)}${approx.show}", subtyping) {
210210

211211
def monitoredIsSubType = {
212212
if (pendingSubTypes == null) {
@@ -2276,13 +2276,6 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
22762276
NoType
22772277
}
22782278

2279-
/** Show type, handling type types better than the default */
2280-
private def showType(tp: Type)(using Context) = tp match {
2281-
case ClassInfo(_, cls, _, _, _) => cls.showLocated
2282-
case bounds: TypeBounds => "type bounds" + bounds.show
2283-
case _ => tp.show
2284-
}
2285-
22862279
/** A comparison function to pick a winner in case of a merge conflict */
22872280
private def isAsGood(tp1: Type, tp2: Type): Boolean = tp1 match {
22882281
case tp1: ClassInfo =>
@@ -2546,10 +2539,10 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
25462539
finally myInstance = saved
25472540

25482541
/** The trace of comparison operations when performing `op` */
2549-
def explained[T](op: ExplainingTypeComparer => T)(using Context): String =
2542+
def explained[T](op: ExplainingTypeComparer => T, header: String = "Subtype trace:")(using Context): String =
25502543
val cmp = explainingTypeComparer
25512544
inSubComparer(cmp)(op)
2552-
cmp.lastTrace()
2545+
cmp.lastTrace(header)
25532546

25542547
def tracked[T](op: TrackingTypeComparer => T)(using Context): T =
25552548
inSubComparer(trackingTypeComparer)(op)
@@ -2565,10 +2558,13 @@ object TypeComparer {
25652558
var tpe: Type = NoType
25662559
}
25672560

2568-
private[core] def show(res: Any)(using Context): String = res match {
2569-
case res: printing.Showable if !ctx.settings.YexplainLowlevel.value => res.show
2570-
case _ => String.valueOf(res)
2571-
}
2561+
private[core] def show(res: Any)(using Context): String =
2562+
if ctx.settings.YexplainLowlevel.value then String.valueOf(res)
2563+
else res match
2564+
case ClassInfo(_, cls, _, _, _) => cls.showLocated
2565+
case bounds: TypeBounds => i"type bounds [$bounds]"
2566+
case res: printing.Showable => res.show
2567+
case _ => String.valueOf(res)
25722568

25732569
/** The approximation state indicates how the pair of types currently compared
25742570
* relates to the types compared originally.
@@ -2595,8 +2591,8 @@ object TypeComparer {
25952591
def addLow: Repr = approx | LoApprox
25962592
def addHigh: Repr = approx | HiApprox
25972593
def show: String =
2598-
val lo = if low then "LoApprox" else ""
2599-
val hi = if high then "HiApprox" else ""
2594+
val lo = if low then " (left is tightened)" else ""
2595+
val hi = if high then " (right is tigthened)" else ""
26002596
lo ++ hi
26012597
end ApproxState
26022598
type ApproxState = ApproxState.Repr
@@ -2698,8 +2694,8 @@ object TypeComparer {
26982694
def constrainPatternType(pat: Type, scrut: Type)(using Context): Boolean =
26992695
comparing(_.constrainPatternType(pat, scrut))
27002696

2701-
def explained[T](op: ExplainingTypeComparer => T)(using Context): String =
2702-
comparing(_.explained(op))
2697+
def explained[T](op: ExplainingTypeComparer => T, header: String = "Subtype trace:")(using Context): String =
2698+
comparing(_.explained(op, header))
27032699

27042700
def tracked[T](op: TrackingTypeComparer => T)(using Context): T =
27052701
comparing(_.tracked(op))
@@ -2848,17 +2844,20 @@ class ExplainingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
28482844
res
28492845
}
28502846

2847+
private def frozenNotice: String =
2848+
if frozenConstraint then " in frozen constraint" else ""
2849+
28512850
override def isSubType(tp1: Type, tp2: Type, approx: ApproxState): Boolean =
28522851
def moreInfo =
28532852
if Config.verboseExplainSubtype || ctx.settings.verbose.value
28542853
then s" ${tp1.getClass} ${tp2.getClass}"
28552854
else ""
2856-
traceIndented(s"${show(tp1)} <:< ${show(tp2)}$moreInfo ${approx.show} ${if (frozenConstraint) " frozen" else ""}") {
2855+
traceIndented(s"${show(tp1)} <: ${show(tp2)}$moreInfo${approx.show}$frozenNotice") {
28572856
super.isSubType(tp1, tp2, approx)
28582857
}
28592858

28602859
override def recur(tp1: Type, tp2: Type): Boolean =
2861-
traceIndented(s"${show(tp1)} <:< ${show(tp2)} recur ${if (frozenConstraint) " frozen" else ""}") {
2860+
traceIndented(s"${show(tp1)} <: ${show(tp2)} (recurring)$frozenNotice") {
28622861
super.recur(tp1, tp2)
28632862
}
28642863

@@ -2882,5 +2881,5 @@ class ExplainingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
28822881
super.addConstraint(param, bound, fromBelow)
28832882
}
28842883

2885-
def lastTrace(): String = "Subtype trace:" + { try b.toString finally b.clear() }
2884+
def lastTrace(header: String): String = header + { try b.toString finally b.clear() }
28862885
}

compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class ConsoleReporter(
3535

3636
if (didPrint && shouldExplain(dia))
3737
printMessage(explanation(dia.msg))
38-
else if (didPrint && dia.msg.explanation.nonEmpty)
38+
else if (didPrint && dia.msg.canExplain)
3939
printMessage("\nlonger explanation available when compiling with `-explain`")
4040
}
4141

compiler/src/dotty/tools/dotc/reporting/Diagnostic.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import java.util.Optional
1212
object Diagnostic:
1313

1414
def shouldExplain(dia: Diagnostic)(using Context): Boolean =
15-
dia.msg.explanation.nonEmpty && ctx.settings.explain.value
15+
ctx.settings.explain.value && dia.msg.canExplain
1616

1717
// `Diagnostics to be consumed by `Reporter` ---------------------- //
1818
class Error(

compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,9 @@ enum ErrorMessageID extends java.lang.Enum[ErrorMessageID] {
170170
TraitMayNotDefineNativeMethodID,
171171
JavaEnumParentArgsID,
172172
AlreadyDefinedID,
173-
CaseClassInInlinedCodeID
173+
CaseClassInInlinedCodeID,
174+
OverrideTypeMismatchErrorID,
175+
OverrideErrorID
174176

175177
def errorNumber = ordinal - 2
176178
}

compiler/src/dotty/tools/dotc/reporting/Message.scala

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ abstract class Message(val errorId: ErrorMessageID) { self =>
5858
*/
5959
protected def explain: String
6060

61+
/** Does this message have an explanation?
62+
* This is normally the same as `explain.nonEmpty` but can be overridden
63+
* if we need a way to return `true` without actually calling the
64+
* `explain` method.
65+
*/
66+
def canExplain: Boolean = explain.nonEmpty
67+
6168
private var myMsg: String | Null = null
6269
private var myIsNonSensical: Boolean = false
6370

@@ -95,22 +102,25 @@ abstract class Message(val errorId: ErrorMessageID) { self =>
95102
* that was captured in the original message.
96103
*/
97104
def persist: Message = new Message(errorId) {
98-
val kind = self.kind
99-
val msg = self.msg
100-
val explain = self.explain
105+
val kind = self.kind
106+
val msg = self.msg
107+
val explain = self.explain
108+
override val canExplain = self.canExplain
101109
}
102110

103111
def append(suffix: => String): Message = mapMsg(_ ++ suffix)
104112

105113
def mapMsg(f: String => String): Message = new Message(errorId):
106-
val kind = self.kind
107-
def msg = f(self.msg)
108-
def explain = self.explain
114+
val kind = self.kind
115+
def msg = f(self.msg)
116+
def explain = self.explain
117+
override def canExplain = self.canExplain
109118

110119
def appendExplanation(suffix: => String): Message = new Message(errorId):
111-
val kind = self.kind
112-
def msg = self.msg
113-
def explain = self.explain ++ suffix
120+
val kind = self.kind
121+
def msg = self.msg
122+
def explain = self.explain ++ suffix
123+
override def canExplain = true
114124

115125
override def toString = msg
116126
}

compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@ import transform.SymUtils._
4444
abstract class TypeMsg(errorId: ErrorMessageID) extends Message(errorId):
4545
def kind = "Type"
4646

47-
abstract class TypeMismatchMsg(errorId: ErrorMessageID) extends Message(errorId):
47+
abstract class TypeMismatchMsg(found: Type, expected: Type)(errorId: ErrorMessageID)(using Context) extends Message(errorId):
4848
def kind = "Type Mismatch"
49+
def explain = err.whyNoMatchStr(found, expected)
50+
override def canExplain = true
4951

5052
abstract class NamingMsg(errorId: ErrorMessageID) extends Message(errorId):
5153
def kind = "Naming"
@@ -236,7 +238,7 @@ import transform.SymUtils._
236238
}
237239

238240
class TypeMismatch(found: Type, expected: Type, addenda: => String*)(using Context)
239-
extends TypeMismatchMsg(TypeMismatchID):
241+
extends TypeMismatchMsg(found, expected)(TypeMismatchID):
240242

241243
// replace constrained TypeParamRefs and their typevars by their bounds where possible
242244
// the idea is that if the bounds are also not-subtypes of each other to report
@@ -274,9 +276,7 @@ import transform.SymUtils._
274276
val (foundStr, expectedStr) = Formatting.typeDiff(found2, expected2)(using printCtx)
275277
s"""|Found: $foundStr
276278
|Required: $expectedStr""".stripMargin
277-
+ whereSuffix + err.whyNoMatchStr(found, expected) + postScript
278-
279-
def explain = ""
279+
+ whereSuffix + postScript
280280
end TypeMismatch
281281

282282
class NotAMember(site: Type, val name: Name, selected: String, addendum: => String = "")(using Context)
@@ -1074,6 +1074,14 @@ import transform.SymUtils._
10741074
|"""
10751075
}
10761076

1077+
class OverrideError(override val msg: String) extends DeclarationMsg(OverrideErrorID):
1078+
def explain = ""
1079+
1080+
class OverrideTypeMismatchError(override val msg: String, memberTp: Type, otherTp: Type)(using Context)
1081+
extends DeclarationMsg(OverrideTypeMismatchErrorID):
1082+
def explain = err.whyNoMatchStr(memberTp, otherTp)
1083+
override def canExplain = true
1084+
10771085
class ForwardReferenceExtendsOverDefinition(value: Symbol, definition: Symbol)(using Context)
10781086
extends ReferenceMsg(ForwardReferenceExtendsOverDefinitionID) {
10791087
def msg = em"${definition.name} is a forward reference extending over the definition of ${value.name}"
@@ -1324,7 +1332,7 @@ import transform.SymUtils._
13241332
if (isNullary) "\nNullary methods may not be called with parenthesis"
13251333
else ""
13261334

1327-
"You have specified more parameter lists as defined in the method definition(s)." + addendum
1335+
"You have specified more parameter lists than defined in the method definition(s)." + addendum
13281336
}
13291337

13301338
}
@@ -1414,36 +1422,22 @@ import transform.SymUtils._
14141422
}
14151423

14161424
class DoesNotConformToBound(tpe: Type, which: String, bound: Type)(using Context)
1417-
extends TypeMismatchMsg(DoesNotConformToBoundID) {
1418-
def msg = em"Type argument ${tpe} does not conform to $which bound $bound${err.whyNoMatchStr(tpe, bound)}"
1419-
def explain = ""
1425+
extends TypeMismatchMsg(tpe, bound)(DoesNotConformToBoundID) {
1426+
def msg = em"Type argument ${tpe} does not conform to $which bound $bound"
14201427
}
14211428

14221429
class DoesNotConformToSelfType(category: String, selfType: Type, cls: Symbol,
1423-
otherSelf: Type, relation: String, other: Symbol)(
1430+
otherSelf: Type, relation: String, other: Symbol)(
14241431
implicit ctx: Context)
1425-
extends TypeMismatchMsg(DoesNotConformToSelfTypeID) {
1432+
extends TypeMismatchMsg(selfType, otherSelf)(DoesNotConformToSelfTypeID) {
14261433
def msg = em"""$category: self type $selfType of $cls does not conform to self type $otherSelf
14271434
|of $relation $other"""
1428-
def explain =
1429-
em"""You mixed in $other which requires self type $otherSelf, but $cls has self type
1430-
|$selfType and does not inherit from $otherSelf.
1431-
|
1432-
|Note: Self types are indicated with the notation
1433-
| ${s"class "}$other ${hl("{ this: ")}$otherSelf${hl(" => ")}
1434-
"""
14351435
}
14361436

14371437
class DoesNotConformToSelfTypeCantBeInstantiated(tp: Type, selfType: Type)(
14381438
implicit ctx: Context)
1439-
extends TypeMismatchMsg(DoesNotConformToSelfTypeCantBeInstantiatedID) {
1439+
extends TypeMismatchMsg(tp, selfType)(DoesNotConformToSelfTypeCantBeInstantiatedID) {
14401440
def msg = em"""$tp does not conform to its self type $selfType; cannot be instantiated"""
1441-
def explain =
1442-
em"""To create an instance of $tp it needs to inherit $selfType in some way.
1443-
|
1444-
|Note: Self types are indicated with the notation
1445-
| ${s"class "}$tp ${hl("{ this: ")}$selfType${hl(" => ")}
1446-
|"""
14471441
}
14481442

14491443
class AbstractMemberMayNotHaveModifier(sym: Symbol, flag: FlagSet)(
@@ -2207,7 +2201,7 @@ import transform.SymUtils._
22072201
}
22082202

22092203
class NoMatchingOverload(val alternatives: List[SingleDenotation], pt: Type)(using Context)
2210-
extends TypeMismatchMsg(NoMatchingOverloadID) {
2204+
extends TypeMsg(NoMatchingOverloadID) {
22112205
def msg =
22122206
em"""None of the ${err.overloadedAltsStr(alternatives)}
22132207
|match ${err.expectedTypeStr(pt)}"""

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

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,25 @@ object ErrorReporting {
117117
}
118118

119119
/** A subtype log explaining why `found` does not conform to `expected` */
120-
def whyNoMatchStr(found: Type, expected: Type): String = {
121-
if (ctx.settings.explainTypes.value)
122-
i"""
123-
|${ctx.typerState.constraint}
124-
|${TypeComparer.explained(_.isSubType(found, expected))}"""
125-
else
126-
""
127-
}
120+
def whyNoMatchStr(found: Type, expected: Type): String =
121+
val header =
122+
i"""I tried to show that
123+
| $found
124+
|conforms to
125+
| $expected
126+
|but the comparison trace ended with `false`:
127+
"""
128+
val c = ctx.typerState.constraint
129+
val constraintText =
130+
if c.domainLambdas.isEmpty then
131+
"empty constraint"
132+
else
133+
i"""following constraint:
134+
|${c.contentsToString}"""
135+
i"""
136+
|${TypeComparer.explained(_.isSubType(found, expected), header)}
137+
|
138+
|The tests were made under the $constraintText"""
128139

129140
/** Format `raw` implicitNotFound or implicitAmbiguous argument, replacing
130141
* all occurrences of `${X}` where `X` is in `paramNames` with the

0 commit comments

Comments
 (0)