|
| 1 | +# Capture Set Comparison |
| 2 | + |
| 3 | +## Capture reference definition |
| 4 | + |
| 5 | +All references in capture sets are `CaptureRef`s. |
| 6 | + |
| 7 | +Valid `CaptureRef`s can be categorized into two kinds: |
| 8 | + |
| 9 | +1. Value references, representing some singleton values in the set: |
| 10 | + |
| 11 | + * A singleton reference `SingletonCaptureRef`, e.g. `x`, `x.y`, `this` |
| 12 | + - Stable `TermRef` |
| 13 | + - `TermParamRef` |
| 14 | + - `ThisType` |
| 15 | + |
| 16 | + The info of a value reference can be: |
| 17 | + |
| 18 | + * `CapturingType(parent, refs)`, a type with capture set: `x: A^{a, b}`; |
| 19 | + |
| 20 | + * a value reference: `x: (y.type & A)`. In this case, `x` and `y` should be treated as the same reference during comparison. |
| 21 | + |
| 22 | + * A union `|` of two value references, e.g. `x | y | z.a` |
| 23 | + |
| 24 | + * An intersection `&` containing at least one value reference, e.g. `x & y & A` |
| 25 | + |
| 26 | + * `MaybeCapability(x)` or `ReachCapability(x)` where `x` is a value reference. |
| 27 | + |
| 28 | +2. Capture variable references, representing capture set polymorphism. |
| 29 | + All capture variable references are bounded by `CapSet`. |
| 30 | + A capture variable should not have any value instance, e.g. `val x: CapSet^{a}` |
| 31 | + |
| 32 | + * A type reference, e.g. `C` in `{x, C}` |
| 33 | + |
| 34 | + - `TypeRef` |
| 35 | + |
| 36 | + - `TypeParamRef` |
| 37 | + |
| 38 | + The info of a capture variable reference can be: |
| 39 | + |
| 40 | + * a `TypeBounds(lo, hi)` where `lo` and `hi` are both capture variable references. |
| 41 | + If the type is an alias, both `lo` and `hi` are the same type. |
| 42 | + |
| 43 | + * a `CapturingType(parent, refs)`, where `parent` is a capture variable reference. |
| 44 | + |
| 45 | + * A union or intersection of two capture variable references, e.g. `C | CapSet^{x, y}` |
| 46 | + |
| 47 | +## Comparison |
| 48 | + |
| 49 | +Capture set comparison starts at: |
| 50 | + |
| 51 | +```scala |
| 52 | +class CaptureSet: |
| 53 | + final def subCaptures(that: CaptureSet, frozen: Boolean)(using Context): CompareResult |
| 54 | +``` |
| 55 | + |
| 56 | +Later, `def subsumes(y: CaptureRef)` is called to compare two capture references: `{y} <:< {this}`. |
| 57 | + |
| 58 | +There are three cases when comparing two capture references: |
| 59 | + |
| 60 | +1. Both are value references: the comparison should be done in the `subsumes`. |
| 61 | + |
| 62 | +2. Both are capture variable references: the subtyping should be used to compare the two types. |
| 63 | + The existing subtyping algorithm is able to check all possible type relations. |
| 64 | + |
| 65 | +3. One is a value reference and the other is a capture variable reference. |
| 66 | + This is the most tricky case. |
| 67 | + The value reference should be converted to a capture variable with the reference in the capture set. |
| 68 | + |
| 69 | + For example, when comparing `{x} <:< {C}`, where `C` is defined as `cap C >: {x} <: C2`, |
| 70 | + the comparison becomes `CapSet^{x} <:< C`. |
| 71 | + By the subtyping algorithm, `CapSet^{x}` will be compared with the lower bound of `C`, which is `{x}`. |
| 72 | + Then the comparison is reduced to `{x} <:< {x}`, which is true. |
| 73 | + |
| 74 | +The tricky part is the info of `this`: for example, `x: IO^{io}` and `y: IO^{io}`, `{x} <:< {y}` should be false, as `x` and `y` can be two different instances both capturing `io`. |
| 75 | + |
| 76 | +## Detailed comparison rules |
| 77 | + |
| 78 | +### Subsuming `{y} <:< {x}` |
| 79 | + |
| 80 | +Union and intersection types are treated specially in the rules, |
| 81 | +as they are not allowed directly in the capture set, for example, `A^{(x.type | y.type), z}`. |
| 82 | +Therefore, a value reference must be a singleton type, `SingletonCaptureRef`. |
| 83 | +Note the `||` and `&&` in the cases, because we only consider **equivalent references**. |
| 84 | + |
| 85 | +Assuming both `x` and `y` are valid `CaptureRef`s, `x.subsumes(y)`: |
| 86 | + |
| 87 | +TODO: `subsumesExistentially` should be considered. |
| 88 | + |
| 89 | +1. If `x` and `y` are the same reference, `x eq y`. |
| 90 | +2. If `x` is a root capability. |
| 91 | +3. If `y` is a singleton type and not a root capability, |
| 92 | + 1. If `x` is also a singleton type, and `x =:= y`. |
| 93 | + 2. If `y` is a selection `ypre.f`, and `x.subsumes(ypre)`. |
| 94 | + 3. By the info of `y`, |
| 95 | + * If `y.info` is a singleton type, `x.subsumes(y.info)`. |
| 96 | + * If `y =:= y1 | y2` and `y1` and `y2` are both singleton types, `x.subsumes(y1) && x.subsumes(y2)`. |
| 97 | + * If `y =:= y1 & y2`, `x.subsumes(y1) || x.subsumes(y2)`. |
| 98 | + 4. If `x` is bounded by `CapSet` and `CapSet^{y} <:< x`. |
| 99 | +4. If `y` is a `MaybeCapability(y1)` and `x` is a `MaybeCapability(x1)`, `x1.subsumes(y1)`. |
| 100 | +5. If `x` is a `ReachCapability(x1)` and `y` is a `ReachCapability(y1)`, `x1.subsumes(y1)`. |
| 101 | +6. If `x` is a singleton type, |
| 102 | + 1. By the info of `x`, |
| 103 | + * If `x.info` is a singleton type, `x.info.subsumes(y)`. |
| 104 | + * If `x =:= x1 | x2`, `x1.subsumes(y) && x2.subsumes(y)`. |
| 105 | + * If `x =:= x1 & x2`, `x1.subsumes(y) || x2.subsumes(y)`. |
| 106 | + 2. If `y` is bounded by `CapSet` and `y <:< CapSet^{x}`. |
| 107 | +7. If `x` and `y` are both bounded by `CapSet`, `y <:< x`. |
| 108 | +8. If `x` is bounded by `CapSet` and `y` is assumed to be contained in `x`. |
| 109 | + |
| 110 | + |
| 111 | +### Subtyping `T1 <:< T2` |
| 112 | + |
| 113 | +`T1 <:< T2`: |
| 114 | + |
| 115 | +TODO: we are still not sure about the rules for union and intersection types. |
| 116 | +For example, should `C2^{x}` be a subtype of `C1^{x} | C2^{y}` given `x` and `y` are different references? |
0 commit comments