diff --git a/compiler/src/dotty/tools/dotc/cc/Capability.scala b/compiler/src/dotty/tools/dotc/cc/Capability.scala index 41a084a5d87e..77c67dff5a00 100644 --- a/compiler/src/dotty/tools/dotc/cc/Capability.scala +++ b/compiler/src/dotty/tools/dotc/cc/Capability.scala @@ -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(_) => diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala index d5f8b635fa58..420e6e1ab253 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala @@ -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() @@ -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. diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 990eb01b999e..493a4207717d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -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 { diff --git a/tests/pos/i23885/S_1.scala b/tests/pos/i23885/S_1.scala new file mode 100644 index 000000000000..85b5dad758c4 --- /dev/null +++ b/tests/pos/i23885/S_1.scala @@ -0,0 +1,4 @@ +import language.experimental.captureChecking + +class A: + def f(x: A^): A^{this, x} = ??? diff --git a/tests/pos/i23885/S_2.scala b/tests/pos/i23885/S_2.scala new file mode 100644 index 000000000000..651f8f7f5330 --- /dev/null +++ b/tests/pos/i23885/S_2.scala @@ -0,0 +1,3 @@ +class B: + private val a: A = ??? + def g(b: A) = a.f(b)