Skip to content

Commit e0c3ee6

Browse files
committed
Represent all retains annotations as CompactAnnotations
1 parent 0ed90d5 commit e0c3ee6

File tree

8 files changed

+58
-33
lines changed

8 files changed

+58
-33
lines changed

compiler/src/dotty/tools/dotc/cc/CaptureOps.scala

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import Annotations.Annotation
1414
import CaptureSet.VarState
1515
import Capabilities.*
1616
import Mutability.isStatefulType
17-
import StdNames.nme
17+
import StdNames.{nme, tpnme}
1818
import config.Feature
1919
import NameKinds.TryOwnerName
2020
import typer.ProtoTypes.WildcardSelectionProto
@@ -547,11 +547,13 @@ extension (sym: Symbol)
547547

548548
/** This symbol is one of `retains` or `retainsCap` */
549549
def isRetains(using Context): Boolean =
550-
sym == defn.RetainsAnnot || sym == defn.RetainsCapAnnot
550+
sym.name == tpnme.retains && sym == defn.RetainsAnnot
551+
|| sym.name == tpnme.retainsCap && sym == defn.RetainsCapAnnot
551552

552553
/** This symbol is one of `retains`, `retainsCap`, or`retainsByName` */
553554
def isRetainsLike(using Context): Boolean =
554-
isRetains || sym == defn.RetainsByNameAnnot
555+
isRetains
556+
|| sym.name == tpnme.retainsByName && sym == defn.RetainsByNameAnnot
555557

556558
/** A class is pure if:
557559
* - one its base types has an explicitly declared self type with an empty capture set
@@ -658,7 +660,8 @@ class PathSelectionProto(val select: Select, val pt: Type) extends typer.ProtoTy
658660
def selector(using Context): Symbol = select.symbol
659661

660662
/** Drop retains annotations in the inferred type if CC is not enabled
661-
* or transform them into RetainingTypes if CC is enabled.
663+
* or transform them into RetainingTypes with Nothing as argument if CC is enabled
664+
* (we need to do that to keep by-name status).
662665
*/
663666
class CleanupRetains(using Context) extends TypeMap:
664667
def apply(tp: Type): Type = tp match

compiler/src/dotty/tools/dotc/cc/ccConfig.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ object ccConfig:
5959

6060
/** Not used currently. Handy for trying out new features */
6161
def newScheme(using ctx: Context): Boolean =
62-
Feature.sourceVersion.stable.isAtLeast(SourceVersion.`3.9`)
62+
Feature.sourceVersion.stable.isAtLeast(SourceVersion.`3.7`)
6363

6464
def allowUse(using Context): Boolean =
6565
Feature.sourceVersion.stable.isAtMost(SourceVersion.`3.7`)

compiler/src/dotty/tools/dotc/core/Annotations.scala

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,8 @@ object Annotations {
8585
EmptyAnnotation // strip retains-like annotations unless capture checking is enabled
8686
else
8787
val mappedType = sanitize(tm(arg.tpe))
88-
if mappedType `eql` arg.tpe then
89-
this
90-
else if cc.ccConfig.newScheme then
91-
CompactAnnotation(symbol.typeRef.appliedTo(mappedType))
92-
else
93-
derivedAnnotation(rebuild(tree, mappedType))
88+
if mappedType `eql` arg.tpe then this
89+
else CompactAnnotation(symbol.typeRef.appliedTo(mappedType))
9490

9591
case args =>
9692
// Checks if `tm` would result in any change by applying it to types
@@ -185,16 +181,16 @@ object Annotations {
185181
case _ => None
186182

187183
override def mapWith(tm: TypeMap)(using Context): Annotation =
188-
def derived(tp: Type) =
189-
if tm.isRange(tp) then EmptyAnnotation else derivedAnnotation(tp)
190-
def sanitizeArg(tp: Type) = tp match
191-
case tp @ AppliedType(tycon, args) =>
192-
tp.derivedAppliedType(tycon, args.mapConserve(sanitize))
184+
val isRetains = symbol.isRetainsLike
185+
if isRetains && !Feature.ccEnabledSomewhere then EmptyAnnotation
186+
else tm(tp) match
187+
case tp1 @ AppliedType(tycon, args) =>
188+
val args1 = if isRetains then args.mapConserve(sanitize) else args
189+
derivedAnnotation(tp1.derivedAppliedType(tycon, args1))
190+
case tp1: TypeRef =>
191+
derivedAnnotation(tp1)
193192
case _ =>
194-
tp
195-
if !symbol.isRetainsLike then derived(tm(tp))
196-
else if Feature.ccEnabledSomewhere then derived(sanitizeArg(tm(tp)))
197-
else EmptyAnnotation // strip retains-like annotations unless capture checking is enabled
193+
EmptyAnnotation
198194

199195
override def refersToParamOf(tl: TermLambda)(using Context): Boolean =
200196
refersToLambdaParam(tp, tl)
@@ -203,6 +199,12 @@ object Annotations {
203199
override def eql(that: Annotation) = that match
204200
case that: CompactAnnotation => this.tp `eql` that.tp
205201
case _ => false
202+
203+
object CompactAnnotation:
204+
def apply(tree: Tree)(using Context): CompactAnnotation =
205+
val argTypes = tpd.allArguments(tree).map(_.tpe)
206+
apply(annotClass(tree).typeRef.appliedTo(argTypes))
207+
206208
end CompactAnnotation
207209

208210
/** Sanitize @retains arguments to approximate illegal types that could cause a compilation
@@ -313,7 +315,18 @@ object Annotations {
313315

314316
def apply(tree: Tree): Annotation = tree match
315317
case tree: TypeTree => CompactAnnotation(tree.tpe)
316-
case _ => ConcreteAnnotation(tree)
318+
case _ =>
319+
def assertNoRetains(t: Tree): Unit = t match
320+
case Apply(fn, _) => assertNoRetains(fn)
321+
case TypeApply(fn, _) => assertNoRetains(fn)
322+
case Select(t, StdNames.nme.CONSTRUCTOR) => assertNoRetains(t)
323+
case New(t) => assertNoRetains(t)
324+
case t: RefTree =>
325+
if t.name.toString == "retains" || t.name.toString == "retainsByName" then
326+
new Error().printStackTrace()
327+
case _ =>
328+
//assertNoRetains(tree)
329+
ConcreteAnnotation(tree)
317330

318331
def apply(cls: ClassSymbol, span: Span)(using Context): Annotation =
319332
apply(cls, Nil, span)
@@ -328,7 +341,9 @@ object Annotations {
328341
apply(atp, arg :: Nil, span)
329342

330343
def apply(atp: Type, args: List[Tree], span: Span)(using Context): Annotation =
331-
apply(New(atp, args).withSpan(span))
344+
if atp.typeSymbol.isRetainsLike && args.isEmpty
345+
then CompactAnnotation(atp)
346+
else apply(New(atp, args).withSpan(span))
332347

333348
/** Create an annotation where the tree is computed lazily. */
334349
def deferred(sym: Symbol)(treeFn: Context ?=> Tree): Annotation =

compiler/src/dotty/tools/dotc/transform/PostTyper.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,10 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
201201
inJavaAnnot = annot.symbol.is(JavaDefined)
202202
if (inJavaAnnot) checkValidJavaAnnotation(annot)
203203
try
204-
val annotCtx = if annot.hasAttachment(untpd.RetainsAnnot)
205-
then ctx.addMode(Mode.InCaptureSet) else ctx
204+
val annotCtx =
205+
if annot.hasAttachment(untpd.RetainsAnnot)
206+
then ctx.addMode(Mode.InCaptureSet)
207+
else ctx
206208
transform(annot)(using annotCtx)
207209
finally inJavaAnnot = saved
208210
}

compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import NameOps.*
1111
import collection.mutable
1212
import reporting.*
1313
import Checking.{checkNoPrivateLeaks, checkNoWildcard}
14-
import cc.CaptureSet
14+
import cc.{CaptureSet, isRetainsLike}
1515
import util.Property
1616
import transform.Splicer
1717

@@ -572,7 +572,10 @@ trait TypeAssigner {
572572

573573
def assignType(tree: untpd.Annotated, arg: Tree, annot: Tree)(using Context): Annotated = {
574574
assert(tree.isType) // annotating a term is done via a Typed node, can't use Annotate directly
575-
tree.withType(AnnotatedType(arg.tpe, Annotation(annot)))
575+
val ann =
576+
if annotClass(annot).isRetainsLike then CompactAnnotation(annot)
577+
else Annotation(annot)
578+
tree.withType(AnnotatedType(arg.tpe, ann))
576579
}
577580

578581
def assignType(tree: untpd.PackageDef, pid: Tree)(using Context): PackageDef =

tests/neg-custom-args/captures/lazyvals.check

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
| Note that capability console is not included in capture set {x}.
1717
|
1818
| longer explanation available when compiling with `-explain`
19-
-- Error: tests/neg-custom-args/captures/lazyvals.scala:16:18 ----------------------------------------------------------
19+
-- Error: tests/neg-custom-args/captures/lazyvals.scala:16:12 ----------------------------------------------------------
2020
16 | val fun3: () ->{x} String = () => x() // error // error
21-
| ^
22-
| (x : () -> String) cannot be tracked since its capture set is empty
21+
| ^^^^^^^^^^^^^^^
22+
| (x : () -> String) cannot be tracked since its capture set is empty

tests/neg-custom-args/captures/spread-problem.check

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
11 | race(src1, src2) // error
88
| ^^^^^^^^^^
99
| Found: (Source[T]^, Source[T]^)
10-
| Required: Seq[Source[T]^]
10+
| Required: Seq[Source[T]^{C}]
11+
|
12+
| where: C is a type variable with constraint >: scala.caps.CapSet and <: scala.caps.CapSet^
1113
|
1214
| longer explanation available when compiling with `-explain`
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
-- Error: tests/neg-custom-args/captures/wf-reach-1.scala:2:17 ---------------------------------------------------------
1+
-- Error: tests/neg-custom-args/captures/wf-reach-1.scala:2:9 ----------------------------------------------------------
22
2 | val y: Object^{x*} = ??? // error
3-
| ^^
4-
| x* cannot be tracked since its deep capture set is empty
3+
| ^^^^^^^^^^^
4+
| x* cannot be tracked since its deep capture set is empty

0 commit comments

Comments
 (0)