Skip to content

const-eval.const-expr.borrows: mention indirect places #1865

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

Merged
merged 3 commits into from
Jul 3, 2025

Conversation

RalfJung
Copy link
Member

Follow-up to #1858.

@rustbot rustbot added the S-waiting-on-review Status: The marked PR is awaiting review from a maintainer label Jun 18, 2025
@RalfJung RalfJung force-pushed the const-expr-borrows-indirect branch from 9fe65cb to 9d83c64 Compare June 18, 2025 00:36
@RalfJung
Copy link
Member Author

Based on the last commit I added in rust-lang/rust#140942, instead of a list of things we allow, we could consider just saying what is not allowed -- mutable / interior mutable borrows of lifetime-extended places. Maybe that would be better?

@traviscross
Copy link
Contributor

Thanks @RalfJung; @ehuss and talked about this new language. On first pass, it looks pretty good to us. To your question about stating this in terms of what's not allowed, that also had some appeal to us, and we actually think it'd be best in this case to do both, i.e. to state what's allowed, as is done here, and to then also approach the same truth from the other side by stating what's not allowed. We do that in a number of places, and this seems like a good candidate for it, in terms of being as clear as possible.

@RalfJung
Copy link
Member Author

I've given that a shot, please let me know what you think.

I also realized destructors.scope.lifetime-extension.static fails to mention that it applies specifically to the top-level scope of const/static initializers.

@traviscross
Copy link
Contributor

traviscross commented Jun 23, 2025

Thanks. Probably we'll want to add some examples here for each.

Regarding the "top-level scope" bit, I wonder if there's a more clear way we could say that. We allow, of course, e.g.:

struct S;
const fn temp() -> S { S }
const C1: &S = { let y = { let x = &temp(); x }; y };
const C2: &S = 'top: { let y = { let x = &temp(); break 'top x; }; };

That is, there's a distinction between the temporary being created in the top-level scope and being referenced by the final value.

@RalfJung
Copy link
Member Author

All of those are immutable references so yes of course we allow them. That doesn't really exercise the clauses this PR is about, though.

There are no examples for any of the surrounding text here so I'll not try to figure out the best way to integrate examples into this part of the docs.

@traviscross
Copy link
Contributor

All of those are immutable references so yes of course we allow them. That doesn't really exercise the clauses this PR is about, though.

The question is about this change specifically, which is independent of mutability:

 r[destructors.scope.lifetime-extension.static]
-Lifetime extension also applies to `static` and `const` items, where it makes temporaries live until the end of the program. 
+Lifetime extension also applies to the top-level scope of `static` and `const` items, where it makes temporaries live until the end of the program.

It seems that the rule for this should be essentially the same as the rule that precedes it, except that we're extending to 'static rather than to the scope of the containing block:

r[destructors.scope.lifetime-extension.let]
The temporary scopes for expressions in let statements are sometimes extended to the scope of the block containing the let statement. This is done when the usual temporary scope would be too small, based on certain syntactic rules.

(Admittedly, that rule is rather ambiguous itself.)

So if this "top-level scope" qualification makes sense for one, then it would seem to make sense for the other.

But I think it gets into some ambiguity about "are we talking about the temporary being created in the top-level scope or about the temporary being referenced by the value returned from the top-level scope?". Clearly we don't mean the former.

@RalfJung
Copy link
Member Author

We mean "the temporary is extended to the scope outside the const initializer expression". Not sure what is the best way to put that...

@traviscross
Copy link
Contributor

traviscross commented Jun 26, 2025

There are no examples for any of the surrounding text here so I'll not try to figure out the best way to integrate examples into this part of the docs.

We'll take care of integrating these, but @ehuss and I would be interested if you could provide here, just as a reply, differentiating examples (i.e. examples that fit in exactly one category) for each of the things we're defining here, i.e. transient places, indirect places, places based on static items, and places based on promoted expressions.

@traviscross traviscross force-pushed the const-expr-borrows-indirect branch from ae67917 to 2ee9fd4 Compare June 26, 2025 17:51
@traviscross
Copy link
Contributor

We mean "the temporary is extended to the scope outside the const initializer expression". Not sure what is the best way to put that...

Thanks. I pushed a clarification to define this in terms of the other rule that touches on this.

@RalfJung
Copy link
Member Author

differentiating examples (i.e. examples that fit in exactly one category) for each of the things we're defining here, i.e. transient places, indirect places, places based on static items, and places based on promoted expressions.

static mut S: i32= 0;

#[allow(static_mut_refs)]
const C: () = {
  let mut x = 0;
  let mref = &mut x; // reference to transient place
  let mref2 = &mut *mref; // reference to indirect place
  let sref = unsafe { &mut S }; // reference to static place
  let pref: &mut [i32] = &mut []; // reference to promoted
};

@traviscross traviscross force-pushed the const-expr-borrows-indirect branch 2 times, most recently from 691ec18 to 846f675 Compare June 27, 2025 07:25
@traviscross
Copy link
Contributor

traviscross commented Jun 27, 2025

Thanks @RalfJung for the examples. I've pushed commits to integrate those and some others and to hopefully clarify some language. Let me know if that all looks right.

@traviscross traviscross force-pushed the const-expr-borrows-indirect branch 2 times, most recently from bf9bc37 to 07b3859 Compare June 27, 2025 07:53
@traviscross
Copy link
Contributor

In testing, I notice this:

---- const_eval.md - Constant_evaluation::Constant_expressions (line 212) stdout ----
error[E0764]: mutable borrows of lifetime-extended temporaries in the top-level scope of a constant are not allowed
 --> const_eval.md:213:18
  |
3 | const C: &[u8] = &mut [];
  |                  ^^^^^^^

error: aborting due to 1 previous error

This borrow isn't of an interior mutable temporary. Does this imply a second or a broader exception, you think, or is this covered by something else?

@traviscross traviscross force-pushed the const-expr-borrows-indirect branch from 07b3859 to c55401b Compare June 27, 2025 08:10
@RalfJung
Copy link
Member Author

It's mutable, but not interior mutable.

Though what I find odd is that &mut [] should be promoted, and then I'd expect this to actually compile...

@RalfJung
Copy link
Member Author

So, uh, looks like we are doing the static const checks before we do promotion? @oli-obk is that expected?

That means we should remove promotion from the text and examples in this PR.

@oli-obk
Copy link
Contributor

oli-obk commented Jun 27, 2025

static const checks happen before promotion intentionally. I think they should even be a part of typeck, and I think we should be able to move more of it into typeck

@RalfJung
Copy link
Member Author

Okay, looks like I just entirely forgot about that then.

That's good, discussing promoteds here got a bit awkward IMO...

compiler-errors added a commit to compiler-errors/rust that referenced this pull request Jun 27, 2025
… r=oli-obk

const checks for lifetime-extended temporaries: avoid 'top-level scope' terminology

This error recently got changed in rust-lang#140942 to use the terminology of "top-level scope", but after further discussion in rust-lang/reference#1865 it seems the reference will not be using that terminology after all. So let's also remove it from the compiler again, and let's focus on what actually happens with these temporaries: their lifetime is extended until the end of the program.

r? `@oli-obk` `@traviscross`
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Jun 27, 2025
… r=oli-obk

const checks for lifetime-extended temporaries: avoid 'top-level scope' terminology

This error recently got changed in rust-lang#140942 to use the terminology of "top-level scope", but after further discussion in rust-lang/reference#1865 it seems the reference will not be using that terminology after all. So let's also remove it from the compiler again, and let's focus on what actually happens with these temporaries: their lifetime is extended until the end of the program.

r? ``@oli-obk`` ``@traviscross``
@traviscross
Copy link
Contributor

It seems a rather subtle point to document, given that:

const C1: &[u8] = { let x: &'static mut [u8] = &mut []; x }; //~ OK
const C2: &[u8] = { &mut [] }; //~ ERROR

rust-timer added a commit to rust-lang/rust that referenced this pull request Jun 28, 2025
Rollup merge of #143092 - RalfJung:const-check-lifetime-ext, r=oli-obk

const checks for lifetime-extended temporaries: avoid 'top-level scope' terminology

This error recently got changed in #140942 to use the terminology of "top-level scope", but after further discussion in rust-lang/reference#1865 it seems the reference will not be using that terminology after all. So let's also remove it from the compiler again, and let's focus on what actually happens with these temporaries: their lifetime is extended until the end of the program.

r? ``@oli-obk`` ``@traviscross``
github-actions bot pushed a commit to rust-lang/miri that referenced this pull request Jun 28, 2025
const checks for lifetime-extended temporaries: avoid 'top-level scope' terminology

This error recently got changed in rust-lang/rust#140942 to use the terminology of "top-level scope", but after further discussion in rust-lang/reference#1865 it seems the reference will not be using that terminology after all. So let's also remove it from the compiler again, and let's focus on what actually happens with these temporaries: their lifetime is extended until the end of the program.

r? ``@oli-obk`` ``@traviscross``
@RalfJung
Copy link
Member Author

Yeah, that is kind of odd -- the fact that lifetime extension kicks in here means that code that would otherwise have compiled (if there was no lifetime extension) now does not compile.

@traviscross traviscross force-pushed the const-expr-borrows-indirect branch from c55401b to 6091e79 Compare June 30, 2025 11:49
@traviscross
Copy link
Contributor

I may yet make some further editorial revisions, but @RalfJung, how does this look?

@traviscross traviscross force-pushed the const-expr-borrows-indirect branch 2 times, most recently from 8b3722b to 0d9c1cf Compare July 3, 2025 22:09
Rather than talking about lifetime-extended temporaries in the
top-level scope of an initializer, which is maybe a bit ambiguous,
let's speak directly to the result of the lifetime extension, which is
that these temporaries disallowed for borrows would have their
lifetimes extended to the end of the program.

Let's also speak about place expressions, rather than places, as
that's more precise here.

We'll add examples throughout.  Thanks to RalfJ for the substance of
many of these.
@traviscross traviscross force-pushed the const-expr-borrows-indirect branch from 0d9c1cf to c1f8da5 Compare July 3, 2025 22:20
@traviscross traviscross added this pull request to the merge queue Jul 3, 2025
@traviscross
Copy link
Contributor

Thanks @RalfJung for the follow-up here.

Merged via the queue into rust-lang:master with commit c1720b1 Jul 3, 2025
5 checks passed
@rustbot rustbot removed the S-waiting-on-review Status: The marked PR is awaiting review from a maintainer label Jul 3, 2025
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.

4 participants