Skip to content

Commit 0af8460

Browse files
committed
Rendering of CapSet types
1 parent 693768c commit 0af8460

File tree

4 files changed

+62
-35
lines changed

4 files changed

+62
-35
lines changed

local/project/dummy/capturevars.scala

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
11
package dummy
22

33
import language.experimental.captureChecking
4+
import caps.*
45

56
trait Test:
6-
type T[-C^]
7+
val a: AnyRef^
8+
val b: AnyRef^
9+
type Ordinary
10+
type Ordinary2 >: Int <: String
11+
type T[-C^ >: {a,b}]
712
type U[+C^]
813
type C^
9-
type D^
10-
def foo[C^](x: T[C]): Unit
11-
def bar(x: T[{}]): Unit
12-
def baz(x: T[{caps.cap}]): Unit
14+
type D^ >: {C} <: {a,b}
15+
type E^ <: C
16+
type F^ <: {D,E}
17+
def foo[C^ >: {a,b}](x: T[C]): Unit
18+
def bar(x: T[{a,b}]): Unit
19+
def baz(x: T[{a,b,caps.cap}]): Unit
1320
def foo2[C^](x: U[C]): Unit
14-
def bar2(x: U[{}]): Unit
21+
def bar2(x: U[{a,b,cap}]): Unit
1522
def baz2(x: U[{caps.cap}]): Unit
16-
def test[E^, F^ >: {caps.cap} <: {}](x: T[E], y: U[F]): Unit
23+
def test[E^, F^ >: {caps.cap} <: {}](x: T[{E,a,b}], y: U[F]): Unit

scaladoc/src/dotty/tools/scaladoc/cc/CaptureOps.scala

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,18 @@ extension (using qctx: Quotes)(tpe: qctx.reflect.TypeRepr) // FIXME clean up and
9393
def isAnyFunction: Boolean = tpe.typeSymbol.fullName.startsWith("scala.Function")
9494

9595
def isAnyContextFunction: Boolean = tpe.typeSymbol.fullName.startsWith("scala.ContextFunction")
96+
97+
def isCapSet: Boolean = tpe.typeSymbol == CaptureDefs.Caps_CapSet
98+
99+
def isCapSetPure: Boolean =
100+
tpe.isCapSet && tpe.match
101+
case CapturingType(_, refs) => refs.isEmpty
102+
case _ => true
103+
104+
def isCapSetCap: Boolean =
105+
tpe.isCapSet && tpe.match
106+
case CapturingType(_, List(ref)) => ref.isCaptureRoot
107+
case _ => false
96108
end extension
97109

98110
/** Matches `import scala.language.experimental.captureChecking` */
@@ -136,14 +148,14 @@ def decomposeCaptureRefs(using qctx: Quotes)(typ0: qctx.reflect.TypeRepr): Optio
136148
def include(t: TypeRepr): Boolean = { buffer += t; true }
137149
def traverse(typ: TypeRepr): Boolean =
138150
typ match
151+
case t if t.typeSymbol == defn.NothingClass => true
139152
case OrType(t1, t2) => traverse(t1) && traverse(t2)
140153
case t @ ThisType(_) => include(t)
141154
case t @ TermRef(_, _) => include(t)
142155
case t @ ParamRef(_, _) => include(t)
143156
case t @ ReachCapability(_) => include(t)
144157
case t @ ReadOnlyCapability(_) => include(t)
145-
case t if t.typeSymbol == defn.NothingClass => true
146-
// TODO: are atoms only ever the above? Then we could refine the return type
158+
case t : TypeRef => include(t) // FIXME: does this need a more refined check?
147159
case _ => report.warning(s"Unexpected type tree $typ while trying to extract capture references from $typ0"); false // TODO remove warning eventually
148160
if traverse(typ0) then Some(buffer.toList) else None
149161
end decomposeCaptureRefs

scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import dotty.tools.scaladoc._
55
import dotty.tools.scaladoc.{Signature => DSignature}
66
import dotty.tools.scaladoc.Inkuire
77

8+
import dotty.tools.scaladoc.cc.CaptureDefs
9+
810
import scala.quoted._
911

1012
import SymOps._

scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ trait TypesSupport:
5555
private def tpe(using Quotes)(symbol: reflect.Symbol)(using inCC: Option[Any]): SSignature =
5656
import SymOps._
5757
val dri: Option[DRI] = Option(symbol).filterNot(_.isHiddenByVisibility).map(_.dri)
58-
if inCC.isDefined then
58+
if inCC.isDefined then // we are in the context of a capture set and want paths to be rendered plainly
5959
dotty.tools.scaladoc.Plain(symbol.normalizedName).l
6060
else
6161
dotty.tools.scaladoc.Type(symbol.normalizedName, dri).l
@@ -115,9 +115,9 @@ trait TypesSupport:
115115
++ keyword(" & ").l
116116
++ inParens(inner(right), shouldWrapInParens(right, tp, false))
117117
case ByNameType(CapturingType(tpe, refs)) =>
118-
renderByNameArrow(using qctx)(Some(refs)) ++ (plain(" ") :: inner(tpe))
118+
emitByNameArrow(using qctx)(Some(refs)) ++ (plain(" ") :: inner(tpe))
119119
case ByNameType(tpe) =>
120-
renderByNameArrow(using qctx)(None) ++ (plain(" ") :: inner(tpe))
120+
emitByNameArrow(using qctx)(None) ++ (plain(" ") :: inner(tpe))
121121
case ConstantType(constant) =>
122122
plain(constant.show).l
123123
case ThisType(tpe) =>
@@ -130,12 +130,14 @@ trait TypesSupport:
130130
inner(tpe) :+ plain("*")
131131
case AppliedType(repeatedClass, Seq(tpe)) if isRepeated(repeatedClass) =>
132132
inner(tpe) :+ plain("*")
133-
case CapturingType(base, refs) => base match
134-
case t @ AppliedType(base, args) if t.isFunctionType =>
135-
functionType(base, args)(using inCC = Some(refs))
136-
case t : Refinement if t.isFunctionType =>
137-
inner(base)(using inCC = Some(refs))
138-
case _ => inner(base) ++ renderCapturing(refs)
133+
case CapturingType(base, refs) =>
134+
base match
135+
case t @ AppliedType(base, args) if t.isFunctionType =>
136+
functionType(base, args)(using inCC = Some(refs))
137+
case t : Refinement if t.isFunctionType =>
138+
inner(base)(using inCC = Some(refs))
139+
case t if t.isCapSet => emitCaptureSet(refs, omitCap = false)
140+
case _ => inner(base) ++ emitCapturing(refs)
139141
case AnnotatedType(tpe, _) =>
140142
inner(tpe)
141143
case tl @ TypeLambda(params, paramBounds, AppliedType(tpe, args))
@@ -211,7 +213,7 @@ trait TypesSupport:
211213
inCC match
212214
case None | Some(Nil) => keyword(arrPrefix + "->").l
213215
case Some(List(c)) if c.isCaptureRoot => keyword(arrPrefix + "=>").l
214-
case Some(refs) => keyword(arrPrefix + "->") :: renderCaptureSet(refs)
216+
case Some(refs) => keyword(arrPrefix + "->") :: emitCaptureSet(refs)
215217
else keyword(arrPrefix + "=>").l
216218
val resType = inner(m.resType)(using inCC = None)
217219
paramList ++ (plain(" ") :: arrow) ++ (plain(" ") :: resType)
@@ -265,6 +267,8 @@ trait TypesSupport:
265267
case _ => topLevelProcess(t)
266268
}) ++ plain("]").l
267269

270+
case t : TypeRef if t.isCapSet => emitCaptureSet(Nil)
271+
268272
case tp @ TypeRef(qual, typeName) =>
269273
qual match {
270274
case r: RecursiveThis => tpe(s"this.$typeName").l
@@ -350,7 +354,7 @@ trait TypesSupport:
350354
inCC: Option[List[reflect.TypeRepr]],
351355
): SSignature =
352356
import reflect._
353-
val arrow = plain(" ") :: (renderFunctionArrow(using qctx)(funTy, inCC) ++ plain(" ").l)
357+
val arrow = plain(" ") :: (emitFunctionArrow(using qctx)(funTy, inCC) ++ plain(" ").l)
354358
given Option[List[TypeRepr]] = None // FIXME: this is ugly
355359
args match
356360
case Nil => Nil
@@ -366,7 +370,10 @@ trait TypesSupport:
366370

367371
private def typeBound(using Quotes)(t: reflect.TypeRepr, low: Boolean)(using elideThis: reflect.ClassDef) =
368372
import reflect._
369-
val ignore = if (low) t.typeSymbol == defn.NothingClass else t.typeSymbol == defn.AnyClass
373+
val ignore = low && (ccEnabled && t.isCapSetPure
374+
|| t.typeSymbol == defn.NothingClass)
375+
|| !low && (ccEnabled && t.isCapSetCap
376+
|| t.typeSymbol == defn.AnyClass)
370377
val prefix = keyword(if low then " >: " else " <: ")
371378
t match {
372379
case l: TypeLambda => prefix :: inParens(inner(l)(using elideThis))
@@ -466,31 +473,30 @@ trait TypesSupport:
466473
case AnnotatedType(tr, _) => stripAnnotated(tr)
467474
case other => other
468475

469-
private def renderCapability(using Quotes)(ref: reflect.TypeRepr)(using elideThis: reflect.ClassDef): SSignature =
476+
private def emitCapability(using Quotes)(ref: reflect.TypeRepr)(using elideThis: reflect.ClassDef): SSignature =
470477
import reflect._
471478
ref match
472-
case ReachCapability(c) => renderCapability(c) :+ Keyword("*")
473-
case ReadOnlyCapability(c) => renderCapability(c) :+ Keyword(".rd")
479+
case ReachCapability(c) => emitCapability(c) :+ Keyword("*")
480+
case ReadOnlyCapability(c) => emitCapability(c) :+ Keyword(".rd")
474481
case ThisType(_) => List(Keyword("this"))
475482
case t => inner(t)(using skipTypeSuffix = true, inCC = Some(Nil))
476483

477-
private def renderCaptureSet(using Quotes)(refs: List[reflect.TypeRepr])(using elideThis: reflect.ClassDef): SSignature =
478-
import dotty.tools.scaladoc.tasty.NameNormalizer._
484+
private def emitCaptureSet(using Quotes)(refs: List[reflect.TypeRepr], omitCap: Boolean = true)(using elideThis: reflect.ClassDef): SSignature =
479485
import reflect._
480486
refs match
481-
case List(ref) if ref.isCaptureRoot => Nil
487+
case List(ref) if omitCap && ref.isCaptureRoot => Nil
482488
case refs =>
483-
val res0 = refs.map(renderCapability)
489+
val res0 = refs.map(emitCapability)
484490
val res1 = res0 match
485491
case Nil => Nil
486492
case other => other.reduce((r, e) => r ++ (List(Plain(", ")) ++ e))
487493
Plain("{") :: (res1 ++ List(Plain("}")))
488494

489-
private def renderCapturing(using Quotes)(refs: List[reflect.TypeRepr])(using elideThis: reflect.ClassDef): SSignature =
495+
private def emitCapturing(using Quotes)(refs: List[reflect.TypeRepr])(using elideThis: reflect.ClassDef): SSignature =
490496
import reflect._
491-
Keyword("^") :: renderCaptureSet(refs)
497+
Keyword("^") :: emitCaptureSet(refs)
492498

493-
private def renderFunctionArrow(using Quotes)(funTy: reflect.TypeRepr, captures: Option[List[reflect.TypeRepr]])(using elideThis: reflect.ClassDef): SSignature =
499+
private def emitFunctionArrow(using Quotes)(funTy: reflect.TypeRepr, captures: Option[List[reflect.TypeRepr]])(using elideThis: reflect.ClassDef): SSignature =
494500
import reflect._
495501
val isContextFun = funTy.isAnyContextFunction || funTy.isAnyImpureContextFunction
496502
val prefix = if isContextFun then "?" else ""
@@ -506,14 +512,14 @@ trait TypesSupport:
506512
else if isImpureFun then
507513
List(Keyword(prefix + "=>"))
508514
else
509-
report.error(s"Cannot render function arrow: expected a (Context)Function* or Impure(Context)Function*, but got: ${funTy.show}")
515+
report.error(s"Cannot emit function arrow: expected a (Context)Function* or Impure(Context)Function*, but got: ${funTy.show}")
510516
Nil
511517
case Some(refs) =>
512518
// there is some capture set
513519
refs match
514520
case Nil => List(Keyword(prefix + "->"))
515521
case List(ref) if ref.isCaptureRoot => List(Keyword(prefix + "=>"))
516-
case refs => Keyword(prefix + "->") :: renderCaptureSet(refs)
522+
case refs => Keyword(prefix + "->") :: emitCaptureSet(refs)
517523

518-
private def renderByNameArrow(using Quotes)(captures: Option[List[reflect.TypeRepr]])(using elideThis: reflect.ClassDef): SSignature =
519-
renderFunctionArrow(CaptureDefs.Function1.typeRef, captures)
524+
private def emitByNameArrow(using Quotes)(captures: Option[List[reflect.TypeRepr]])(using elideThis: reflect.ClassDef): SSignature =
525+
emitFunctionArrow(CaptureDefs.Function1.typeRef, captures)

0 commit comments

Comments
 (0)