Skip to content

Conversation

dianne
Copy link
Contributor

@dianne dianne commented Aug 25, 2025

Reference PR: rust-lang/reference#1980

This changes the semantics for super let (and macros implemented in terms of it, such as pin!, format_args!, write!, and println!) as suggested by @theemathas in #145784 (comment), making super let initializers only count as extending expressions when the super let itself is within an extending block. Since super let initializers aren't temporary drop scopes, their temporaries outside of inner temporary scopes are effectively always extended, even when not in extending positions; this only affects two cases as far as I can tell:

  • Block tail expressions in Rust 2024. This PR makes f(pin!({ &temp() })) drop temp() at the end of the block in Rust 2024, whereas previously it would live until after the call to f because syntactically the temp() was in an extending position as a result of super let in pin!'s expansion.
  • super let nested within a non-extended super let is no longer extended. i.e. a normal let is required to treat super lets as extending (in which case nested super lets will also be extending).

Closes #145784

This is a breaking change. Both static and dynamic semantics are affected. The most likely breakage is for programs to stop compiling, but it's technically possible for drop order to silently change as well (as in #145784). Since this affects stable macros, it probably would need a crater run.

Nominating for discussion alongside #145784: @rustbot label +I-lang-nominated +I-libs-api-nominated

Tracking issue for super let: #139076

@rustbot
Copy link
Collaborator

rustbot commented Aug 25, 2025

r? @jackh726

rustbot has assigned @jackh726.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. I-lang-nominated Nominated for discussion during a lang team meeting. I-libs-api-nominated Nominated for discussion during a libs-api team meeting. labels Aug 25, 2025
@rust-log-analyzer

This comment has been minimized.

@dianne dianne force-pushed the non-extending-super-let branch from 0542d4f to a35548f Compare August 25, 2025 07:51
@dianne
Copy link
Contributor Author

dianne commented Aug 25, 2025

Copied from #145784 (comment), since I think this is a notable caveat of this PR and worth considering before approving it:

This comes with a bit of a gotcha in terms of temporary lifetimes: it might be strange that the temp() would live longer in

non_extending({ let x = { &temp() }; f(x) }); // ok

than in

non_extending({ super let x = { &temp() }; f(x) }); // error

Though the case for if let is similar: its scrutinee isn't a temporary scope and it doesn't have lifetime extension rules that can make block tail expressions' temporaries live longer.

@dianne
Copy link
Contributor Author

dianne commented Aug 25, 2025

Also copied since it motivates this PR: I think something like this may be necessary for the identity

&EXPR === { super let x = &EXPR; x }

to hold in both extending and non-extending contexts without changing how block tail expression scopes work. Substituting, e.g. { &temp() } in for EXPR, the identity only currently holds in extending contexts in Rust 2024. In non-extending contexts,

&{ &temp() }

drops temp() when leaving the block, but

{ super let x = &{ &temp() }; x }

would extend it to outlive x. This PR shortens the lifetime of temp() such that it's dropped at the end of the block in both cases. I haven't done any rigorous proof or extensive testing that this PR together with #145342 makes the identity always hold, however.

@jieyouxu jieyouxu added the T-lang Relevant to the language team label Aug 25, 2025
@traviscross traviscross added the P-lang-drag-1 Lang team prioritization drag level 1. https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang label Aug 25, 2025
@traviscross
Copy link
Contributor

traviscross commented Aug 25, 2025

To confirm, with this PR, does this behavior hold (in Rust 2024)?:

fn f<T>(_: LogDrop<'_>, x: T) -> T { x }

// These two should be the same.
assert_drop_order(1..=3, |e| {
    let _v = f(e.log(2), &{ &raw const *&e.log(1) });
    drop(e.log(3));
});
assert_drop_order(1..=3, |e| {
    let _v = f(e.log(2), {
        super let v = &{ &raw const *&e.log(1) };
        v
    });
    drop(e.log(3));
});
// These two should be the same.
assert_drop_order(1..=3, |e| {
    let _v = f(e.log(1), &&raw const *&e.log(2));
    drop(e.log(3));
});
assert_drop_order(1..=3, |e| {
    let _v = f(e.log(1), {
        super let v = &&raw const *&e.log(2);
        v
    });
    drop(e.log(3));
});
// These two should be the same.
assert_drop_order(1..=2, |e| {
    let _v = &{ &raw const *&e.log(2) };
    drop(e.log(1));
});
assert_drop_order(1..=2, |e| {
    let _v = {
        super let v = &{ &raw const *&e.log(2) };
        v
    };
    drop(e.log(1));
});

Playground link

(If any of these are missing, please add them as tests.)

@theemathas
Copy link
Contributor

theemathas commented Aug 25, 2025

super let nested within a non-extended super let is no longer extended

Does this PR affect any edition 2021 code?

@dianne dianne force-pushed the non-extending-super-let branch from a35548f to f0c43cf Compare August 25, 2025 12:05
@dianne
Copy link
Contributor Author

dianne commented Aug 25, 2025

To confirm, with this PR, does this behavior hold (in Rust 2024)?:

Those all hold under this PR, yes. I've added them all as tests (with some additional versioning to account for the Edition-dependent drop order in the first one); thanks!

super let nested within a non-extended super let is no longer extended

Does this PR affect any edition 2021 code?

Not that I'm aware of. That detail matters in Edition 2024, since the nested super let could have an extending borrow operator within a block tail expression in its initializer, which would previously extend the borrowed temporary's lifetime to outlive the outer super let's binding. In Edition 2021, that temporary outlives the outer super let's binding regardless, since block tail expressions (and super let initializers) aren't temporary scopes.

@rust-log-analyzer

This comment has been minimized.

@dianne dianne force-pushed the non-extending-super-let branch from f0c43cf to 387cfa5 Compare August 25, 2025 12:23
Comment on lines 131 to 132
// We have extending borrow expressions within the initializer
// expression.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// We have extending borrow expressions within the initializer
// expression.
// We have extending borrow expressions within a non-extending
// expression within the initializer expression.

(Revising my earlier text here.)

@traviscross traviscross added needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. S-waiting-on-documentation Status: Waiting on approved PRs to documentation before merging and removed T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Aug 25, 2025
@traviscross
Copy link
Contributor

This is correct, I believe. Let's propose to do it.

@rfcbot fcp merge

@dianne, let's document this in the Reference.

cc @rust-lang/lang-docs

@rfcbot
Copy link

rfcbot commented Aug 25, 2025

Team member @traviscross has proposed to merge this. The next step is review by the rest of the tagged team members:

No concerns currently listed.

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

cc @rust-lang/lang-advisors: FCP proposed for lang, please feel free to register concerns.
See this document for info about what commands tagged team members can give me.

@rfcbot rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels Aug 25, 2025
@traviscross
Copy link
Contributor

cc @m-ou-se @rust-lang/libs-api

@dianne dianne force-pushed the non-extending-super-let branch from 387cfa5 to 23caea2 Compare August 26, 2025 00:58
@theemathas
Copy link
Contributor

I've reviewed 13 of the test failures in #145838 (comment) (top-to-bottom from Arxoto.sudoku.78ccf4297d22f37e62e8bf07ff19f7f2e7843139 to LegendarySaiyan.structures.f375d1f242c501951f7a3a6c49a09a7895f57326). All of them are flaky tests. Made a crater PR.

@theemathas
Copy link
Contributor

Saethlin told me that I should wait for the retry before checking the test failures.

@craterbot
Copy link
Collaborator

🚧 Experiment pr-145838-1 is now running

ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot
Copy link
Collaborator

🎉 Experiment pr-145838-1 is completed!
📊 697 regressed and 670 fixed (187804 total)
📊 580 spurious results on the retry-regessed-list.txt, consider a retry1 if this is a significant amount.
📰 Open the summary report.

⚠️ If you notice any spurious failure please add them to the denylist!
ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

Footnotes

  1. re-run the experiment with crates=https://crater-reports.s3.amazonaws.com/pr-145838-1/retry-regressed-list.txt

@craterbot craterbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-crater Status: Waiting on a crater run to be completed. labels Sep 1, 2025
@dianne
Copy link
Contributor Author

dianne commented Sep 1, 2025

At a glance, the "build compiler error" failures look real, following the pattern of using &format!(...) in a block of a conditional expression inside a format argument (alongside at least one other format argument). The rest I've peeked at look spurious. The "fixed" crates are almost certainly all noise, so I'm expecting a comparable number of spurious regressions. I'll try and review them more comprehensively later.


In the mean time, I've implemented my lifetime extension suggestion for block tails. It's not ready for review yet, but it lives at #146098 now for testing/discussion purposes.

@jieyouxu
Copy link
Member

jieyouxu commented Sep 2, 2025

Retrying since there's a lot of "cannot find directory"s.
@craterbot run mode=build-and-test crates=https://crater-reports.s3.amazonaws.com/pr-145838-1/retry-regressed-list.txt p=1

@craterbot
Copy link
Collaborator

👌 Experiment pr-145838-2 created and queued.
🤖 Automatically detected try build b83b707
🔍 You can check out the queue and this experiment's details.

ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot craterbot added S-waiting-on-crater Status: Waiting on a crater run to be completed. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 2, 2025
@traviscross traviscross removed I-lang-nominated Nominated for discussion during a lang team meeting. P-lang-drag-1 Lang team prioritization drag level 1. https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang labels Sep 2, 2025
@craterbot
Copy link
Collaborator

🚧 Experiment pr-145838-2 is now running

ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot
Copy link
Collaborator

🎉 Experiment pr-145838-2 is completed!
📊 22 regressed and 18 fixed (1276 total)
📊 65 spurious results on the retry-regessed-list.txt, consider a retry1 if this is a significant amount.
📰 Open the summary report.

⚠️ If you notice any spurious failure please add them to the denylist!
ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

Footnotes

  1. re-run the experiment with crates=https://crater-reports.s3.amazonaws.com/pr-145838-2/retry-regressed-list.txt

@craterbot craterbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-crater Status: Waiting on a crater run to be completed. labels Sep 3, 2025
@dianne
Copy link
Contributor Author

dianne commented Sep 3, 2025

22 isn't too bad to sift through. I'll write up a list of what I find. As before, the build failures look real and the test failures I've seen so far look spurious.

@dianne
Copy link
Contributor Author

dianne commented Sep 4, 2025

Build failures (all real, same crates as in #145838 (comment), though the last failure there is missing):

For completeness, the last build failure from the first crater run:


Test failures (all spurious):

@Skgland
Copy link
Contributor

Skgland commented Sep 4, 2025

In case anyone else is wondering about the disappearance of https://github.com/musicodeXQQ/updownserv.
In creater run pr-145838-1 it ended up in the error category (master log, try log), resulting in it not being included on the retry-regressed-list.txt.

@apiraino
Copy link
Contributor

apiraino commented Sep 4, 2025

Beta backport declined as per compiler team on Zulip, we are pretty close to the new release and this patch wouldn't have time to "bake" properly.

@rustbot label -beta-nominated

@rustbot rustbot removed the beta-nominated Nominated for backporting to the compiler in the beta channel. label Sep 4, 2025
Comment on lines 501 to 505
// Don't lifetime-extend child `super let`s or block tail expressions' temporaries in
// the initializer when this `super let` is not itself extended by a parent `let`
// (#145784). Block tail expressions are temporary drop scopes in Editions 2024 and
// later, their temps shouldn't outlive the block in e.g. `f(pin!({ &temp() }))`.
extend_initializer = false;
Copy link
Member

Choose a reason for hiding this comment

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

Just an initial quick review - I don't really know this code and really need to dig in to review this properly. My general initial thought is that this change is kind of not-so-great. I wonder if there's some kind of refactoring that could be done to this that would make this code more "clean"

Copy link
Contributor Author

@dianne dianne Sep 4, 2025

Choose a reason for hiding this comment

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

Agreed. I also tried making the if expression containing that evaluate to a bool (so it could be assigned to a variable), and I tried let extend_initializer; (so it could be assigned in each branch), but in both cases it felt even messier with the else block needed. Maybe it would be nicer as a match? I hesitated since it looked like would require a lot of reindentation, but I'll give it a shot.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It looks like it didn't even require reindenting or reflowing! I think it's much nicer as a match, but possibly it could be cleaned up with further refactoring to not require assigning to a bool variable at all. (diff)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. 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-review Status: Awaiting review from the assignee but also interested parties. 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
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