Skip to content

document Closure capture precision#698

Open
tshepang wants to merge 32 commits into
mainfrom
tshepang/capture-precision
Open

document Closure capture precision#698
tshepang wants to merge 32 commits into
mainfrom
tshepang/capture-precision

Conversation

@tshepang

@tshepang tshepang commented Apr 20, 2026

Copy link
Copy Markdown
Member

This captures some gaps FLS has compared to Reference, and the content is adjusted from Closure capture precision section.

Comment thread src/types-and-traits.rst Outdated
@kirtchev-adacore kirtchev-adacore self-requested a review April 21, 2026 07:11

@kirtchev-adacore kirtchev-adacore left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is it for now. I will have to revisit the remaining rules once the "place" business has been cleared up. I think we can find better wording/grouping of the remaining rules, as they feel somewhat randomly sprinkled.

View changes since this review

Comment thread src/types-and-traits.rst Outdated
Comment thread src/types-and-traits.rst Outdated
Comment thread src/types-and-traits.rst Outdated
Comment thread src/types-and-traits.rst Outdated
Comment thread src/types-and-traits.rst Outdated
Comment thread src/types-and-traits.rst Outdated
Comment thread src/types-and-traits.rst Outdated
Comment thread src/types-and-traits.rst Outdated
@tshepang tshepang force-pushed the tshepang/capture-precision branch from e3ed8e9 to 7be2ad5 Compare April 21, 2026 09:49
@tshepang

tshepang commented Apr 21, 2026

Copy link
Copy Markdown
Member Author

all addressed but #698 (comment)

@kirtchev-adacore kirtchev-adacore left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will probably need to read Capture precision to ensure that no rules have been omitted.

View changes since this review

Comment thread src/expressions.rst Outdated
Comment thread src/expressions.rst Outdated
A :t:`closure expression` :t:`[borrow]s` or :t:`moves <by move>` the :t:`capture path`, which may be truncated based on these rules:

- :dp:`fls_4TESOxGpEY2h`
When a :t:`capture path` and an ancestor :t:`capture path` are both :t:`captured <capturing>`, the ancestor :t:`capture path` is captured with the highest :t:`capture mode` among the two :t:`[capture path]s`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
When a :t:`capture path` and an ancestor :t:`capture path` are both :t:`captured <capturing>`, the ancestor :t:`capture path` is captured with the highest :t:`capture mode` among the two :t:`[capture path]s`.
- When a :t:`capture path` and an ancestor :t:`capture path` are both :t:`captured <capturing>`, the ancestor :t:`capture path` is :t:`captured` with the highest :t:`capture mode` among the two :t:`[capture path]s`.

This and the following rules should be list items.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The shared-prefix rule also seems to need the Reference's recursive-propagation note.

A deeper descendant can strengthen an intermediate ancestor, and that stronger mode may then need to propagate again to a more distant ancestor.

Could a short note be added here saying this rule may need to be applied recursively?

Support:

  • Reference type.closure.capture.precision.shared-prefix

@tshepang tshepang Apr 23, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was thinking an ancestor should cover that, since ancestor can mean parent, grand parent, great grand parent, and so on... unless you don't think that is strong enough

@tshepang tshepang Apr 23, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and the following rules should be list items.

it already is... see the line prior

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am OK with Tshepang's latest change. Pete however should also chime in with respect to his comment.

@kirtchev-adacore kirtchev-adacore May 11, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about something along the lines of

When a :t:capture path and an ancestor :t:capture path are both :t:captured <capturing>, the ancestor :t:capture path is :t:captured with the highest :t:capture mode among the two :t:[capture path]s, recursively, for all such pairs of :t:[capture path]s.

The Reference's "recursive" note is not 100% clear because one also has to take into account pairs of paths.

This comment is still relevant.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/expressions.rst
Comment thread src/expressions.rst Outdated
Comment on lines +5192 to +5235
:dp:`fls_v8IFXHJnXhez`
A :t:`place` is not captured when an :t:`underscore expression` is used to bind it.

:dp:`fls_gujpU7p5n9Zx`
A :t:`place` is not captured by destructuring tuples, structs, and single-variant enums.

:dp:`fls_t8tFLUg8O83Q`
A :t:`place` is not captured by being matched against a :t:`rest pattern`.

:dp:`fls_RaONmCLH2KGM`
The entire :t:`slice` or :t:`array` is always captured even if used with :t:`underscore expression`, :t:`indexing <index expression>`, or :t:`slicing <slice>`.

:dp:`fls_Vt9C9mKxHOwo`
A :t:`place` is captured by :t:`immutable borrow` if its :t:`discriminant` is read by :t:`pattern matching`.

:dp:`fls_Fs12dmznjsMf`
Matching against a variant of an enum that has more than one variant captures the :t:`place` by :t:`immutable borrow`.

:dp:`fls_7EXHdE2eOVek`
Matching against a variant of an enum that has one variant does not capture the place, unless it is marked with :t:`attribute` ``non_exhaustive``, in which case the place is captured by :t:`immutable borrow`.

:dp:`fls_iLH8X2U4ADHb`
Matching against a :t:`range pattern` captures the place by :t:`immutable borrow`.

:dp:`fls_HMJUXHrvOmPl`
Matching a :t:`slice` against a slice :t:`pattern`, other than one with only a single rest pattern ``[..]``, captures the slice by :t:`immutable borrow`.

:dp:`fls_Gj1znNpthHY6`
Matching an array against a slice pattern does not capture the :t:`place`.

:dp:`fls_IFyJvb6mlFU4`
Move closures can only capture the prefix of a :t:`capture path` that runs up to, but not including, the first :t:`dereference` of a :t:`reference`.

:dp:`fls_7NEEJgKSpQQ8`
Closures will only capture the prefix of a :t:`capture path` that runs up to, but not including, the first :t:`dereference` of a :t:`raw pointer`.

:dp:`fls_kYFd3p06pWWV`
Closures will only capture the prefix of a :t:`capture path` of a :t:`union` that runs up to the union itself.

:dp:`fls_fATMTNUOHsfb`
Closures will only capture the prefix of the :t:`capture path` that runs up to, but not including, the first :t:`field access expression` into a structure that uses the :t:`attribute` ``packed`` representation, in unaligned :t:`[field]s` in a struct.

:dp:`fls_fITor3jpmgrl`
Taking the address of an unaligned :t:`field` captures the entire struct.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made significant reorderings and rewording of some of the paragraphs.

Suggested change
:dp:`fls_v8IFXHJnXhez`
A :t:`place` is not captured when an :t:`underscore expression` is used to bind it.
:dp:`fls_gujpU7p5n9Zx`
A :t:`place` is not captured by destructuring tuples, structs, and single-variant enums.
:dp:`fls_t8tFLUg8O83Q`
A :t:`place` is not captured by being matched against a :t:`rest pattern`.
:dp:`fls_RaONmCLH2KGM`
The entire :t:`slice` or :t:`array` is always captured even if used with :t:`underscore expression`, :t:`indexing <index expression>`, or :t:`slicing <slice>`.
:dp:`fls_Vt9C9mKxHOwo`
A :t:`place` is captured by :t:`immutable borrow` if its :t:`discriminant` is read by :t:`pattern matching`.
:dp:`fls_Fs12dmznjsMf`
Matching against a variant of an enum that has more than one variant captures the :t:`place` by :t:`immutable borrow`.
:dp:`fls_7EXHdE2eOVek`
Matching against a variant of an enum that has one variant does not capture the place, unless it is marked with :t:`attribute` ``non_exhaustive``, in which case the place is captured by :t:`immutable borrow`.
:dp:`fls_iLH8X2U4ADHb`
Matching against a :t:`range pattern` captures the place by :t:`immutable borrow`.
:dp:`fls_HMJUXHrvOmPl`
Matching a :t:`slice` against a slice :t:`pattern`, other than one with only a single rest pattern ``[..]``, captures the slice by :t:`immutable borrow`.
:dp:`fls_Gj1znNpthHY6`
Matching an array against a slice pattern does not capture the :t:`place`.
:dp:`fls_IFyJvb6mlFU4`
Move closures can only capture the prefix of a :t:`capture path` that runs up to, but not including, the first :t:`dereference` of a :t:`reference`.
:dp:`fls_7NEEJgKSpQQ8`
Closures will only capture the prefix of a :t:`capture path` that runs up to, but not including, the first :t:`dereference` of a :t:`raw pointer`.
:dp:`fls_kYFd3p06pWWV`
Closures will only capture the prefix of a :t:`capture path` of a :t:`union` that runs up to the union itself.
:dp:`fls_fATMTNUOHsfb`
Closures will only capture the prefix of the :t:`capture path` that runs up to, but not including, the first :t:`field access expression` into a structure that uses the :t:`attribute` ``packed`` representation, in unaligned :t:`[field]s` in a struct.
:dp:`fls_fITor3jpmgrl`
Taking the address of an unaligned :t:`field` captures the entire struct.
:dp:`fls_Vt9C9mKxHOwo`
A :t:`place` is :t:`captured` by :t:`immutable borrow` if its :t:`discriminant` is read by :t:`pattern matching`.
:dp:`fls_v8IFXHJnXhez`
A :t:`place` is not :t:`captured` when an :t:`underscore expression` is used to bind it.
:dp:`fls_gujpU7p5n9Zx`
A :t:`place` is not :t:`captured` by destructuring :t:`[struct]s`, :t:`[tuple]s`, and :t:`[enum]s` with a single :t:`enum variant`.
:dp:`fls_t8tFLUg8O83Q`
A :t:`place` is not :t:`captured` by being matched against a :t:`rest pattern`.
:dp:`fls_RaONmCLH2KGM`
An :t:`array` or :t:`slice` is :t:`captured` whole.
:dp:`fls_Fs12dmznjsMf`
Matching against an :t:`enum variant` of an :t:`enum` with more than one :t:`[enum variant]s` :t:`captures` the :t:`place` by :t:`immutable borrow`.
:dp:`fls_7EXHdE2eOVek`
Matching against an :t:`enum variant` of an :t:`enum` with one :t:`enum variant` does not :t:`capture` the :t:`place`, unless it is subject to :t:`attribute` ``non_exhaustive``, in which case the :t:`place` is captured by :t:`immutable borrow`.
:dp:`fls_iLH8X2U4ADHb`
Matching against a :t:`range pattern` :t:`captures` the :t:`place` by :t:`immutable borrow`.
:dp:`fls_HMJUXHrvOmPl`
Matching a :t:`slice` against a :t:`slice pattern`, other than one with only a single :t:`rest pattern`, :t:`captures` the :t:`slice` by :t:`immutable borrow`.
:dp:`fls_Gj1znNpthHY6`
Matching an :t:`array` against a :t:`slice pattern` does not :t:`capture` the :t:`place`.
:dp:`fls_IFyJvb6mlFU4`
A :t:`closure expression` subject to keyword ``move`` :t:`captures` the prefix of a :t:`capture path` that runs up to, but not including, the first :t:`dereference` of a :t:`reference`.
:dp:`fls_7NEEJgKSpQQ8`
A :t:`closure expression` :t:`captures` the prefix of a :t:`capture path` that runs up to, but not including, the first :t:`dereference` of a :t:`raw pointer`.
:dp:`fls_kYFd3p06pWWV`
A :t:`closure expression` :t:`captures` the prefix of a :t:`capture path` of a :t:`union` that runs up to the :t:`union` itself.
:dp:`fls_fATMTNUOHsfb`
A :t:`closure expression` :t:`captures` the prefix of the :t:`capture path` that runs up to, but not including, the first :t:`field access expression` into a :t:`struct` that uses the :t:`attribute` :c:`repr` with modifier ``packed``, in unaligned :t:`[field]s` in a :t:`struct`.
:dp:`fls_fITor3jpmgrl`
Taking the address of an unaligned :t:`field` captures the entire struct.

@PLeVasseur PLeVasseur Apr 22, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Several rules in this block still seem too strong relative to the Reference.

  • _ here should use the FLS term underscore pattern, not underscore expression, and _ does not introduce a binding.
  • The destructuring / .. / array-vs-slice rules should say does not by itself read or capture. That qualifier matters because subpatterns can still capture subplaces; e.g. let (x0, ..) = x; still captures x.0.

Could this subsection be adjusted along those lines?

Support:

@PLeVasseur PLeVasseur Apr 22, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Separate point on the Matching a slice against a slice pattern... rule in this block: the current summary seems to compress away an important Reference detail.

Upstream treats this as a read of the slice length, but the captured place is the slice pointee rather than the wide-pointer place.

That distinction matters for cases like &mut [T] and &mut &'l [u8], where the effective capture is *x / **x, not x / *x.

Could that pointee-vs-wide-pointer behavior be made explicit here, or backed with a brief note/example mirroring the Reference?

Support:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Separate point on the repr(packed) rule in this block: this still seems narrower than the Reference.

Upstream truncates at the first field access into any repr(packed) struct, and explicitly says this includes fields that are currently aligned as well, for future-compatibility reasons.

Could this sentence be widened accordingly instead of limiting it to unaligned fields?

Support:

  • Reference type.closure.capture.precision.unaligned

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

array-vs-slice rules

what does this mean, a typo perhaps... the paragraph is about enums, structs, and tuples

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it fls_fATMTNUOHsfb that had redundancy removed, or which one is it

you maybe missed this question

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it fls_fATMTNUOHsfb that had redundancy removed, or which one is it

you maybe missed this question

I am confused, what redundancy are you referring to?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's implied that a read happens (iiuc), as that decides whether a capture happens... mentioning that a read happens is redundant

I have the vague memory that perhaps @PLeVasseur suggested that the rules should indicate when a read happens, but I can't find the comment...

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am confused, what redundancy are you referring to?

it's in this comment #698 (comment)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I can't find where the redundancy was, even after comparing the two suggestions. 🤷

@PLeVasseur PLeVasseur left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pulling this material into 6.22. The remaining comments are mainly about semantic completeness and alignment with the Rust Reference so the new section lands as dependable spec text.

View changes since this review

Comment thread src/expressions.rst Outdated
Comment thread src/changelog.rst
- `Const blocks are no longer evaluated to determine if expressions involving fallible operations can implicitly be constant-promoted <https://github.com/rust-lang/rust/pull/150557>`_
- `Make operational semantics of pattern matching independent of crate and module <https://github.com/rust-lang/rust/pull/150681>`_

Changed paragraphs:

@PLeVasseur PLeVasseur Apr 22, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The paragraph-ID list itself looks fine, but this changelog attribution still seems too broad.

The changed capture-mode paragraphs fit rust-lang/rust#150681, but the new capture-precision block spans multiple sources: some items align with the later pattern/discriminant work in rust-lang/rust#138961 and rust-lang/reference#1837, while others are pre-existing Reference behavior now being documented here.

Could this entry be split, or reworded so #150681 is not presented as introducing the whole block? The note that these paragraphs mostly document behavior that existed before this release already points in that direction.

Support:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can go find where each behavior was added, but am not sure it's worth the time

maybe a good compromise is having a different heading, like "Pre-existing behavior before 1.95", or just "FLS maintenance"

Comment thread src/expressions.rst Outdated
Comment thread src/expressions.rst Outdated
A :t:`closure expression` :t:`[borrow]s` or :t:`moves <by move>` the :t:`capture path`, which may be truncated based on these rules:

- :dp:`fls_4TESOxGpEY2h`
When a :t:`capture path` and an ancestor :t:`capture path` are both :t:`captured <capturing>`, the ancestor :t:`capture path` is captured with the highest :t:`capture mode` among the two :t:`[capture path]s`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The shared-prefix rule also seems to need the Reference's recursive-propagation note.

A deeper descendant can strengthen an intermediate ancestor, and that stronger mode may then need to propagate again to a more distant ancestor.

Could a short note be added here saying this rule may need to be applied recursively?

Support:

  • Reference type.closure.capture.precision.shared-prefix

Comment thread src/expressions.rst Outdated
Comment on lines +5192 to +5235
:dp:`fls_v8IFXHJnXhez`
A :t:`place` is not captured when an :t:`underscore expression` is used to bind it.

:dp:`fls_gujpU7p5n9Zx`
A :t:`place` is not captured by destructuring tuples, structs, and single-variant enums.

:dp:`fls_t8tFLUg8O83Q`
A :t:`place` is not captured by being matched against a :t:`rest pattern`.

:dp:`fls_RaONmCLH2KGM`
The entire :t:`slice` or :t:`array` is always captured even if used with :t:`underscore expression`, :t:`indexing <index expression>`, or :t:`slicing <slice>`.

:dp:`fls_Vt9C9mKxHOwo`
A :t:`place` is captured by :t:`immutable borrow` if its :t:`discriminant` is read by :t:`pattern matching`.

:dp:`fls_Fs12dmznjsMf`
Matching against a variant of an enum that has more than one variant captures the :t:`place` by :t:`immutable borrow`.

:dp:`fls_7EXHdE2eOVek`
Matching against a variant of an enum that has one variant does not capture the place, unless it is marked with :t:`attribute` ``non_exhaustive``, in which case the place is captured by :t:`immutable borrow`.

:dp:`fls_iLH8X2U4ADHb`
Matching against a :t:`range pattern` captures the place by :t:`immutable borrow`.

:dp:`fls_HMJUXHrvOmPl`
Matching a :t:`slice` against a slice :t:`pattern`, other than one with only a single rest pattern ``[..]``, captures the slice by :t:`immutable borrow`.

:dp:`fls_Gj1znNpthHY6`
Matching an array against a slice pattern does not capture the :t:`place`.

:dp:`fls_IFyJvb6mlFU4`
Move closures can only capture the prefix of a :t:`capture path` that runs up to, but not including, the first :t:`dereference` of a :t:`reference`.

:dp:`fls_7NEEJgKSpQQ8`
Closures will only capture the prefix of a :t:`capture path` that runs up to, but not including, the first :t:`dereference` of a :t:`raw pointer`.

:dp:`fls_kYFd3p06pWWV`
Closures will only capture the prefix of a :t:`capture path` of a :t:`union` that runs up to the union itself.

:dp:`fls_fATMTNUOHsfb`
Closures will only capture the prefix of the :t:`capture path` that runs up to, but not including, the first :t:`field access expression` into a structure that uses the :t:`attribute` ``packed`` representation, in unaligned :t:`[field]s` in a struct.

:dp:`fls_fITor3jpmgrl`
Taking the address of an unaligned :t:`field` captures the entire struct.

@PLeVasseur PLeVasseur Apr 22, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Several rules in this block still seem too strong relative to the Reference.

  • _ here should use the FLS term underscore pattern, not underscore expression, and _ does not introduce a binding.
  • The destructuring / .. / array-vs-slice rules should say does not by itself read or capture. That qualifier matters because subpatterns can still capture subplaces; e.g. let (x0, ..) = x; still captures x.0.

Could this subsection be adjusted along those lines?

Support:

Comment thread src/expressions.rst Outdated
Comment on lines +5192 to +5235
:dp:`fls_v8IFXHJnXhez`
A :t:`place` is not captured when an :t:`underscore expression` is used to bind it.

:dp:`fls_gujpU7p5n9Zx`
A :t:`place` is not captured by destructuring tuples, structs, and single-variant enums.

:dp:`fls_t8tFLUg8O83Q`
A :t:`place` is not captured by being matched against a :t:`rest pattern`.

:dp:`fls_RaONmCLH2KGM`
The entire :t:`slice` or :t:`array` is always captured even if used with :t:`underscore expression`, :t:`indexing <index expression>`, or :t:`slicing <slice>`.

:dp:`fls_Vt9C9mKxHOwo`
A :t:`place` is captured by :t:`immutable borrow` if its :t:`discriminant` is read by :t:`pattern matching`.

:dp:`fls_Fs12dmznjsMf`
Matching against a variant of an enum that has more than one variant captures the :t:`place` by :t:`immutable borrow`.

:dp:`fls_7EXHdE2eOVek`
Matching against a variant of an enum that has one variant does not capture the place, unless it is marked with :t:`attribute` ``non_exhaustive``, in which case the place is captured by :t:`immutable borrow`.

:dp:`fls_iLH8X2U4ADHb`
Matching against a :t:`range pattern` captures the place by :t:`immutable borrow`.

:dp:`fls_HMJUXHrvOmPl`
Matching a :t:`slice` against a slice :t:`pattern`, other than one with only a single rest pattern ``[..]``, captures the slice by :t:`immutable borrow`.

:dp:`fls_Gj1znNpthHY6`
Matching an array against a slice pattern does not capture the :t:`place`.

:dp:`fls_IFyJvb6mlFU4`
Move closures can only capture the prefix of a :t:`capture path` that runs up to, but not including, the first :t:`dereference` of a :t:`reference`.

:dp:`fls_7NEEJgKSpQQ8`
Closures will only capture the prefix of a :t:`capture path` that runs up to, but not including, the first :t:`dereference` of a :t:`raw pointer`.

:dp:`fls_kYFd3p06pWWV`
Closures will only capture the prefix of a :t:`capture path` of a :t:`union` that runs up to the union itself.

:dp:`fls_fATMTNUOHsfb`
Closures will only capture the prefix of the :t:`capture path` that runs up to, but not including, the first :t:`field access expression` into a structure that uses the :t:`attribute` ``packed`` representation, in unaligned :t:`[field]s` in a struct.

:dp:`fls_fITor3jpmgrl`
Taking the address of an unaligned :t:`field` captures the entire struct.

@PLeVasseur PLeVasseur Apr 22, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Separate point on the Matching a slice against a slice pattern... rule in this block: the current summary seems to compress away an important Reference detail.

Upstream treats this as a read of the slice length, but the captured place is the slice pointee rather than the wide-pointer place.

That distinction matters for cases like &mut [T] and &mut &'l [u8], where the effective capture is *x / **x, not x / *x.

Could that pointee-vs-wide-pointer behavior be made explicit here, or backed with a brief note/example mirroring the Reference?

Support:

Comment thread src/expressions.rst Outdated
Comment on lines +5192 to +5235
:dp:`fls_v8IFXHJnXhez`
A :t:`place` is not captured when an :t:`underscore expression` is used to bind it.

:dp:`fls_gujpU7p5n9Zx`
A :t:`place` is not captured by destructuring tuples, structs, and single-variant enums.

:dp:`fls_t8tFLUg8O83Q`
A :t:`place` is not captured by being matched against a :t:`rest pattern`.

:dp:`fls_RaONmCLH2KGM`
The entire :t:`slice` or :t:`array` is always captured even if used with :t:`underscore expression`, :t:`indexing <index expression>`, or :t:`slicing <slice>`.

:dp:`fls_Vt9C9mKxHOwo`
A :t:`place` is captured by :t:`immutable borrow` if its :t:`discriminant` is read by :t:`pattern matching`.

:dp:`fls_Fs12dmznjsMf`
Matching against a variant of an enum that has more than one variant captures the :t:`place` by :t:`immutable borrow`.

:dp:`fls_7EXHdE2eOVek`
Matching against a variant of an enum that has one variant does not capture the place, unless it is marked with :t:`attribute` ``non_exhaustive``, in which case the place is captured by :t:`immutable borrow`.

:dp:`fls_iLH8X2U4ADHb`
Matching against a :t:`range pattern` captures the place by :t:`immutable borrow`.

:dp:`fls_HMJUXHrvOmPl`
Matching a :t:`slice` against a slice :t:`pattern`, other than one with only a single rest pattern ``[..]``, captures the slice by :t:`immutable borrow`.

:dp:`fls_Gj1znNpthHY6`
Matching an array against a slice pattern does not capture the :t:`place`.

:dp:`fls_IFyJvb6mlFU4`
Move closures can only capture the prefix of a :t:`capture path` that runs up to, but not including, the first :t:`dereference` of a :t:`reference`.

:dp:`fls_7NEEJgKSpQQ8`
Closures will only capture the prefix of a :t:`capture path` that runs up to, but not including, the first :t:`dereference` of a :t:`raw pointer`.

:dp:`fls_kYFd3p06pWWV`
Closures will only capture the prefix of a :t:`capture path` of a :t:`union` that runs up to the union itself.

:dp:`fls_fATMTNUOHsfb`
Closures will only capture the prefix of the :t:`capture path` that runs up to, but not including, the first :t:`field access expression` into a structure that uses the :t:`attribute` ``packed`` representation, in unaligned :t:`[field]s` in a struct.

:dp:`fls_fITor3jpmgrl`
Taking the address of an unaligned :t:`field` captures the entire struct.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Separate point on the repr(packed) rule in this block: this still seems narrower than the Reference.

Upstream truncates at the first field access into any repr(packed) struct, and explicitly says this includes fields that are currently aligned as well, for future-compatibility reasons.

Could this sentence be widened accordingly instead of limiting it to unaligned fields?

Support:

  • Reference type.closure.capture.precision.unaligned

Comment thread src/expressions.rst Outdated
The :t:`capture path` is truncated at the rightmost :t:`dereference` in the :t:`capture path` if the :t:`dereference` is applied to a :t:`shared reference`.

:dp:`fls_v8IFXHJnXhez`
A :t:`place` is not captured when an :t:`underscore expression` is used to bind it.

@PLeVasseur PLeVasseur Apr 22, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional completeness point: upstream also states that values matched with wildcards must still be initialized.

If you want closer Reference parity in this subsection, consider adding a short note or rule for that as well.

Support:

  • Reference type.closure.capture.precision.wildcard.initialized

View changes since the review

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I skipped that deliberately as I thought it was redundant (pedantic)... the value won't be used anyways, and if this restriction gets removed in future, it does not affect the content of the FLS.

@kirtchev-adacore kirtchev-adacore May 11, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have the following blanket rule:

15.2:4 A variable shall be initialized before it is accessed.

where we conveniently do not explain what "accessed" means. 😆

This comment is still relevant.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kirtchev-adacore should I create a term for it, and what would the definition be

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the most part, "accessed" is equivalent to "read", however unsafe code and raw pointers change the game. I think it is possible to play pointer arithmetic games and read chunks of a variable, such as fields, but in a low-level fashion.

I guess 15.2:4 can be rephrased as

A variable shall be initialized before either the variable or any of its fields are read.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is that change worth making, seeing that "access" is used throughout the text (like "a field access expression is an expressions that accesses a field of a value")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not worth it then.

Comment thread src/expressions.rst Outdated
Matching against a variant of an enum that has more than one variant captures the :t:`place` by :t:`immutable borrow`.

:dp:`fls_7EXHdE2eOVek`
Matching against a variant of an enum that has one variant does not capture the place, unless it is marked with :t:`attribute` ``non_exhaustive``, in which case the place is captured by :t:`immutable borrow`.

@PLeVasseur PLeVasseur Apr 22, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Separate completeness point on the discriminant-read rules: this block still seems to omit the Reference rule for uninhabited variants.

Even if all variants but the one being matched against are uninhabited, the discriminant is still read if it otherwise would be.

Could this subsection add that explicitly?

Support:

View changes since the review

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is similar to #698 (comment)

@kirtchev-adacore kirtchev-adacore May 11, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The FLS does not define what an "uninhabited type" is, and it should. I propose we insert the following rules between 4.1:1 and 4.1:2:

An :t:inhabited type is a :t:type with :t:[constructor]s and :t:[value]s.

An :t:uninhabited type is a :t:type without :t:[constructor]s and :t:[value]s.

The FLS also does not define what a "constructor" is, and it should. I propose we insert a new 6.1.3 Constructor Expressions subsection, with the following singular legality rule:

A :t:constructor is an :t:array expression, a :t:struct expression, or a :t:tuple expression.

(I probably missed an expression here...)

Needless to say, all these rules will need corresponding entries in the Glossary.

Give the above, I propose the following changes:

:dp:fls_Fs12dmznjsMf
Matching against an :t:enum variant of an :t:enum with more than one :t:[enum variant]s :t:captures <capturing> the :t:place by :t:immutable borrow as the :t:discriminant is read.

:dp:fls_7EXHdE2eOVek
Matching against an :t:enum variant of an :t:enum with one :t:enum variant does not :t:capture <capturing> the :t:place as the :t:discriminant is not read, unless the :t:enum is subject to :t:attribute non_exhaustive, in which case the :t:place is captured by :t:immutable borrow.

I do not understand what the type.closure.capture.precision.discriminants.uninhabited-variants Reference rule is trying to say. Is it something along the lines of "if an enum type has at least one inhabited variant, and possibly multiple uninhabited variants, then the discriminant is still read, thus capturing takes place"?

This comment is still relevant.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kirtchev-adacore could these suggestions be a separate issue(s) instead... feels like it needs further refinement, and am not confident on the suggestion

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A separate issue sounds good!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tshepang tshepang mentioned this pull request Apr 27, 2026
@rustbot

This comment has been minimized.

tshepang and others added 18 commits May 2, 2026 19:24
Co-authored-by: Ana Hobden <operator@hoverbear.org>
Co-authored-by: Hristian Kirtchev <60669983+kirtchev-adacore@users.noreply.github.com>
Co-authored-by: Hristian Kirtchev <60669983+kirtchev-adacore@users.noreply.github.com>
Co-authored-by: Hristian Kirtchev <60669983+kirtchev-adacore@users.noreply.github.com>
…erload the term)

Co-authored-by: Hristian Kirtchev <60669983+kirtchev-adacore@users.noreply.github.com>
Co-authored-by: Hristian Kirtchev <60669983+kirtchev-adacore@users.noreply.github.com>
@tshepang tshepang force-pushed the tshepang/capture-precision branch from dcd9707 to 550f10d Compare May 2, 2026 17:24
@rustbot

rustbot commented May 2, 2026

Copy link
Copy Markdown
Collaborator

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

Comment thread src/expressions.rst Outdated
Comment thread src/patterns.rst Outdated
Comment thread src/patterns.rst Outdated
Comment thread src/patterns.rst Outdated
Comment thread src/expressions.rst Outdated
tshepang and others added 7 commits May 5, 2026 12:16
Co-authored-by: Hristian Kirtchev <60669983+kirtchev-adacore@users.noreply.github.com>
Co-authored-by: Hristian Kirtchev <60669983+kirtchev-adacore@users.noreply.github.com>
Co-authored-by: Hristian Kirtchev <60669983+kirtchev-adacore@users.noreply.github.com>
@kirtchev-adacore

Copy link
Copy Markdown
Contributor

This is it for now. I will have to revisit the remaining rules once the "place" business has been cleared up. I think we can find better wording/grouping of the remaining rules, as they feel somewhat randomly sprinkled.

View changes since this review

Just to clarify, after reading the Reference's Capture precision, I think "place" is indeed the proper term.

In an FLS call, we decided that it's not important after all...
my thinking is that mentioning a read is not exactly relevant,
as FLS is not a place to explain mechanics of rustc.
It should be enough to only explain surface behavior,
and not why that behavior is so.

This reverts commit a3d7be4.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants