Skip to content

Commit edd968c

Browse files
committed
Fix rendering paths and reach capabilities
1 parent ca2abd7 commit edd968c

File tree

3 files changed

+107
-44
lines changed

3 files changed

+107
-44
lines changed

local/project/dummy/arrows.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ package dummy
33
import language.experimental.captureChecking
44
import caps.*
55

6+
trait Nested:
7+
val c: AnyRef^
8+
val next: Nested
9+
610
trait Arrows:
711
val a: AnyRef^
812
val b: AnyRef^
@@ -29,7 +33,16 @@ trait Arrows:
2933
def usesAndConsumes(@use a: AnyRef^, @consume b: AnyRef^): Any
3034
def usesAndConsumes2(@use @consume x: AnyRef^{a}): Any
3135
def consumesAndUses(@consume @use x: AnyRef^{a}): Any
36+
def consumesAndUses2(@consume @use x: List[AnyRef^]): Array[AnyRef^{x*}]
37+
38+
def reachThis: AnyRef^{this*}
3239

3340
def byNamePure(f: -> Int): Int
3441
def byNameImpure(f: ->{a,b,c} Int): Int
3542
def byNameImpure2(f: => Int): Int
43+
44+
def pathDependent(n: Nested^)(g: AnyRef^{n.c} => Any): Any
45+
def pathDependent2(n: Nested^)(g: AnyRef^{n.next.c} => Any): Any
46+
def pathDependent3(n: Nested^)(g: AnyRef^{n.c} => AnyRef^{n.next.c} ->{n.c} Any): Any
47+
def pathDependent4(n: Nested^)(g: AnyRef^{n.c} => AnyRef^{n.next.c} ->{n.c} Any): AnyRef^{n.next.next.c}
48+
def pathDependent5(n: Nested^)(g: AnyRef^{n.c} => AnyRef^{n.next.c} ->{n.c} Any): AnyRef^{n.next.next.c*, n.c}

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

Lines changed: 49 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,36 @@ import scala.quoted._
66

77
object CaptureDefs:
88
// these should become part of the reflect API in the distant future
9-
def retains(using qctx: Quotes) = qctx.reflect.Symbol.requiredClass("scala.annotation.retains")
10-
def retainsCap(using qctx: Quotes) = qctx.reflect.Symbol.requiredClass("scala.annotation.retainsCap")
11-
def retainsByName(using qctx: Quotes) = qctx.reflect.Symbol.requiredClass("scala.annotation.retainsByName")
12-
def CapsModule(using qctx: Quotes) = qctx.reflect.Symbol.requiredPackage("scala.caps")
13-
def captureRoot(using qctx: Quotes) = qctx.reflect.Symbol.requiredPackage("scala.caps.cap")
14-
def Caps_Capability(using qctx: Quotes) = qctx.reflect.Symbol.requiredClass("scala.caps.Capability")
15-
def Caps_CapSet(using qctx: Quotes) = qctx.reflect.Symbol.requiredClass("scala.caps.CapSet")
16-
def Caps_Mutable(using qctx: Quotes) = qctx.reflect.Symbol.requiredClass("scala.caps.Mutable")
17-
def Caps_SharedCapability(using qctx: Quotes) = qctx.reflect.Symbol.requiredClass("scala.caps.SharedCapability")
18-
19-
def UseAnnot(using qctx: Quotes) = qctx.reflect.Symbol.requiredClass("scala.caps.use")
20-
def ConsumeAnnot(using qctx: Quotes) = qctx.reflect.Symbol.requiredClass("scala.caps.consume")
21-
9+
def retains(using qctx: Quotes) =
10+
qctx.reflect.Symbol.requiredClass("scala.annotation.retains")
11+
def retainsCap(using qctx: Quotes) =
12+
qctx.reflect.Symbol.requiredClass("scala.annotation.retainsCap")
13+
def retainsByName(using qctx: Quotes) =
14+
qctx.reflect.Symbol.requiredClass("scala.annotation.retainsByName")
15+
def CapsModule(using qctx: Quotes) =
16+
qctx.reflect.Symbol.requiredPackage("scala.caps")
17+
def captureRoot(using qctx: Quotes) =
18+
qctx.reflect.Symbol.requiredPackage("scala.caps.cap")
19+
def Caps_Capability(using qctx: Quotes) =
20+
qctx.reflect.Symbol.requiredClass("scala.caps.Capability")
21+
def Caps_CapSet(using qctx: Quotes) =
22+
qctx.reflect.Symbol.requiredClass("scala.caps.CapSet")
23+
def Caps_Mutable(using qctx: Quotes) =
24+
qctx.reflect.Symbol.requiredClass("scala.caps.Mutable")
25+
def Caps_SharedCapability(using qctx: Quotes) =
26+
qctx.reflect.Symbol.requiredClass("scala.caps.SharedCapability")
27+
def UseAnnot(using qctx: Quotes) =
28+
qctx.reflect.Symbol.requiredClass("scala.caps.use")
29+
def ConsumeAnnot(using qctx: Quotes) =
30+
qctx.reflect.Symbol.requiredClass("scala.caps.consume")
31+
def ReachCapabilityAnnot(using qctx: Quotes) =
32+
qctx.reflect.Symbol.requiredClass("scala.annotation.internal.reachCapability")
33+
def RootCapabilityAnnot(using qctx: Quotes) =
34+
qctx.reflect.Symbol.requiredClass("scala.caps.internal.rootCapability")
35+
def ReadOnlyCapabilityAnnot(using qctx: Quotes) =
36+
qctx.reflect.Symbol.requiredClass("scala.annotation.internal.readOnlyCapability")
37+
def RequiresCapabilityAnnot(using qctx: Quotes) =
38+
qctx.reflect.Symbol.requiredClass("scala.annotation.internal.requiresCapability")
2239
end CaptureDefs
2340

2441
extension (using qctx: Quotes)(ann: qctx.reflect.Symbol)
@@ -29,24 +46,38 @@ extension (using qctx: Quotes)(ann: qctx.reflect.Symbol)
2946
/** This symbol is one of `retains`, `retainsCap`, or `retainsByName` */
3047
def isRetainsLike: Boolean =
3148
ann.isRetains || ann == CaptureDefs.retainsByName
49+
50+
def isReachCapabilityAnnot: Boolean =
51+
ann == CaptureDefs.ReachCapabilityAnnot
3252
end extension
3353

3454
extension (using qctx: Quotes)(tpe: qctx.reflect.TypeRepr)
3555
def isCaptureRoot: Boolean = tpe.termSymbol == CaptureDefs.captureRoot
3656
end extension
3757

58+
object ReachCapability:
59+
def unapply(using qctx: Quotes)(ty: qctx.reflect.TypeRepr): Option[qctx.reflect.TypeRepr] =
60+
import qctx.reflect._
61+
ty match
62+
case AnnotatedType(base, Apply(Select(New(annot), _), Nil)) if annot.symbol.isReachCapabilityAnnot =>
63+
Some(base)
64+
case _ => None
65+
end ReachCapability
66+
3867
/** Decompose capture sets in the union-type-encoding into the sequence of atomic `TypeRepr`s.
3968
* Returns `None` if the type is not a capture set.
4069
*/
4170
def decomposeCaptureRefs(using qctx: Quotes)(typ0: qctx.reflect.TypeRepr): Option[List[qctx.reflect.TypeRepr]] =
4271
import qctx.reflect._
4372
val buffer = collection.mutable.ListBuffer.empty[TypeRepr]
73+
def include(t: TypeRepr): Boolean = { buffer += t; true }
4474
def traverse(typ: TypeRepr): Boolean =
4575
typ match
46-
case OrType(t1, t2) => traverse(t1) && traverse(t2)
47-
case t @ ThisType(_) => buffer += t; true
48-
case t @ TermRef(_, _) => buffer += t; true
49-
case t @ ParamRef(_, _) => buffer += t; true
76+
case OrType(t1, t2) => traverse(t1) && traverse(t2)
77+
case t @ ThisType(_) => include(t)
78+
case t @ TermRef(_, _) => include(t)
79+
case t @ ParamRef(_, _) => include(t)
80+
case t @ ReachCapability(_) => include(t)
5081
case t if t.typeSymbol == defn.NothingClass => true
5182
// TODO: are atoms only ever the above? Then we could refine the return type
5283
case _ => report.warning(s"Unexpected type tree $typ while trying to extract capture references from $typ0"); false // TODO remove warning eventually
@@ -66,22 +97,4 @@ object CapturingType:
6697
case AnnotatedType(base, Apply(Select(New(annot), _), Nil)) if annot.symbol == CaptureDefs.retainsCap =>
6798
Some((base, List(CaptureDefs.captureRoot.termRef)))
6899
case _ => None
69-
end CapturingType
70-
71-
def renderCaptureSet(using qctx: Quotes)(refs: List[qctx.reflect.TypeRepr]): List[SignaturePart] =
72-
import dotty.tools.scaladoc.tasty.NameNormalizer._
73-
import qctx.reflect._
74-
refs match
75-
case List(ref) if ref.isCaptureRoot => List(Keyword("^"))
76-
case refs =>
77-
val res0 = refs.map { ref =>
78-
ref match
79-
case ThisType(_) => List(Keyword("this"))
80-
case TermRef(_, sym) => List(Plain(sym)) // FIXME: use type other than Plain, can we have clickable links to say, caps.cap and other things?
81-
case pf @ ParamRef(tpe, i) => List(Plain(tpe.asInstanceOf[MethodType].paramNames(i))) // FIXME: not sure if this covers all cases
82-
case _ => List(Plain("<unknown>"))
83-
}
84-
val res1 = res0 match
85-
case Nil => Nil
86-
case other => other.reduce((r, e) => r ++ (List(Plain(", ")) ++ e))
87-
Keyword("^") :: Plain("{") :: (res1 ++ List(Plain("}")))
100+
end CapturingType

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

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ trait TypesSupport:
2121
def asSignature(elideThis: reflect.ClassDef): SSignature =
2222
import reflect._
2323
tpeTree match
24-
case TypeBoundsTree(low, high) => typeBoundsTreeOfHigherKindedType(low.tpe, high.tpe)(using elideThis)
24+
case TypeBoundsTree(low, high) => typeBoundsTreeOfHigherKindedType(low.tpe, high.tpe)(using elideThis, inCC = None)
2525
case tpeTree: TypeTree => topLevelProcess(tpeTree.tpe)(using elideThis)
2626
case term: Term => topLevelProcess(term.tpe)(using elideThis)
2727

@@ -35,19 +35,30 @@ trait TypesSupport:
3535

3636
private def keyword(str: String): SignaturePart = Keyword(str)
3737

38-
private def tpe(str: String, dri: DRI): SignaturePart = dotty.tools.scaladoc.Type(str, Some(dri))
38+
private def tpe(str: String, dri: DRI)(using inCC: Option[Unit]): SignaturePart =
39+
if inCC.isDefined then
40+
dotty.tools.scaladoc.Plain(str)
41+
else
42+
dotty.tools.scaladoc.Type(str, Some(dri))
3943

40-
private def tpe(str: String): SignaturePart = dotty.tools.scaladoc.Type(str, None)
44+
private def tpe(str: String)(using inCC: Option[Unit]): SignaturePart =
45+
if inCC.isDefined then
46+
dotty.tools.scaladoc.Plain(str)
47+
else
48+
dotty.tools.scaladoc.Type(str, None)
4149

4250
private def inParens(s: SSignature, wrap: Boolean = true) =
4351
if wrap then plain("(").l ++ s ++ plain(")").l else s
4452

4553
extension (on: SignaturePart) def l: List[SignaturePart] = List(on)
4654

47-
private def tpe(using Quotes)(symbol: reflect.Symbol): SSignature =
55+
private def tpe(using Quotes)(symbol: reflect.Symbol)(using inCC: Option[Unit]): SSignature =
4856
import SymOps._
4957
val dri: Option[DRI] = Option(symbol).filterNot(_.isHiddenByVisibility).map(_.dri)
50-
dotty.tools.scaladoc.Type(symbol.normalizedName, dri).l
58+
if inCC.isDefined then
59+
dotty.tools.scaladoc.Plain(symbol.normalizedName).l
60+
else
61+
dotty.tools.scaladoc.Type(symbol.normalizedName, dri).l
5162

5263
private def commas(lists: List[SSignature]) = lists match
5364
case List(single) => single
@@ -87,6 +98,9 @@ trait TypesSupport:
8798
elideThis: reflect.ClassDef,
8899
indent: Int = 0,
89100
skipTypeSuffix: Boolean = false,
101+
// inCC means in capture-checking context.
102+
// Somewhat hacky, because it should be a Boolean, but then it'd clash with skipTypeSuffix
103+
inCC: Option[Unit] = None,
90104
): SSignature =
91105
import reflect._
92106
def noSupported(name: String): SSignature =
@@ -102,7 +116,7 @@ trait TypesSupport:
102116
++ keyword(" & ").l
103117
++ inParens(inner(right), shouldWrapInParens(right, tp, false))
104118
case CapturingType(base, refs) =>
105-
inner(base) ++ renderCaptureSet(refs)
119+
inner(base) ++ renderCapturing(refs)
106120
case ByNameType(CapturingType(tpe, refs)) =>
107121
refs match
108122
case Nil => keyword("-> ") :: inner(tpe)
@@ -345,7 +359,7 @@ trait TypesSupport:
345359
case _ => Nil
346360
}
347361

348-
private def typeBoundsTreeOfHigherKindedType(using Quotes)(low: reflect.TypeRepr, high: reflect.TypeRepr)(using elideThis: reflect.ClassDef) =
362+
private def typeBoundsTreeOfHigherKindedType(using Quotes)(low: reflect.TypeRepr, high: reflect.TypeRepr)(using elideThis: reflect.ClassDef, inCC: Option[Unit]) =
349363
import reflect._
350364
def regularTypeBounds(low: TypeRepr, high: TypeRepr) =
351365
if low == high then keyword(" = ").l ++ inner(low)(using elideThis)
@@ -355,7 +369,7 @@ trait TypesSupport:
355369
if resType.typeSymbol == defn.AnyClass then
356370
plain("[").l ++ commas(params.zip(paramBounds).map { (name, typ) =>
357371
val normalizedName = if name.matches("_\\$\\d*") then "_" else name
358-
tpe(normalizedName).l ++ inner(typ)(using elideThis)
372+
tpe(normalizedName)(using inCC).l ++ inner(typ)(using elideThis)
359373
}) ++ plain("]").l
360374
else
361375
regularTypeBounds(low, high)
@@ -435,3 +449,26 @@ trait TypesSupport:
435449
tr match
436450
case AnnotatedType(tr, _) => stripAnnotated(tr)
437451
case other => other
452+
453+
private def renderCapability(using Quotes)(ref: reflect.TypeRepr)(using elideThis: reflect.ClassDef): List[SignaturePart] =
454+
import reflect._
455+
ref match
456+
case ReachCapability(c) => renderCapability(c) :+ Keyword("*")
457+
case ThisType(_) => List(Keyword("this"))
458+
case t => inner(t)(using skipTypeSuffix = true, inCC = Some(()))
459+
460+
private def renderCaptureSet(using Quotes)(refs: List[reflect.TypeRepr])(using elideThis: reflect.ClassDef): List[SignaturePart] =
461+
import dotty.tools.scaladoc.tasty.NameNormalizer._
462+
import reflect._
463+
refs match
464+
case List(ref) if ref.isCaptureRoot => Nil
465+
case refs =>
466+
val res0 = refs.map(renderCapability)
467+
val res1 = res0 match
468+
case Nil => Nil
469+
case other => other.reduce((r, e) => r ++ (List(Plain(", ")) ++ e))
470+
Plain("{") :: (res1 ++ List(Plain("}")))
471+
472+
private def renderCapturing(using Quotes)(refs: List[reflect.TypeRepr])(using elideThis: reflect.ClassDef): List[SignaturePart] =
473+
import reflect._
474+
Keyword("^") :: renderCaptureSet(refs)

0 commit comments

Comments
 (0)