-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Canonicalize capture variable subtype comparisons #22299
Conversation
Moved to dotty @odersky |
2770688
to
bb1b215
Compare
Notes on the extension-method problem: The example in cc-poly-varargs.scala seems to loop infinitely on However, a simpler version of it does not diverge, but reveals another problem (not clear if it is related): import language.experimental.captureChecking
abstract class Source[+T, Cap^]
extension [T, Cap^](src: Source[T, Cap]^)
def transformValuesWith[U](f: (T -> U)^{Cap^}): Source[U, Cap]^{src, f} = ???
def simpler[T, Cap^](src: Source[T, Cap]^{Cap^}): Source[T, Cap]^{Cap^} = // <- need to annotate the explicit return type, otherwise error
src.transformValuesWith(identity) If we leave out the return type of -- [E007] Type Mismatch Error: local/cc-poly-varargs.scala:12:74 ---------------
12 |def simpler[T, Cap^](src: Source[T, Cap]^{Cap^}) = src.transformValuesWith(identity)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| Found: Source[box T^?, caps.CapSet^{Cap^}]^{src}
| Required: Source[T, caps.CapSet]
|
| Note that the expected type Source[T, caps.CapSet]
| is the previously inferred result type of method simpler
| which is also the type seen in separately compiled sources.
| The new inferred type Source[box T^?, caps.CapSet^{Cap^}]^{src}
| must conform to this type.
|
| longer explanation available when compiling with `-explain` Edit: It looks like the initial problem that we had with Edit2: This seems to be the expected behavior, we need to annotate the return type if it's impure. |
A simpler example that exhibits the divergence: import language.experimental.captureChecking
abstract class Source[+T, Cap^]
def id[T, Cap^](src: Source[T, Cap]^): Source[T, Cap]^{src} = src
def promote[T, Cap^](src: Source[T, Cap]^{Cap^}): Source[T, Cap]^{Cap^} = src
def fun[T, Cap^](src: Source[T, Cap]^{Cap^}): Source[T, Cap]^{Cap^} =
val res = id(src)
promote(res) Applying ...
def fun[T, Cap >: caps.CapSet <: caps.CapSet^](src: Source[T, Cap]^{Cap^}):
Source[T, Cap]^{Cap^} =
{
val res: Source[box T^?, caps.CapSet^{Cap^, res}]^{src} =
id[box T^?, caps.CapSet^{Cap^, res}](src)
promote[box T^?, caps.CapSet^{res, Cap^}](res)
} We already have a cyclic reference at But even with an explicit ascription
the last line will still carry def fun[T, Cap >: caps.CapSet <: caps.CapSet^](src: Source[T, Cap]^{Cap^}):
Source[T, Cap]^{Cap^} =
{
val res: Source[T, Cap]^{src} = id[box T^?, caps.CapSet^{Cap^}](src)
promote[box T^?, caps.CapSet^{res, Cap^}](res)
} |
Part of the problem seems to stem from subsumes: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice that the problem in AvoidMap got solved by the changes in subsumes. The changes make sense. I'd like to understand better why the AvoidMap problem poppsed up without these changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also maybe squash the commits, since they tried several things that got reverted afterwards?
Fixes scala#22103 Subtype problems where at least one side is a type variable representing a capture variable are canonicalized to capturing type comparisons on the special `CapSet` for the universe capture sets. For example, `C <: CapSet^{C^}` becomes `CapSet^{C^} <: CapSet^{C^}`, and `A <: B` becomes `CapSet^{A^} <: CapSet^{B^}` if both `A` and `B` are capture variables. Needs a patch in subsumes Without the patch we get divergence in AvoidMap for cc-poly-varargs. The underlying cause for the divergence is that we could not conclude that the capture variable `Cap^` subsumes `left` through `src1`, i.e., `left` reaches `Cap^`. That caused `left` to appear circularly in its own capture set and subsequently avoidance to loop forever.
cef5116
to
a587261
Compare
I squashed all the commits into one. Will push some polishing changes on top. |
Make tests in TypeComparer fit better with the rest
@@ -119,6 +118,15 @@ trait CaptureRef extends TypeProxy, ValueType: | |||
case info: SingletonCaptureRef => test(info) | |||
case info: AndType => viaInfo(info.tp1)(test) || viaInfo(info.tp2)(test) | |||
case info: OrType => viaInfo(info.tp1)(test) && viaInfo(info.tp2)(test) | |||
case info @ CapturingType(_,_) if this.derivesFrom(defn.Caps_CapSet) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this condition check this
instead of info
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The case here does not look currect for me.viaInfo
is used only if there is some equivalent relation from singleton types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's tailored to the case that we want to check that a term variable reaches a capture variable (resp. the capture variable subsumes the term variable, so we check the this
), because of the issue outlined in
#22299 (comment)
Not sure if this is best place to do it, but logically we have to chase along the infos of the context entries. The case y: TermRef if !y.isRootCapability =>
part will invoke this function as a last resort, and since we have to follow the info then, we might as well do it here.
Feel free to open a new PR if you see a better way to do it.
val a: C = ??? | ||
val b: CapSet^{C^} = a | ||
val c: C = b | ||
val d: CapSet^{C^, c} = a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In theory, we should never have an instance of CapSet
. I don't think we need to test CapSet^{C^, c}
, a singleton c
with type CapSet
inside a capture set. Because this is confusing to handle whether a CaptureRef
is representing a value or its underlying capture set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The point of this is to test subtyping relations between capture sets. The term bindings are a crutch. Back then I could not do it in the "obvious" way using e.g. summon[C <:< CapSet^{C^}]
, because resolution happens before capture checking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the assignments are fine to test. I mean the c
inside the capture set.
Fixes #22103
Subtype problems where at least one side is a type variable representing a capture variable are canonicalized to capturing type comparisons on the special
CapSet
for the universe capture sets. For example,C <: CapSet^{C^}
becomesCapSet^{C^} <: CapSet^{C^}
, andA <: B
becomesCapSet^{A^} <: CapSet^{B^}
if bothA
andB
are capture variables.Supersedes #22183 and #22289. This solution is overall cleaner and does not require adding a new bit to the TypeComparer's ApproxState.
TODOs/Issues/Questions:
D <: E
fails, but its canonicalized formCapSet^{D^} <: CapSet^{E^}
now succeeds. Potential problem in the subcapturing implementation.Extend to intersection/unionsLacking good uses cases, not planned right now.def f[C^, D^, E <: C | D, F <: C & D](...) = ...
etc.If we haveWill be addressed by a new scheme for declaring capture variables using context bounds.C^
declared in the current context, should there be a difference betweenC
vs.C^
for subsequent mentions? We currently do, but seems a bit too subtle for users.