Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/cc/Capability.scala
Original file line number Diff line number Diff line change
Expand Up @@ -898,7 +898,9 @@ object Capabilities:
case t @ AnnotatedType(parent, ann) =>
val parent1 = this(parent)
if ann.symbol.isRetains && ann.tree.toCaptureSet.containsCap then
this(CapturingType(parent1, ann.tree.toCaptureSet))
// Applying `this` can cause infinite recursion in some cases during printing.
// scalac -Xprint:all tests/pos/i23885/S_1.scala tests/pos/i23885/S_2.scala
mapOver(CapturingType(parent1, ann.tree.toCaptureSet))
else
t.derivedAnnotatedType(parent1, ann)
case defn.RefinedFunctionOf(_) =>
Expand Down
18 changes: 12 additions & 6 deletions compiler/src/dotty/tools/dotc/cc/CaptureOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import Annotations.Annotation
import CaptureSet.VarState
import Capabilities.*
import StdNames.nme
import config.Feature

/** Attachment key for capturing type trees */
private val Captures: Key[CaptureSet] = Key()
Expand Down Expand Up @@ -634,13 +635,18 @@ extension (tp: AnnotatedType)
case ann: CaptureAnnotation => ann.boxed
case _ => false

/** Drop retains annotations in the type. */
/** Drop retains annotations in the inferred type if CC is not enabled
* or transform them into RetainingTypes if CC is enabled.
*/
class CleanupRetains(using Context) extends TypeMap:
def apply(tp: Type): Type =
tp match
case AnnotatedType(tp, annot) if annot.symbol == defn.RetainsAnnot || annot.symbol == defn.RetainsByNameAnnot =>
RetainingType(tp, defn.NothingType, byName = annot.symbol == defn.RetainsByNameAnnot)
case _ => mapOver(tp)
def apply(tp: Type): Type = tp match
case AnnotatedType(parent, annot) if annot.symbol.isRetainsLike =>
if Feature.ccEnabled then
if annot.symbol == defn.RetainsAnnot || annot.symbol == defn.RetainsByNameAnnot then
RetainingType(parent, defn.NothingType, byName = annot.symbol == defn.RetainsByNameAnnot)
else mapOver(tp)
else apply(parent)
case _ => mapOver(tp)

/** A base class for extractors that match annotated types with a specific
* Capability annotation.
Expand Down
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -771,8 +771,9 @@ object Checking {
!(symBoundary.isContainedIn(otherBoundary) ||
otherLinkedBoundary.exists && symBoundary.isContainedIn(otherLinkedBoundary))
}
&& !(inCaptureSet && other.isAllOf(LocalParamAccessor))
// class parameters in capture sets are not treated as leaked since in
&& !(inCaptureSet && (!Feature.ccEnabled || other.isAllOf(LocalParamAccessor)))
// All references are skipped in capture sets when CC is not enabled.
// Class parameters in capture sets are not treated as leaked since in
// phase CheckCaptures these are treated as normal vals.

def apply(tp: Type): Type = tp match {
Expand Down
4 changes: 4 additions & 0 deletions tests/pos/i23885/S_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import language.experimental.captureChecking

class A:
def f(x: A^): A^{this, x} = ???
3 changes: 3 additions & 0 deletions tests/pos/i23885/S_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class B:
private val a: A = ???
def g(b: A) = a.f(b)
Loading