Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

temporary lifetime extension for block tail expressions #146098

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

Draft
dianne wants to merge 9 commits into rust-lang:master
base: master
Choose a base branch
Loading
from dianne:extending-blocks

Conversation

Copy link
Contributor

@dianne dianne commented Sep 1, 2025
edited
Loading

For convenience of implementation and testing, I've based this on #145838 with #145342's commits cherry-picked in, plus some slight tweaks and additional tests (#145838 (comment)). The real change this PR makes is the final commit.

This implements the temporary lifetime extension semantics I suggested in #145838 (comment), with the goal of making temporary lifetimes and drop order more consistent between extending and non-extending blocks. As a side-effect, this fixes #145784 with hopefully milder regressions than #145838 (in exchange for having much broader surface area).

Roughly, this subjects extending borrows (and super lets) in block tails to extended scopes, using the same rules as let statement initializers. Under this PR,

// This `temp()` is now extended past the block tail in all contexts.
{ &temp() }

now extends the lifetime of temp() to outlive the block tail in Rust 2024 regardless of whether the block is an extending expression in a let statement initializer (in which context it was already extended to outlive the block before this PR). The scoping rules for tails of extending blocks remain the same: extending subexpressions' temporary scopes are extended based on the source of the lifetime extension (e.g. to match the scope of a parent let statement's bindings). For blocks not extended by any other source, extending borrows in the tail expression now share a temporary scope with the result of the block. This can in turn extend nested blocks within blocks' tail expressions:

// This `temp()` is extended past the outer block tail.
// It is now dropped after the reference to it at the `;`.
f({{ &temp() }});
// This context-sensitivity is consistent with `let`:
// This `temp()` was already extended.
// It is still dropped after `x` at the end of its scope.
let x = {{ &temp() }};

Since this uses the same rules as let, it only applies to extending sub-expressions.

// This `temp()` is still never extended in any context.
// In Rust 2024, it is dropped at the end of the block tail.
{ identity(&temp()) }

I'm opening this as a draft for now to have a separate place to test and discuss it. Before the implementation can be properly reviewed, I think it should have more tests, it should be optimized, it should have a Reference PR to cross-reference, and the commit history should be cleaned up.

@rustbot label +T-lang

dianne and others added 8 commits August 25, 2025 19:35
@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team labels Sep 1, 2025

This comment has been minimized.

@rustbot rustbot added the stable-nominated Nominated for backporting to the compiler in the stable channel. label Sep 1, 2025
Copy link
Contributor Author

dianne commented Sep 1, 2025

@rustbot label -stable-nominated

I'm not intending to stable-nominate this, at least. Someone else can, but I don't expect it's needed or that it would be accepted.

jieyouxu reacted with thumbs up emoji

This comment was marked as off-topic.

This comment has been minimized.

@jieyouxu jieyouxu removed the stable-nominated Nominated for backporting to the compiler in the stable channel. label Sep 2, 2025
@traviscross traviscross added I-lang-radar Items that are on lang's radar and will need eventual work or consideration. needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. labels Sep 2, 2025
Copy link
Contributor

Does this only affect code in Rust 2024, or would you expect any visible difference in earlier editions?

@rustbot rustbot added the stable-nominated Nominated for backporting to the compiler in the stable channel. label Sep 2, 2025
@theemathas theemathas removed the stable-nominated Nominated for backporting to the compiler in the stable channel. label Sep 2, 2025
Copy link
Contributor Author

dianne commented Sep 2, 2025
edited
Loading

It should only be visible in Rust 2024. The only extending expressions that introduce temporary drop scopes are Rust 2024 block tail expressions. Suppose we have a macro extending!, for which $expr is extending if extending!($expr) is extending. Under this PR, in a non-extending context, { extending!(&temp()) } would give temp() the same temporary scope as the result of the block. Prior to Rust 2021, they're already in the same scope, due to extending! being unable to introduce temporary scopes.

Or to generalize this, the aim of this PR is that in a non-extending context, extending!(&temp()) should give temp() the same temporary scope as the expansion, similar to how let x = extending!(&temp()); gives temp() the same scope as x. This already holds in Rust 2021 and prior.

If new expressions are added to Rust that are both extending and temporary scopes, I'd want this behavior to apply to them as well.

Copy link
Contributor

Since this would effectively reduce the scope of the Rust 2024 tail expression temporary scope change, we'd also want to be sure to reflect that in the behavior of the tail-expr-drop-order lint.

Copy link
Contributor Author

dianne commented Sep 2, 2025

I haven't done extensive testing, but see this test diff for that lint: lint-tail-expr-drop-order-borrowck.rs. I'm applying the lifetime extension rules on all editions, and lifetime extension prevents the temporary scope from being registered as potentially forwards-incompatible (even though the extended scopes are technically the same as the old scopes in old editions). Though I think I've convinced myself at this point that lifetime extension doesn't need to be applied to block tails of non-extending old-edition blocks1 , so potentially the lint change could be implemented in some other way instead.

Footnotes

  1. I was worried about mixed-edition code, but I don't think it's an issue anymore.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Reviewers
No reviews
Assignees
No one assigned
Labels
I-lang-radar Items that are on lang's radar and will need eventual work or consideration. needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

pin!() changed temporary lifetime extension behavior in version 1.88.0 with edition 2024 tail expression temporary scopes

AltStyle によって変換されたページ (->オリジナル) /