-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Fix 25830 Inference of Capture-Polymorphic Lambdas #25853
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
Closed
Closed
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| import language.experimental.captureChecking | ||
| import caps.* | ||
|
|
||
| class File extends SharedCapability | ||
|
|
||
| // Bounded capture polymorphism: retains inside type-parameter bounds must | ||
| // survive PostTyper's CleanupRetains. The `inBound` flag in CleanupRetains | ||
| // preserves any CapSet-derived ref found inside a TypeBounds, regardless | ||
| // of whether its binder/owner is a named method. See i25830. | ||
|
|
||
| // (A) Flat bounded: `[C^, D^ <: {C}]` in a single poly lambda literal. | ||
| // `{C}` in the bound of `D` is a TypeParamRef to the lambda's own | ||
| // PolyType — the binders stack already handles it. | ||
| def testFlat() = | ||
| val f = { [C^, D^ <: {C}] => (xs: List[File^{D}]) => xs } | ||
| val a = File() | ||
| val _ : List[File^{a}] = f[{a}, {a}](List[File^{a}](a)) | ||
|
|
||
| // (B) Outer `def` + inner poly lambda whose bound references the outer | ||
| // def's capset param. `{C}` in the bound is a ref to a *named* | ||
| // method's type param. Without the `inBound` flag, the scope check | ||
| // would erase `{C}` (named methods are normally excluded). With it, | ||
| // bound retains are preserved unconditionally. | ||
| def testCurried[C^]: (xs: List[File^{C}]) => List[File^{C}] = | ||
| val f = { [D^ <: {C}] => (xs: List[File^{D}]) => xs } | ||
| f[{C}] | ||
|
|
||
| def useCurried() = | ||
| val a = File() | ||
| val _ : List[File^{a}] = testCurried[{a}](List[File^{a}](a)) | ||
|
|
||
| // (C) Bound references an outer-class capset param. | ||
| class Holder[OuterC^]: | ||
| val mk = { [D^ <: {OuterC}] => (xs: List[File^{D}]) => xs } | ||
|
|
||
| def useHolder() = | ||
| val a = File() | ||
| val h = new Holder[{a}] | ||
| val _ : List[File^{a}] = h.mk[{a}](List[File^{a}](a)) |
17 changes: 17 additions & 0 deletions
17
tests/pos-custom-args/captures/i25830-capset-members.scala
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| import language.experimental.captureChecking | ||
| import caps.* | ||
|
|
||
| class File extends SharedCapability | ||
|
|
||
| // Capture-set type member of a class appears in the inferred tpt of a | ||
| // polymorphic lambda inside the class. `{C}` here is a TypeRef to the | ||
| // class's type member — it must be preserved through PostTyper's | ||
| // CleanupRetains via the owner-ancestry branch. See i25830. | ||
| class Box[X^](val x0: File^{X}): | ||
| type C^ = X | ||
| val mk = { [D^] => (xs: List[File^{C}]) => xs.head } | ||
|
|
||
| def useBox() = | ||
| val a = File(); val b = File() | ||
| val bx = new Box[{a}](a) | ||
| val _ : File^{a} = bx.mk[{b}](List[File^{a}](a)) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| import language.experimental.captureChecking | ||
| import caps.* | ||
|
|
||
| class File extends SharedCapability | ||
|
|
||
| // Every local poly lambda below mentions at least one *outer* capture-set | ||
| // parameter in its own signature, and varies the lambda's own binder shape — | ||
| // multiple cap-sets, interleaved with plain types, nested poly, etc. | ||
| // Shapes where the local lambda does not reference anything from the outer | ||
| // scope are covered by tests/pos-custom-args/captures/i25830.scala. | ||
|
|
||
| // (1) val-form of the `def g[C^] = (xs) => xs.head` TODO line from | ||
| // tests/neg-custom-args/captures/use-capset.scala. The def form still | ||
| // errors (separate @use/classifier issue); the val form works because | ||
| // the lambda binds its own C. | ||
| def useCapsetVal() = | ||
| val g = { [C^] => (xs: List[File^{C}]) => xs.head } | ||
| val io = File() | ||
| val _ : File^{io} = g[{io}](List[File^{io}](io)) | ||
|
|
||
| // (2) Baseline: simple local `[C^]` inside a def with `[OuterC^]`, the | ||
| // lambda's signature mentions both. | ||
| def baseline[OuterC^](a: File^{OuterC}) = | ||
| val mk = { [C^] => (x: File^{C}, y: File^{OuterC}) => x } | ||
| val b = File() | ||
| val _ : File^{b} = mk[{b}](b, a) | ||
|
|
||
| // (3) Fully interleaved local binders `[T, C^, U, D^]` around an outer cap-set. | ||
| def interleaved_local[OuterC^](a: File^{OuterC}) = | ||
| val mk = { [T, C^, U, D^] => | ||
| (t: T, x: File^{C}, u: U, y: File^{D}, z: File^{OuterC}) => (t, u, x) | ||
| } | ||
| val b = File(); val c = File() | ||
| val _ = mk[Int, {b}, String, {c}](1, b, "s", c, a) | ||
|
|
||
| // (4) Outer itself mixes plain + cap-set; local lambda does too. | ||
| def mixed_both_sides[T, OuterC^](t: T, a: File^{OuterC}) = | ||
| val mk = { [U, C^] => | ||
| (u: U, x: File^{C}, t2: T, y: File^{OuterC}) => (u, x, t2) | ||
| } | ||
| val b = File() | ||
| val _ = mk[Boolean, {b}](true, b, t, a) | ||
|
|
||
| // (5) Class field: lambda's interleaved signature mentions the class's | ||
| // cap-set parameter. | ||
| class Holder[OuterC^](val outer: File^{OuterC}): | ||
| val mk = { [T, C^, U] => | ||
| (t: T, x: File^{C}, u: U, y: File^{OuterC}) => x | ||
| } | ||
|
|
||
| def useHolder() = | ||
| val a = File(); val b = File() | ||
| val h = Holder[{a}](a) | ||
| val _ : File^{b} = h.mk[Int, {b}, String](1, b, "s", a) | ||
|
|
||
| // (6) Trait abstract member + subclass override, both mentioning the | ||
| // enclosing cap-set parameter. Exercises the explicit-tpt path. | ||
| trait Ops[OuterC^]: | ||
| val mk: [T, C^] -> (t: T, x: File^{C}, y: File^{OuterC}) -> File^{C} | ||
|
|
||
| class OpsImpl[X^](x0: File^{X}) extends Ops[X]: | ||
| val mk = { [T, C^] => (t: T, x: File^{C}, y: File^{X}) => x } | ||
|
|
||
| def useOps() = | ||
| val a = File(); val b = File() | ||
| val ops = OpsImpl[{a}](a) | ||
| val _ : File^{b} = ops.mk[Int, {b}](7, b, a) | ||
|
|
||
| // (7) Nested outer scopes (class + inner def) each contribute a cap-set; | ||
| // the local lambda mentions both. | ||
| class Outer[X^](val xr: File^{X}): | ||
| def inner[T, Y^](yr: File^{Y})(t0: T) = | ||
| val mk = { [U, C^] => | ||
| (u: U, x: File^{C}, a: File^{X}, b: File^{Y}, t: T) => x | ||
| } | ||
| val c = File() | ||
| val _ : File^{c} = mk[String, {c}]("s", c, xr, yr, t0) | ||
|
|
||
| // (8) Nested poly lambda: the body of the outer lambda is itself a poly | ||
| // lambda, with interleaved binders at each level, and the inner level | ||
| // mentions the enclosing method's OuterC. | ||
| def nested_poly_interleaved[OuterC^](a: File^{OuterC}) = | ||
| val mk = { [T, C^] => (t: T, x: File^{C}) => | ||
| { [U, D^] => (u: U, y: File^{D}, z: File^{OuterC}) => (t, x) } } | ||
| val b = File(); val c = File() | ||
| val _ = mk[Int, {b}](1, b)[String, {c}]("s", c, a) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import language.experimental.captureChecking | ||
| import caps.* | ||
|
|
||
| class File extends SharedCapability | ||
|
|
||
| @main def test = | ||
| val convert = { [C^] => (xs: List[File^{C}]) => xs.map(_ => ()) } | ||
| val x = File() | ||
| val files: List[File^{x}] = List(x) | ||
| val result = convert[{x}](files) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
@odersky the
existsinstead offorallis for the case we have mixed retains of the form{C, x}.