Skip to content

Commit 4d4898e

Browse files
committed
Fix CaptureSet.ofInfo for capture variables
This change now makes both the tests cc-poly-varargs and capturesubtyping pass. - Move CapSet-related methods to CaptureOps - Let NamedType case in secondTry of TypeComparer handle capture variables as well - Added a case in capturesubtyping test to test forgotten NamedType case above
1 parent c25d11c commit 4d4898e

File tree

4 files changed

+35
-31
lines changed

4 files changed

+35
-31
lines changed

Diff for: compiler/src/dotty/tools/dotc/cc/CaptureOps.scala

+26
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,32 @@ extension (tree: Tree)
170170

171171
extension (tp: Type)
172172

173+
/**
174+
* Is the type `tp` a `CapSet` type, i.e., a capture variable?
175+
*
176+
* @param tp The type to check
177+
* @param includeCapSet Whether to include the bare `CapSet` type itself in the check, false at the top level
178+
*/
179+
final def isCapSet(includeCapSet: Boolean = false)(using Context): Boolean = tp match
180+
case tp: TypeRef => (includeCapSet && (tp.symbol eq defn.Caps_CapSet)) || {
181+
tp.underlying match
182+
case TypeBounds(lo, hi) => lo.isCapSet(true) && hi.isCapSet(true)
183+
case TypeAlias(alias) => alias.isCapSet() // TODO: test cases involving type aliases
184+
case _ => false
185+
}
186+
case tp: SingletonType => tp.underlying.isCapSet()
187+
case CapturingType(parent, _) => parent.isCapSet(true)
188+
case _ => false
189+
190+
/**
191+
* The capture set of a capture variable. Assumes that tp.isCapSet() is true.
192+
*/
193+
final def captureSetOfCapSet(using Context): CaptureSet = tp match
194+
case CapturingType(_,c) => c
195+
case tp: TypeRef if tp.symbol eq defn.Caps_CapSet => CaptureSet.empty
196+
case tp: SingletonType => tp.underlying.captureSetOfCapSet
197+
case tp: CaptureRef => CaptureSet(tp)
198+
173199
/** Is this type a CaptureRef that can be tracked?
174200
* This is true for
175201
* - all ThisTypes and all TermParamRef,

Diff for: compiler/src/dotty/tools/dotc/cc/CaptureSet.scala

+3
Original file line numberDiff line numberDiff line change
@@ -1073,6 +1073,9 @@ object CaptureSet:
10731073
case ReachCapability(ref1) =>
10741074
ref1.widen.deepCaptureSet(includeTypevars = true)
10751075
.showing(i"Deep capture set of $ref: ${ref1.widen} = ${result}", capt)
1076+
case tp : TypeRef if tp.isCapSet() => tp.underlying match
1077+
case TypeBounds(lo, hi) if hi.isCapSet() => hi.captureSetOfCapSet
1078+
case _ => ofType(ref.underlying, followResult = true)
10761079
case _ =>
10771080
if ref.isMaxCapability then ref.singletonCaptureSet
10781081
else ofType(ref.underlying, followResult = true)

Diff for: compiler/src/dotty/tools/dotc/core/TypeComparer.scala

+5-31
Original file line numberDiff line numberDiff line change
@@ -298,34 +298,6 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
298298
}
299299
}
300300

301-
//TODO move the following functions somewhere more appropriate
302-
/**
303-
* Is the type `tp` a `CapSet` type, i.e., a capture variable?
304-
*
305-
* @param tp The type to check
306-
* @param includeCapSet Whether to include the bare `CapSet` type itself in the check, false at the top level
307-
*/
308-
def isCapSet(tp: Type, includeCapSet: Boolean = false): Boolean = tp match {
309-
case tp: TypeRef => (includeCapSet && (tp.symbol eq defn.Caps_CapSet)) || {
310-
tp.underlying match
311-
case TypeBounds(lo, hi) => isCapSet(lo, true) && isCapSet(hi, true)
312-
case TypeAlias(alias) => isCapSet(alias) // TODO: test cases involving type aliases
313-
case _ => false
314-
}
315-
case tp: SingletonType => isCapSet(tp.underlying)
316-
case CapturingType(parent, _) => isCapSet(parent, true)
317-
case _ => false
318-
}
319-
320-
/**
321-
* Assumes that isCapSet(tp) is true.
322-
*/
323-
def captureSet(tp: Type): CaptureSet = tp match
324-
case CapturingType(_,c) => c
325-
case tp: TypeRef if tp.symbol eq defn.Caps_CapSet => CaptureSet.empty
326-
case tp: SingletonType => captureSet(tp.underlying)
327-
case tp: CaptureRef => CaptureSet(tp)
328-
329301
/** In capture checking, implements the logic to compare type variables which represent
330302
* capture variables.
331303
*
@@ -335,8 +307,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
335307
* 0 if both tp1 and tp2 are capture variables but tp1 is not a subcapture of tp2.
336308
*/
337309
inline def tryHandleCaptureVars: Int =
338-
if !(isCaptureCheckingOrSetup && isCapSet(tp1) && isCapSet(tp2)) then -1
339-
else if (captureSet(tp1).subCaptures(captureSet(tp2), frozenConstraint).isOK) then 1
310+
if !(isCaptureCheckingOrSetup && tp1.isCapSet() && tp2.isCapSet()) then -1
311+
else if (subCaptures(tp1.captureSetOfCapSet, tp2.captureSetOfCapSet, frozenConstraint).isOK) then 1
340312
else 0
341313

342314
def firstTry: Boolean = tp2 match {
@@ -389,7 +361,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
389361
&& !(sym1.isClass && sym2.isClass) // class types don't subtype each other
390362
|| {val cv = tryHandleCaptureVars
391363
if (cv < 0) then thirdTryNamed(tp2)
392-
else cv > 0 }
364+
else cv != 0 }
393365
case _ =>
394366
secondTry
395367
end compareNamed
@@ -477,6 +449,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
477449

478450
def secondTry: Boolean = tp1 match {
479451
case tp1: NamedType =>
452+
val cv = tryHandleCaptureVars
453+
if (cv >= 0) then return cv != 0
480454
tp1.info match {
481455
case info1: TypeAlias =>
482456
if (recur(info1.alias, tp2)) return true

Diff for: tests/neg-custom-args/captures/capture-vars-subtyping.scala

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ def testTrans[C^, D >: CapSet <: C, E >: CapSet <: D, F >: C <: CapSet^] =
2828
val c4: C = f1 // error
2929
val e4: E = f1 // error
3030
val e5: E = d1 // error
31+
val c4: CapSet^{C^} = e1
3132

3233

3334
trait A[+T]

0 commit comments

Comments
 (0)