@@ -10,6 +10,7 @@ import printing.Formatting.hl
1010import config .SourceVersion
1111import cc .CaptureSet
1212import cc .Capabilities .*
13+ import util .Property
1314
1415import scala .annotation .threadUnsafe
1516
@@ -41,6 +42,11 @@ object Message:
4142 i " \n $what can be rewritten automatically under -rewrite $optionStr. "
4243 else " "
4344
45+ /** A context property that turns off expansion of `^` or `=>` to explicit cap's.
46+ * Used in the where clauses of error messages.
47+ */
48+ val NoCapExpansion = new Property .Key [Unit ]
49+
4450 /** A note can produce an added string for an error message */
4551 abstract class Note :
4652
@@ -103,9 +109,12 @@ object Message:
103109 * If the entry was not yet recorded, allocate the next superscript corresponding
104110 * to the same string in the same name space. The first recording is the string proper
105111 * and following recordings get consecutive superscripts starting with 2.
112+ * @param capOk if true and there is already a cap recorded that matches the entry,
113+ * then use an expanded representation with explicit `cap` for the
114+ * original string.
106115 * @return The possibly superscripted version of `str`.
107116 */
108- def record (str : String , isType : Boolean , entry : Recorded )(using Context ): String =
117+ def record (str : String , isType : Boolean , entry : Recorded , capOK : Boolean = false )(using Context ): String =
109118 if disambi.recordOK(str) then
110119 // println(s"recording $str, $isType, $entry")
111120
@@ -119,9 +128,6 @@ object Message:
119128 if (underlying.name == e1.name) underlying else e1.namedType.dealias.typeSymbol
120129 case _ => e1
121130 }
122- val key = SeenKey (str, isType)
123- val existing = seen(key)
124- lazy val dealiased = followAlias(entry)
125131
126132 /** All lambda parameters with the same name are given the same superscript as
127133 * long as their corresponding binders have the same parameter name lists.
@@ -138,13 +144,34 @@ object Message:
138144 case _ =>
139145 false
140146
141- // The length of alts corresponds to the number of superscripts we need to print.
142- var alts = existing.dropWhile(alt => ! sameSuperscript(dealiased, followAlias(alt)))
143- if alts.isEmpty then
144- alts = entry :: existing
145- seen(key) = alts
147+ /** The superscript in `existing` that matches the current entry, using
148+ * `sameSuperscript` on dealiased entries as a test. The superscript is
149+ * the index of the matching entry in existing, counting from the right
150+ * and starting at 1. I.e last entry in the list has superscript 1, next to
151+ * last entry has superscript 2, and so on. A result of 0 means that no
152+ * matching entry exists.
153+ */
154+ def matchingSuperscript (existing : List [Recorded ]): Int =
155+ lazy val dealiased = followAlias(entry)
156+ existing.dropWhile(alt => ! sameSuperscript(dealiased, followAlias(alt))).length
157+
158+ if capOK && ctx.property(NoCapExpansion ).isEmpty
159+ && matchingSuperscript(seen(SeenKey (" cap" , isType = false ))) > 0
160+ then
161+ val capStr = record(" cap" , isType = false , entry)
162+ return str match
163+ case " ^" => s " ^{ $capStr} "
164+ case " =>" => s " ->{ $capStr} "
165+ case " ?=>" => s " ?->{ $capStr} "
166+
167+ val key = SeenKey (str, isType)
168+ val existing = seen(key)
169+ var sup = matchingSuperscript(existing)
170+ if sup == 0 then
171+ seen(key) = entry :: existing
172+ sup = existing.length + 1
146173
147- val suffix = alts.length match {
174+ val suffix = sup match {
148175 case 1 => " "
149176 case n => n.toString.toCharArray.map {
150177 case '0' => '⁰'
@@ -184,26 +211,27 @@ object Message:
184211 " "
185212 }
186213
187- entry match
188- case param : TypeParamRef =>
189- s " is a type variable ${addendum(" constraint" , TypeComparer .bounds(param))}"
190- case param : TermParamRef =>
191- s " is a reference to a value parameter "
192- case sym : Symbol =>
193- val info =
194- if (ctx.gadt.contains(sym))
195- sym.info & ctx.gadt.fullBounds(sym).nn
196- else
197- sym.info
198- s " is a ${ctx.printer.kindString(sym)}${sym.showExtendedLocation}${addendum(" bounds" , info)}"
199- case tp : SkolemType =>
200- s " is an unknown value of type ${tp.widen.show}"
201- case ref : RootCapability =>
202- val relation =
203- if keys.length > 1 then " refer to"
204- else if List (" ^" , " =>" , " ?=>" ).exists(keys(0 ).startsWith) then " refers to"
205- else " is"
206- s " $relation ${ref.descr}"
214+ inContext(ctx.withProperty(NoCapExpansion , Some (()))):
215+ entry match
216+ case param : TypeParamRef =>
217+ s " is a type variable ${addendum(" constraint" , TypeComparer .bounds(param))}"
218+ case param : TermParamRef =>
219+ s " is a reference to a value parameter "
220+ case sym : Symbol =>
221+ val info =
222+ if (ctx.gadt.contains(sym))
223+ sym.info & ctx.gadt.fullBounds(sym).nn
224+ else
225+ sym.info
226+ s " is a ${ctx.printer.kindString(sym)}${sym.showExtendedLocation}${addendum(" bounds" , info)}"
227+ case tp : SkolemType =>
228+ s " is an unknown value of type ${tp.widen.show}"
229+ case ref : RootCapability =>
230+ val relation =
231+ if keys.length > 1 then " refer to"
232+ else if List (" ^" , " =>" , " ?=>" ).exists(keys(0 ).startsWith) then " refers to"
233+ else " is"
234+ s " $relation ${ref.descr}"
207235 end explanation
208236
209237 /** Produce a where clause with explanations for recorded iterms.
@@ -276,14 +304,14 @@ object Message:
276304 if isUniversalCaptureSet(refs) && ! defn.isFunctionType(parent) && ! printDebug && seen.isActive =>
277305 boxText
278306 ~ toTextLocal(parent)
279- ~ seen.record(" ^" , isType = true , refs.elems.nth(0 ).asInstanceOf [RootCapability ])
307+ ~ seen.record(" ^" , isType = true , refs.elems.nth(0 ).asInstanceOf [RootCapability ], capOK = true )
280308 case _ =>
281309 super .toTextCapturing(parent, refs, boxText)
282310
283311 override def funMiddleText (isContextual : Boolean , isPure : Boolean , refs : GeneralCaptureSet | Null ): Text =
284312 refs match
285313 case refs : CaptureSet if isUniversalCaptureSet(refs) && seen.isActive =>
286- seen.record(arrow(isContextual, isPure = false ), isType = true , refs.elems.nth(0 ).asInstanceOf [RootCapability ])
314+ seen.record(arrow(isContextual, isPure = false ), isType = true , refs.elems.nth(0 ).asInstanceOf [RootCapability ], capOK = true )
287315 case _ =>
288316 super .funMiddleText(isContextual, isPure, refs)
289317
0 commit comments