Skip to content

Commit

Permalink
Change handling of curried function types in capture checking (#18131)
Browse files Browse the repository at this point in the history
Implements the proposed treatment in cc-paths. 

- Capture sets of curried functions don't automatically propagate to the
left
- When computing the capture set of a reference, pick up capture sets
along the span of curried functions
- Drop the previous syntactic sugar for propagating captures left to
right in curried function types.
  • Loading branch information
odersky authored Jul 15, 2023
2 parents 04eae14 + c7a5350 commit ca19b46
Show file tree
Hide file tree
Showing 15 changed files with 304 additions and 307 deletions.
72 changes: 42 additions & 30 deletions compiler/src/dotty/tools/dotc/cc/CaptureSet.scala
Original file line number Diff line number Diff line change
Expand Up @@ -210,10 +210,11 @@ sealed abstract class CaptureSet extends Showable:
* any of the elements in the constant capture set `that`
*/
def -- (that: CaptureSet.Const)(using Context): CaptureSet =
val elems1 = elems.filter(!that.accountsFor(_))
if elems1.size == elems.size then this
else if this.isConst then Const(elems1)
else Diff(asVar, that)
if this.isConst then
val elems1 = elems.filter(!that.accountsFor(_))
if elems1.size == elems.size then this else Const(elems1)
else
if that.isAlwaysEmpty then this else Diff(asVar, that)

/** The largest subset (via <:<) of this capture set that does not account for `ref` */
def - (ref: CaptureRef)(using Context): CaptureSet =
Expand Down Expand Up @@ -845,34 +846,45 @@ object CaptureSet:
/** The capture set of the type underlying a CaptureRef */
def ofInfo(ref: CaptureRef)(using Context): CaptureSet = ref match
case ref: TermRef if ref.isRootCapability => ref.singletonCaptureSet
case _ => ofType(ref.underlying)
case _ => ofType(ref.underlying, followResult = true)

/** Capture set of a type */
def ofType(tp: Type)(using Context): CaptureSet =
def recur(tp: Type): CaptureSet = tp.dealias match
case tp: TermRef =>
tp.captureSet
case tp: TermParamRef =>
tp.captureSet
case _: TypeRef =>
if tp.classSymbol.hasAnnotation(defn.CapabilityAnnot) then universal else empty
case _: TypeParamRef =>
empty
case CapturingType(parent, refs) =>
recur(parent) ++ refs
case AppliedType(tycon, args) =>
val cs = recur(tycon)
tycon.typeParams match
case tparams @ (LambdaParam(tl, _) :: _) => cs.substParams(tl, args)
case _ => cs
case tp: TypeProxy =>
recur(tp.underlying)
case AndType(tp1, tp2) =>
recur(tp1) ** recur(tp2)
case OrType(tp1, tp2) =>
recur(tp1) ++ recur(tp2)
case _ =>
empty
def ofType(tp: Type, followResult: Boolean)(using Context): CaptureSet =
def recur(tp: Type): CaptureSet = trace(i"ofType $tp, ${tp.getClass} $followResult", show = true):
tp.dealias match
case tp: TermRef =>
tp.captureSet
case tp: TermParamRef =>
tp.captureSet
case _: TypeRef =>
if tp.classSymbol.hasAnnotation(defn.CapabilityAnnot) then universal else empty
case _: TypeParamRef =>
empty
case CapturingType(parent, refs) =>
recur(parent) ++ refs
case tpd @ RefinedType(parent, _, rinfo: MethodType)
if followResult && defn.isFunctionType(tpd) =>
ofType(parent, followResult = false) // pick up capture set from parent type
++ (recur(rinfo.resType) // add capture set of result
-- CaptureSet(rinfo.paramRefs.filter(_.isTracked)*)) // but disregard bound parameters
case tpd @ AppliedType(tycon, args) =>
if followResult && defn.isNonRefinedFunction(tpd) then
recur(args.last)
// must be (pure) FunctionN type since ImpureFunctions have already
// been eliminated in selector's dealias. Use capture set of result.
else
val cs = recur(tycon)
tycon.typeParams match
case tparams @ (LambdaParam(tl, _) :: _) => cs.substParams(tl, args)
case _ => cs
case tp: TypeProxy =>
recur(tp.underlying)
case AndType(tp1, tp2) =>
recur(tp1) ** recur(tp2)
case OrType(tp1, tp2) =>
recur(tp1) ++ recur(tp2)
case _ =>
empty
recur(tp)
.showing(i"capture set of $tp = $result", capt)

Expand Down
Loading

0 comments on commit ca19b46

Please sign in to comment.