Skip to content

Commit

Permalink
Expand Capability types T to T^ only if no explicit capture set is gi…
Browse files Browse the repository at this point in the history
…ven (#21375)

If C is a class extending caps.Capability we used to always expand C to
C^, even if there was an explicitly given capture set. For instance
`C^{x}` got expanded to `C^{cap}^{x}` which caused a redundant capture x
warning.

We now do this expansion only if there is no explicitly given capture
set.

@natsukagami I hope this helps the Async use case.
  • Loading branch information
bracevac authored Aug 15, 2024
2 parents bcf5d6a + 5dd0bf7 commit 99f431e
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 7 deletions.
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala
Original file line number Diff line number Diff line change
Expand Up @@ -632,7 +632,7 @@ class CheckCaptures extends Recheck, SymTransformer:
def addParamArgRefinements(core: Type, initCs: CaptureSet): (Type, CaptureSet) =
var refined: Type = core
var allCaptures: CaptureSet =
if core.derivesFromCapability then CaptureSet.universal else initCs
if core.derivesFromCapability then defn.universalCSImpliedByCapability else initCs
for (getterName, argType) <- mt.paramNames.lazyZip(argTypes) do
val getter = cls.info.member(getterName).suchThat(_.isRefiningParamAccessor).symbol
if !getter.is(Private) && getter.hasTrackedParts then
Expand Down Expand Up @@ -809,7 +809,7 @@ class CheckCaptures extends Recheck, SymTransformer:
val localSet = capturedVars(sym)
if !localSet.isAlwaysEmpty then
curEnv = Env(sym, EnvKind.Regular, localSet, curEnv)

// ctx with AssumedContains entries for each Contains parameter
val bodyCtx =
var ac = CaptureSet.assumedContains
Expand All @@ -828,7 +828,7 @@ class CheckCaptures extends Recheck, SymTransformer:
interpolateVarsIn(tree.tpt)
curEnv = saved
end recheckDefDef

/** If val or def definition with inferred (result) type is visible
* in other compilation units, check that the actual inferred type
* conforms to the expected type where all inferred capture sets are dropped.
Expand Down
16 changes: 12 additions & 4 deletions compiler/src/dotty/tools/dotc/cc/Setup.scala
Original file line number Diff line number Diff line change
Expand Up @@ -299,24 +299,32 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
CapturingType(fntpe, cs, boxed = false)
else fntpe

def stripImpliedCaptureSet(tp: Type): Type = tp match
case tp @ CapturingType(parent, refs)
if (refs eq defn.universalCSImpliedByCapability) && !tp.isBoxedCapturing =>
parent
case tp @ CapturingType(parent, refs) => tp
case _ => tp

def apply(t: Type) =
t match
case t @ CapturingType(parent, refs) =>
t.derivedCapturingType(this(parent), refs)
t.derivedCapturingType(stripImpliedCaptureSet(this(parent)), refs)
case t @ AnnotatedType(parent, ann) =>
val parent1 = this(parent)
if ann.symbol.isRetains then
val parent2 = stripImpliedCaptureSet(parent1)
for tpt <- tptToCheck do
checkWellformedLater(parent1, ann.tree, tpt)
CapturingType(parent1, ann.tree.toCaptureSet)
checkWellformedLater(parent2, ann.tree, tpt)
CapturingType(parent2, ann.tree.toCaptureSet)
else
t.derivedAnnotatedType(parent1, ann)
case throwsAlias(res, exc) =>
this(expandThrowsAlias(res, exc, Nil))
case t =>
// Map references to capability classes C to C^
if t.derivesFromCapability && !t.isSingleton && t.typeSymbol != defn.Caps_Exists
then CapturingType(t, CaptureSet.universal, boxed = false)
then CapturingType(t, defn.universalCSImpliedByCapability, boxed = false)
else normalizeCaptures(mapOver(t))
end toCapturing

Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1005,6 +1005,9 @@ class Definitions {
@tu lazy val Caps_ContainsTrait: TypeSymbol = CapsModule.requiredType("Contains")
@tu lazy val Caps_containsImpl: TermSymbol = CapsModule.requiredMethod("containsImpl")

/** The same as CaptureSet.universal but generated implicitly for references of Capability subtypes */
@tu lazy val universalCSImpliedByCapability = CaptureSet(captureRoot.termRef)

@tu lazy val PureClass: Symbol = requiredClass("scala.Pure")

// Annotation base classes
Expand Down
12 changes: 12 additions & 0 deletions tests/pos-custom-args/captures/cap-refine.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//> using options -Werror
import caps.Capability

trait Buffer[T] extends Capability:
def append(x: T): this.type

def f(buf: Buffer[Int]) =
val buf1 = buf.append(1).append(2)
val buf2: Buffer[Int]^{buf1} = buf1



0 comments on commit 99f431e

Please sign in to comment.