Skip to content

Commit 5e6b805

Browse files
committed
Fix rendering paths and reach capabilities
1 parent 88934a3 commit 5e6b805

File tree

3 files changed

+108
-44
lines changed

3 files changed

+108
-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: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,13 @@ trait TypesSupport:
2121
def asSignature(elideThis: reflect.ClassDef, originalOwner: reflect.Symbol, skipThisTypePrefix: Boolean): SSignature =
2222
import reflect._
2323
tpeTree match
24-
case TypeBoundsTree(low, high) => typeBoundsTreeOfHigherKindedType(low.tpe, high.tpe, skipThisTypePrefix)(using elideThis, originalOwner)
24+
case TypeBoundsTree(low, high) => typeBoundsTreeOfHigherKindedType(low.tpe, high.tpe, skipThisTypePrefix)(using elideThis, originalOwner, inCC = None)
2525
case tpeTree: TypeTree => topLevelProcess(tpeTree.tpe, skipThisTypePrefix)(using elideThis, originalOwner)
2626
case term: Term => topLevelProcess(term.tpe, skipThisTypePrefix)(using elideThis, originalOwner)
2727
def asSignature(elideThis: reflect.ClassDef, originalOwner: reflect.Symbol): SSignature =
2828
tpeTree.asSignature(elideThis, originalOwner, skipThisTypePrefix = false)
2929

30+
3031
given TypeSyntax: AnyRef with
3132
extension (using Quotes)(tpe: reflect.TypeRepr)
3233
def asSignature(elideThis: reflect.ClassDef, originalOwner: reflect.Symbol, skipThisTypePrefix: Boolean): SSignature =
@@ -39,19 +40,30 @@ trait TypesSupport:
3940

4041
private def keyword(str: String): SignaturePart = Keyword(str)
4142

42-
private def tpe(str: String, dri: DRI): SignaturePart = dotty.tools.scaladoc.Type(str, Some(dri))
43+
private def tpe(str: String, dri: DRI)(using inCC: Option[Unit]): SignaturePart =
44+
if inCC.isDefined then
45+
dotty.tools.scaladoc.Plain(str)
46+
else
47+
dotty.tools.scaladoc.Type(str, Some(dri))
4348

44-
private def tpe(str: String): SignaturePart = dotty.tools.scaladoc.Type(str, None)
49+
private def tpe(str: String)(using inCC: Option[Unit]): SignaturePart =
50+
if inCC.isDefined then
51+
dotty.tools.scaladoc.Plain(str)
52+
else
53+
dotty.tools.scaladoc.Type(str, None)
4554

4655
protected def inParens(s: SSignature, wrap: Boolean = true) =
4756
if wrap then plain("(").l ++ s ++ plain(")").l else s
4857

4958
extension (on: SignaturePart) def l: List[SignaturePart] = List(on)
5059

51-
private def tpe(using Quotes)(symbol: reflect.Symbol): SSignature =
60+
private def tpe(using Quotes)(symbol: reflect.Symbol)(using inCC: Option[Unit]): SSignature =
5261
import SymOps._
5362
val dri: Option[DRI] = Option(symbol).filterNot(_.isHiddenByVisibility).map(_.dri)
54-
dotty.tools.scaladoc.Type(symbol.normalizedName, dri).l
63+
if inCC.isDefined then
64+
dotty.tools.scaladoc.Plain(symbol.normalizedName).l
65+
else
66+
dotty.tools.scaladoc.Type(symbol.normalizedName, dri).l
5567

5668
private def commas(lists: List[SSignature]) = lists match
5769
case List(single) => single
@@ -93,6 +105,9 @@ trait TypesSupport:
93105
originalOwner: reflect.Symbol,
94106
indent: Int = 0,
95107
skipTypeSuffix: Boolean = false,
108+
// inCC means in capture-checking context.
109+
// Somewhat hacky, because it should be a Boolean, but then it'd clash with skipTypeSuffix
110+
inCC: Option[Unit] = None,
96111
): SSignature =
97112
import reflect._
98113
def noSupported(name: String): SSignature =
@@ -108,7 +123,7 @@ trait TypesSupport:
108123
++ keyword(" & ").l
109124
++ inParens(inner(right, skipThisTypePrefix), shouldWrapInParens(right, tp, false))
110125
case CapturingType(base, refs) =>
111-
inner(base, skipThisTypePrefix) ++ renderCaptureSet(refs)
126+
inner(base, skipThisTypePrefix) ++ renderCapturing(refs)
112127
case ByNameType(CapturingType(tpe, refs)) =>
113128
refs match
114129
case Nil => keyword("-> ") :: inner(tpe, skipThisTypePrefix)
@@ -355,7 +370,7 @@ trait TypesSupport:
355370
}
356371

357372
private def typeBoundsTreeOfHigherKindedType(using Quotes)(low: reflect.TypeRepr, high: reflect.TypeRepr, skipThisTypePrefix: Boolean)(
358-
using elideThis: reflect.ClassDef, originalOwner: reflect.Symbol
373+
using elideThis: reflect.ClassDef, originalOwner: reflect.Symbol, inCC: Option[Unit]
359374
) =
360375
import reflect._
361376
def regularTypeBounds(low: TypeRepr, high: TypeRepr) =
@@ -366,7 +381,7 @@ trait TypesSupport:
366381
if resType.typeSymbol == defn.AnyClass then
367382
plain("[").l ++ commas(params.zip(paramBounds).map { (name, typ) =>
368383
val normalizedName = if name.matches("_\\$\\d*") then "_" else name
369-
tpe(normalizedName).l ++ inner(typ, skipThisTypePrefix)(using elideThis, originalOwner)
384+
tpe(normalizedName)(using inCC).l ++ inner(typ, skipThisTypePrefix)(using elideThis, originalOwner)
370385
}) ++ plain("]").l
371386
else
372387
regularTypeBounds(low, high)
@@ -448,3 +463,26 @@ trait TypesSupport:
448463
tr match
449464
case AnnotatedType(tr, _) => stripAnnotated(tr)
450465
case other => other
466+
467+
private def renderCapability(using Quotes)(ref: reflect.TypeRepr)(using elideThis: reflect.ClassDef): List[SignaturePart] =
468+
import reflect._
469+
ref match
470+
case ReachCapability(c) => renderCapability(c) :+ Keyword("*")
471+
case ThisType(_) => List(Keyword("this"))
472+
case t => inner(t)(using skipTypeSuffix = true, inCC = Some(()))
473+
474+
private def renderCaptureSet(using Quotes)(refs: List[reflect.TypeRepr])(using elideThis: reflect.ClassDef): List[SignaturePart] =
475+
import dotty.tools.scaladoc.tasty.NameNormalizer._
476+
import reflect._
477+
refs match
478+
case List(ref) if ref.isCaptureRoot => Nil
479+
case refs =>
480+
val res0 = refs.map(renderCapability)
481+
val res1 = res0 match
482+
case Nil => Nil
483+
case other => other.reduce((r, e) => r ++ (List(Plain(", ")) ++ e))
484+
Plain("{") :: (res1 ++ List(Plain("}")))
485+
486+
private def renderCapturing(using Quotes)(refs: List[reflect.TypeRepr])(using elideThis: reflect.ClassDef): List[SignaturePart] =
487+
import reflect._
488+
Keyword("^") :: renderCaptureSet(refs)

0 commit comments

Comments
 (0)