Skip to content

Commit 14e992c

Browse files
committed
Refactor printing of capturing types and function types
Refactor to avoid generating refsText ahead of time. We need to keep the original capture sets or tree lists so that we can record them as seen.
1 parent 2953d64 commit 14e992c

File tree

4 files changed

+75
-66
lines changed

4 files changed

+75
-66
lines changed

compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import util.SourcePosition
1515
import scala.util.control.NonFatal
1616
import scala.annotation.switch
1717
import config.{Config, Feature}
18+
import ast.tpd
1819
import cc.*
1920

2021
class PlainPrinter(_ctx: Context) extends Printer {
@@ -182,14 +183,41 @@ class PlainPrinter(_ctx: Context) extends Printer {
182183
private def toTextRetainedElems[T <: Untyped](refs: List[Tree[T]]): Text =
183184
"{" ~ Text(refs.map(ref => toTextRetainedElem(ref)), ", ") ~ "}"
184185

186+
type GeneralCaptureSet = CaptureSet | List[tpd.Tree]
187+
188+
protected def isUniversalCaptureSet(refs: GeneralCaptureSet): Boolean = refs match
189+
case refs: CaptureSet =>
190+
// The set if universal if it consists only of caps.cap or
191+
// only of an existential Fresh that is bound to the immediately enclosing method.
192+
val isUniversal =
193+
refs.elems.size == 1
194+
&& (refs.isUniversal
195+
|| !printDebug && !printFresh && !showUniqueIds && refs.elems.nth(0).match
196+
case root.Result(binder) =>
197+
CCState.openExistentialScopes match
198+
case b :: _ => binder eq b
199+
case _ => false
200+
case _ =>
201+
false
202+
)
203+
isUniversal
204+
|| !refs.elems.isEmpty && refs.elems.forall(_.isCapOrFresh) && !printFresh
205+
case (ref: tpd.Tree) :: Nil => ref.symbol == defn.captureRoot
206+
case _ => false
207+
208+
protected def toTextGeneralCaptureSet(refs: GeneralCaptureSet): Text = refs match
209+
case refs: CaptureSet => toTextCaptureSet(refs)
210+
case refs: List[tpd.Tree] => toTextRetainedElems(refs)
211+
185212
/** Print capturing type, overridden in RefinedPrinter to account for
186213
* capturing function types.
187214
*/
188-
protected def toTextCapturing(parent: Type, refsText: Text, boxText: Text): Text =
215+
protected def toTextCapturing(parent: Type, refs: GeneralCaptureSet, boxText: Text): Text =
189216
changePrec(InfixPrec):
190-
boxText ~ toTextLocal(parent) ~ "^" ~ (refsText provided refsText != rootSetText)
191-
192-
final protected def rootSetText = Str("{cap}") // TODO Use disambiguation
217+
boxText
218+
~ toTextLocal(parent)
219+
~ "^"
220+
~ toTextGeneralCaptureSet(refs).provided(!isUniversalCaptureSet(refs))
193221

194222
def toText(tp: Type): Text = controlled {
195223
homogenize(tp) match {
@@ -256,33 +284,10 @@ class PlainPrinter(_ctx: Context) extends Printer {
256284
then
257285
toText(parent)
258286
else
259-
// The set if universal if it consists only of caps.cap or
260-
// only of an existential Fresh that is bound to the immediately enclosing method.
261-
def isUniversal =
262-
refs.elems.size == 1
263-
&& (refs.isUniversal
264-
|| !printDebug && !printFresh && !showUniqueIds && refs.elems.nth(0).match
265-
case root.Result(binder) =>
266-
CCState.openExistentialScopes match
267-
case b :: _ => binder eq b
268-
case _ => false
269-
case _ =>
270-
false
271-
)
272-
val refsText =
273-
if isUniversal then
274-
rootSetText
275-
else if !refs.elems.isEmpty && refs.elems.forall(_.isCapOrFresh) && !printFresh then
276-
rootSetText
277-
else
278-
toTextCaptureSet(refs)
279-
toTextCapturing(parent, refsText, boxText)
287+
toTextCapturing(parent, refs, boxText)
280288
case tp @ RetainingType(parent, refs) =>
281289
if Feature.ccEnabledSomewhere then
282-
val refsText = refs match
283-
case ref :: Nil if ref.symbol == defn.captureRoot => rootSetText
284-
case _ => toTextRetainedElems(refs)
285-
toTextCapturing(parent, refsText, "") ~ Str("R").provided(printDebug)
290+
toTextCapturing(parent, refs, "") ~ Str("R").provided(printDebug)
286291
else toText(parent)
287292
case tp: PreviousErrorType if ctx.settings.XprintTypes.value =>
288293
"<error>" // do not print previously reported error message because they may try to print this error type again recursively

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -159,17 +159,22 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
159159
private def arrow(isGiven: Boolean, isPure: Boolean): String =
160160
(if isGiven then "?" else "") + (if isPure then "->" else "=>")
161161

162-
private def toTextFunction(tp: AppliedType, refs: Text = Str("")): Text =
162+
private def toTextFunction(tp: AppliedType, refs: GeneralCaptureSet | Null): Text =
163163
val AppliedType(tycon, args) = (tp: @unchecked)
164164
val tsym = tycon.typeSymbol
165-
val isContextual = tsym.name.isContextFunction
166-
val capturesRoot = refs == rootSetText
167-
val isPure =
168-
Feature.pureFunsEnabled && !tsym.name.isImpureFunction && !capturesRoot
169-
toTextFunction(args.init, args.last, tp, refs.provided(!capturesRoot), isContextual, isPure)
170-
171-
private def toTextFunction(args: List[Type], res: Type, fn: MethodType | AppliedType, refs: Text,
172-
isContextual: Boolean, isPure: Boolean): Text =
165+
toTextFunction(args.init, args.last, tp, refs,
166+
isContextual = tsym.name.isContextFunction,
167+
isPure = Feature.pureFunsEnabled && !tsym.name.isImpureFunction)
168+
169+
protected def funMiddleText(isContextual: Boolean, isPure: Boolean, refs: GeneralCaptureSet | Null): Text =
170+
val (printPure, refsText) =
171+
if refs == null then (isPure, Str(""))
172+
else if isUniversalCaptureSet(refs) then (false, Str(""))
173+
else (isPure, toTextGeneralCaptureSet(refs))
174+
arrow(isContextual, printPure) ~ refsText
175+
176+
private def toTextFunction(args: List[Type], res: Type, fn: MethodType | AppliedType,
177+
refs: GeneralCaptureSet | Null, isContextual: Boolean, isPure: Boolean): Text =
173178
changePrec(GlobalPrec):
174179
val argStr: Text = args match
175180
case arg :: Nil if !defn.isDirectTupleNType(arg) && !isContextual =>
@@ -179,30 +184,29 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
179184
"("
180185
~ argsText(args)
181186
~ ")"
182-
argStr ~ " " ~ arrow(isContextual, isPure) ~ refs ~ " "
187+
argStr ~ " "
188+
~ funMiddleText(isContextual, isPure, refs) ~ " "
183189
~ fn.match
184190
case fn: MethodType => CCState.inNewExistentialScope(fn)(argText(res))
185191
case _ => argText(res)
186192

187-
protected def toTextMethodAsFunction(info: Type, isPure: Boolean, refs: Text = Str("")): Text =
193+
protected def toTextMethodAsFunction(info: Type, isPure: Boolean, refs: GeneralCaptureSet | Null): Text =
188194
def recur(tp: Type, enclInfo: MethodType | Null): Text = tp match
189195
case tp: MethodType =>
190196
val isContextual = tp.isImplicitMethod
191-
val capturesRoot = refs == rootSetText
192197
if cc.isCaptureCheckingOrSetup
193198
&& tp.allParamNamesSynthetic
194199
&& !tp.looksResultDependent && !tp.looksParamDependent
195200
&& !showUniqueIds && !printDebug && !printFresh
196-
then
201+
then
197202
// cc.Setup converts all functions to dependent functions. Undo that when printing.
198-
toTextFunction(tp.paramInfos, tp.resType, tp, refs.provided(!capturesRoot), isContextual, isPure && !capturesRoot)
203+
toTextFunction(tp.paramInfos, tp.resType, tp, refs, isContextual, isPure)
199204
else
200205
changePrec(GlobalPrec):
201206
"("
202207
~ paramsText(tp)
203208
~ ") "
204-
~ arrow(isContextual, isPure && !capturesRoot)
205-
~ refs.provided(!capturesRoot)
209+
~ funMiddleText(isContextual, isPure, refs)
206210
~ " "
207211
~ recur(tp.resultType, tp)
208212
case tp: PolyType =>
@@ -274,7 +278,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
274278
case _ =>
275279
val tsym = tycon.typeSymbol
276280
if tycon.isRepeatedParam then toTextLocal(args.head) ~ "*"
277-
else if defn.isFunctionSymbol(tsym) then toTextFunction(tp)
281+
else if defn.isFunctionSymbol(tsym) then toTextFunction(tp, null)
278282
else if isInfixType(tp) then
279283
val l :: r :: Nil = args: @unchecked
280284
val opName = tyconName(tycon)
@@ -303,8 +307,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
303307
toTextMethodAsFunction(tp.refinedInfo,
304308
isPure = Feature.pureFunsEnabled && !tp.typeSymbol.name.isImpureFunction,
305309
refs = tp.parent match
306-
case CapturingType(_, cs) => toTextCaptureSet(cs)
307-
case _ => "")
310+
case CapturingType(_, cs) => cs
311+
case _ => null)
308312
case tp: TypeRef =>
309313
if (tp.symbol.isAnonymousClass && !showUniqueIds)
310314
toText(tp.info)
@@ -320,7 +324,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
320324
case tp: ClassInfo =>
321325
if tp.cls.derivesFrom(defn.PolyFunctionClass) then
322326
tp.member(nme.apply).info match
323-
case info: PolyType => toTextMethodAsFunction(info, isPure = false)
327+
case info: PolyType => toTextMethodAsFunction(info, isPure = false, refs = null)
324328
case _ => toTextParents(tp.parents) ~~ "{...}"
325329
else toTextParents(tp.parents) ~~ "{...}"
326330
case JavaArrayType(elemtp) =>
@@ -849,13 +853,13 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
849853
}
850854
}
851855

852-
override protected def toTextCapturing(tp: Type, refsText: Text, boxText: Text): Text = tp match
856+
override protected def toTextCapturing(tp: Type, refs: GeneralCaptureSet, boxText: Text): Text = tp match
853857
case tp: AppliedType if defn.isFunctionSymbol(tp.typeSymbol) && !printDebug =>
854-
boxText ~ toTextFunction(tp, refsText)
858+
boxText ~ toTextFunction(tp, refs)
855859
case tp: RefinedType if defn.isFunctionType(tp) && !printDebug =>
856-
boxText ~ toTextMethodAsFunction(tp.refinedInfo, isPure = !tp.typeSymbol.name.isImpureFunction, refsText)
860+
boxText ~ toTextMethodAsFunction(tp.refinedInfo, isPure = !tp.typeSymbol.name.isImpureFunction, refs)
857861
case _ =>
858-
super.toTextCapturing(tp, refsText, boxText)
862+
super.toTextCapturing(tp, refs, boxText)
859863

860864
override def toText[T <: Untyped](tree: Tree[T]): Text = controlled {
861865
import untpd.*

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ object Message:
225225
case tp: SkolemType => seen.record(tp.repr.toString, isType = true, tp)
226226
case _ => super.toTextRef(tp)
227227

228-
override def toTextMethodAsFunction(info: Type, isPure: Boolean, refs: Text): Text =
228+
override def toTextMethodAsFunction(info: Type, isPure: Boolean, refs: GeneralCaptureSet): Text =
229229
info match
230230
case info: LambdaType =>
231231
seen.openLambda(info)

tests/neg-custom-args/captures/i15116.check

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15116.scala:3:13 ----------------------------------------
22
3 | val x = Foo(m) // error
33
| ^^^^^^
4-
| Found: Foo{val m²: (Bar.this.m : String^)}^{Bar.this.m}
4+
| Found: Foo{val m: (Bar.this.m² : String^)}^{Bar.this.m²}
55
| Required: Foo
66
|
7-
| where: m is a value in class Bar
8-
| m² is a value in class Foo
7+
| where: m is a value in class Foo
8+
| m² is a value in class Bar
99
|
1010
|
1111
| Note that the expected type Foo
@@ -18,11 +18,11 @@
1818
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15116.scala:5:13 ----------------------------------------
1919
5 | val x = Foo(m) // error
2020
| ^^^^^^
21-
| Found: Foo{val m²: (Baz.this.m : String^)}^{Baz.this.m}
21+
| Found: Foo{val m: (Baz.this.m² : String^)}^{Baz.this.m²}
2222
| Required: Foo
2323
|
24-
| where: m is a value in trait Baz
25-
| m² is a value in class Foo
24+
| where: m is a value in class Foo
25+
| m² is a value in trait Baz
2626
|
2727
|
2828
| Note that the expected type Foo
@@ -35,11 +35,11 @@
3535
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15116.scala:7:13 ----------------------------------------
3636
7 | val x = Foo(m) // error
3737
| ^^^^^^
38-
| Found: Foo{val m²: (Bar1.this.m : String^)}^{Bar1.this.m}
38+
| Found: Foo{val m: (Bar1.this.m² : String^)}^{Bar1.this.m²}
3939
| Required: Foo
4040
|
41-
| where: m is a value in class Bar1
42-
| m² is a value in class Foo
41+
| where: m is a value in class Foo
42+
| m² is a value in class Bar1
4343
|
4444
|
4545
| Note that the expected type Foo
@@ -52,11 +52,11 @@
5252
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15116.scala:9:13 ----------------------------------------
5353
9 | val x = Foo(m) // error
5454
| ^^^^^^
55-
| Found: Foo{val m²: (Baz2.this.m : String^)}^{Baz2.this.m}
55+
| Found: Foo{val m: (Baz2.this.m² : String^)}^{Baz2.this.m²}
5656
| Required: Foo
5757
|
58-
| where: m is a value in trait Baz2
59-
| m² is a value in class Foo
58+
| where: m is a value in class Foo
59+
| m² is a value in trait Baz2
6060
|
6161
|
6262
| Note that the expected type Foo

0 commit comments

Comments
 (0)