Skip to content

Commit a14790e

Browse files
committed
Fix pretty-printing of capture-variables
1 parent be7a427 commit a14790e

File tree

4 files changed

+30
-19
lines changed

4 files changed

+30
-19
lines changed

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,25 @@ class PlainPrinter(_ctx: Context) extends Printer {
567567
(tparamStr, bounds.derivedTypeBounds(loRhs, hiRhs))
568568
end decomposeLambdas
569569

570+
/** Is this a capture variable's bounds? i.e. lo and hi are both CapSet-based. */
571+
private def isCaptureVarBounds(lo: Type, hi: Type): Boolean =
572+
lo.derivesFrom(defn.Caps_CapSet) && (hi match
573+
case CapturingType(parent, _) => parent.derivesFrom(defn.Caps_CapSet)
574+
case hi => hi.derivesFrom(defn.Caps_CapSet))
575+
576+
/** Print capture variable bounds using `^` syntax.
577+
* Plain CapSet lower bound and universal upper bound are elided.
578+
*/
579+
private def toTextCaptureVarBounds(lo: Type, hi: Type): Text =
580+
val loText = lo match
581+
case CapturingType(_, refs) => " >: " ~ toTextCaptureSet(refs)
582+
case _ => Text() // plain CapSet = trivial lower bound
583+
val hiText = hi match
584+
case CapturingType(_, refs) if isElidableUniversal(refs) => Text() // trivial upper bound
585+
case CapturingType(_, refs) => " <: " ~ toTextCaptureSet(refs)
586+
case _ => Text()
587+
Str("^") ~ loText ~ hiText
588+
570589
/** String representation of a definition's type following its name */
571590
protected def toTextRHS(tp: Type, isParameter: Boolean = false): Text = controlled {
572591
homogenize(tp) match {
@@ -575,6 +594,8 @@ class PlainPrinter(_ctx: Context) extends Printer {
575594
val binder = rhs match
576595
case tp: AliasingBounds =>
577596
" = " ~ toText(tp.alias)
597+
case TypeBounds(lo, hi) if !printDebug && Feature.ccEnabledSomewhere && isCaptureVarBounds(lo, hi) =>
598+
toTextCaptureVarBounds(lo, hi)
578599
case TypeBounds(lo, hi) =>
579600
(if lo.isExactlyNothing then Text() else " >: " ~ toText(lo))
580601
~ (if hi.isExactlyAny || (!printDebug && hi.isFromJavaObject) then Text() else " <: " ~ toText(hi))

docs/_docs/reference/experimental/capture-checking/safe.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ is done, the library module can be made available for use by safe code.
4242
This scheme is supported by a new `@assumeSafe` annotation, available in
4343
module `caps`.
4444
Modules tagged with this annotation are assumed to be callable from agent-generated code. `@assumeSafe` comes with none of the restrictions that `safe` implies. Instead it is the obligation of the programmer to verify that the module is indeed safe. For instance caching a function results could be implemented like this:
45-
<!-- FIXME: the below is rejected by the compiler! -->
4645
```scala sc:nocompile
4746
import caps.unsafe.untrackedCaptures
4847
import caps.assumeSafe

repl/test/dotty/tools/repl/ReplCompilerTests.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,7 @@ class ReplCompilerTests extends ReplTest:
500500
// Verify capture checking syntax is accepted in subsequent inputs
501501
run("def foo[C^](x: AnyRef^{C}): AnyRef^{x} = x")
502502
assertEquals(
503-
"def foo[C >: caps.CapSet <: caps.CapSet^](x: AnyRef^{C}): AnyRef^{x}",
503+
"def foo[C^](x: AnyRef^{C}): AnyRef^{x}",
504504
storedOutput().trim)
505505

506506
@Test def `i16250b`: Unit =
@@ -520,7 +520,7 @@ class ReplCompilerTests extends ReplTest:
520520
// separationChecking implies captureChecking
521521
run("def foo[C^](x: AnyRef^{C}): AnyRef^{x} = x")
522522
assertEquals(
523-
"def foo[C >: caps.CapSet <: caps.CapSet^](x: AnyRef^{C}): AnyRef^{x}",
523+
"def foo[C^](x: AnyRef^{C}): AnyRef^{x}",
524524
storedOutput().trim)
525525

526526
@Test def `i16250d`: Unit =
@@ -531,7 +531,7 @@ class ReplCompilerTests extends ReplTest:
531531
// safe implies captureChecking
532532
run("def foo[C^](x: AnyRef^{C}): AnyRef^{x} = x")
533533
assertEquals(
534-
"def foo[C >: caps.CapSet <: caps.CapSet^](x: AnyRef^{C}): AnyRef^{x}",
534+
"def foo[C^](x: AnyRef^{C}): AnyRef^{x}",
535535
storedOutput().trim)
536536

537537
@Test def `i16250 nested global language imports error`: Unit = initially:
Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,21 @@
11
-- [E164] Declaration Error: tests/neg-custom-args/captures/unbox-overrides.scala:8:6 ----------------------------------
22
8 | def foo[C^]: Object^{C} // error
33
| ^
4-
|error overriding method foo in trait A of type [C >: scala.caps.CapSet <: scala.caps.CapSet^]: Object^{C};
5-
| method foo of type [C >: scala.caps.CapSet <: scala.caps.CapSet^²]: Object^{C} has a parameter C with different @reserve status than the corresponding parameter in the overridden definition
6-
|
7-
|where: ^ refers to a root capability in the type of type C²
8-
| ^² refers to a root capability in the type of type C³
4+
|error overriding method foo in trait A of type [C^]: Object^{C};
5+
| method foo of type [C^]: Object^{C} has a parameter C with different @reserve status than the corresponding parameter in the overridden definition
96
|
107
| longer explanation available when compiling with `-explain`
118
-- [E164] Declaration Error: tests/neg-custom-args/captures/unbox-overrides.scala:9:6 ----------------------------------
129
9 | def bar[@reserve C^]: Object^{C} // error
1310
| ^
14-
|error overriding method bar in trait A of type [C >: scala.caps.CapSet <: scala.caps.CapSet^]: Object^{C};
15-
| method bar of type [C >: scala.caps.CapSet <: scala.caps.CapSet^²]: Object^{C} has a parameter C with different @reserve status than the corresponding parameter in the overridden definition
16-
|
17-
|where: ^ refers to a root capability in the type of type C²
18-
| ^² refers to a root capability in the type of type C³
11+
|error overriding method bar in trait A of type [C^]: Object^{C};
12+
| method bar of type [C^]: Object^{C} has a parameter C with different @reserve status than the corresponding parameter in the overridden definition
1913
|
2014
| longer explanation available when compiling with `-explain`
2115
-- [E164] Declaration Error: tests/neg-custom-args/captures/unbox-overrides.scala:15:15 --------------------------------
2216
15 |abstract class C extends A, B2 // error
2317
| ^
24-
|error overriding method foo in trait A of type [C >: scala.caps.CapSet <: scala.caps.CapSet^]: Object^{C};
25-
| method foo in trait B2 of type [C >: scala.caps.CapSet <: scala.caps.CapSet^²]: Object^{C} has a parameter C with different @reserve status than the corresponding parameter in the overridden definition
26-
|
27-
|where: ^ refers to a root capability in the type of type C²
28-
| ^² refers to a root capability in the type of type C³
18+
|error overriding method foo in trait A of type [C^]: Object^{C};
19+
| method foo in trait B2 of type [C^]: Object^{C} has a parameter C with different @reserve status than the corresponding parameter in the overridden definition
2920
|
3021
| longer explanation available when compiling with `-explain`

0 commit comments

Comments
 (0)