about summary refs log tree commit diff
path: root/compiler/rustc_hir_analysis/src/check/region.rs
AgeCommit message (Collapse)AuthorLines
2025-09-17Rollup merge of #145838 - dianne:non-extending-super-let, r=jackh726,traviscrossStuart Cook-5/+19
don't apply temporary lifetime extension rules to non-extended `super let` 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 https://github.com/rust-lang/rust/issues/145784#issuecomment-3218658335, making `super let` initializers only count as [extending expressions](https://doc.rust-lang.org/nightly/reference/destructors.html#extending-based-on-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 let`s as extending (in which case nested `super let`s will also be extending). Closes rust-lang/rust#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 rust-lang/rust#145784). Since this affects stable macros, it probably would need a crater run. Nominating for discussion alongside rust-lang/rust#145784: ````@rustbot```` label +I-lang-nominated +I-libs-api-nominated Tracking issue for `super let`: rust-lang/rust#139076
2025-09-04don't extend non-extended `super let` initializers' block tail tempsdianne-5/+19
2025-08-13fix scope of `super let` bindings within `if let`dianne-6/+2
They now use the enclosing temporary scope as their scope, regardless of which `ScopeData` was used to mark it.
2025-08-07only introduce a guard scope for arms with guardsdianne-5/+5
2025-08-07add a scope for `if let` guard temporaries and bindingsdianne-0/+5
This ensures `if let` guard temporaries and bindings are dropped before the match arm's pattern's bindings.
2025-07-13Auto merge of #143213 - dianne:lower-cond-tweaks, r=cjgillotbors-11/+27
de-duplicate condition scoping logic between AST→HIR lowering and `ScopeTree` construction There was some overlap between `rustc_ast_lowering::LoweringContext::lower_cond` and `rustc_hir_analysis::check::region::resolve_expr`, so I've removed the former and migrated its logic to the latter, with some simplifications. Consequences: - For `while` and `if` expressions' `let`-chains, this changes the `HirId`s for the `&&`s to properly correspond to their AST nodes. This is how guards were handled already. - This makes match guards share previously-duplicated logic with `if`/`while` expressions. This will also be used by guard pattern[^1] guards. - Aside from legacy syntax extensions (e.g. some builtin macros) that directly feed AST to the compiler, it's currently impossible to put attributes directly on `&&` operators in `let` chains[^2]. Nonetheless, attributes on `&&` operators in `let` chains in `if`/`while` expression conditions are no longer silently ignored and will be lowered. - This no longer wraps conditions in `DropTemps`, so the HIR and THIR will be slightly smaller. - `DesugaringKind::CondTemporary` is now gone. It's no longer applied to any spans, and all uses of it were dead since they were made to account for `if` and `while` being desugared to `match` on a boolean scrutinee. - Should be a marginal perf improvement beyond that due to leveraging [`ScopeTree` construction](https://github.com/rust-lang/rust/blob/5e749eb66f93ee998145399fbdde337e57cd72ef/compiler/rustc_hir_analysis/src/check/region.rs#L312-L355)'s clever handling of `&&` and `||`: - This removes some unnecessary terminating scopes that were placed around top-level `&&` and `||` operators in conditions. When lowered to MIR, logical operator chains don't create intermediate boolean temporaries, so there's no temporary to drop. The linked snippet handles wrapping the operands in terminating scopes as necessary, in case they create temporaries. - The linked snippet takes care of letting `let` temporaries live and terminating other operands, so we don't need separate traversals of `&&` chains for that. [^1]: rust-lang/rust#129967 [^2]: Case-by-case, here's my justification: `#[attr] e1 && e2` applies the attribute to `e1`. In `#[attr] (e1 && e2)` , the attribute is on the parentheses in the AST, plus it'd fail to parse if `e1` or `e2` contains a `let`. In `#[attr] expands_to_let_chain!()`, the attribute would already be ignored (rust-lang/rust#63221) and it'd fail to parse anyway; even if the expansion site is a condition, the expansion wouldn't be parsed with `Restrictions::ALLOW_LET`. If it *was* allowed, the notion of a "reparse context" from https://github.com/rust-lang/rust/issues/61733#issuecomment-509626449 would be necessary in order to make `let`-chains left-associative; multiple places in the compiler assume they are.
2025-07-05de-duplicate condition scoping logicdianne-11/+27
2025-07-05Remove yields_in_scope from the scope tree.Camille GILLOT-168/+37
2025-06-13Add comment.Mara Bos-0/+3
2025-06-13Implement temporary lifetime extension for tuple ctors.Mara Bos-7/+11
2025-04-07Rollup merge of #139112 - m-ou-se:super-let, r=lcnrStuart Cook-16/+85
Implement `super let` Tracking issue: https://github.com/rust-lang/rust/issues/139076 This implements `super let` as proposed in #139080, based on the following two equivalence rules. 1. For all expressions `$expr` in any context, these are equivalent: - `& $expr` - `{ super let a = & $expr; a }` 2. And, additionally, these are equivalent in any context when `$expr` is a temporary (aka rvalue): - `& $expr` - `{ super let a = $expr; & a }` So far, this experiment has a few interesting results: ## Interesting result 1 In this snippet: ```rust super let a = f(&temp()); ``` I originally expected temporary `temp()` would be dropped at the end of the statement (`;`), just like in a regular `let`, because `temp()` is not subject to temporary lifetime extension. However, it turns out that that would break the fundamental equivalence rules. For example, in ```rust g(&f(&temp())); ``` the temporary `temp()` will be dropped at the `;`. The first equivalence rule tells us this must be equivalent: ```rust g({ super let a = &f(&temp()); a }); ``` But that means that `temp()` must live until the last `;` (after `g()`), not just the first `;` (after `f()`). While this was somewhat surprising to me at first, it does match the exact behavior we need for `pin!()`: The following _should work_. (See also https://github.com/rust-lang/rust/issues/138718) ```rust g(pin!(f(&mut temp()))); ``` Here, `temp()` lives until the end of the statement. This makes sense from the perspective of the user, as no other `;` or `{}` are visible. Whether `pin!()` uses a `{}` block internally or not should be irrelevant. This means that _nothing_ in a `super let` statement will be dropped at the end of that super let statement. It does not even need its own scope. This raises questions that are useful for later on: - Will this make temporaries live _too long_ in cases where `super let` is used not in a hidden block in a macro, but as a visible statement in code like the following? ```rust let writer = { super let file = File::create(&format!("/home/{user}/test")); Writer::new(&file) }; ``` - Is a `let` statement in a block still the right syntax for this? Considering it has _no_ scope of its own, maybe neither a block nor a statement should be involved This leads me to think that instead of `{ super let $pat = $init; $expr }`, we might want to consider something like `let $pat = $init in $expr` or `$expr where $pat = $init`. Although there are also issues with these, as it isn't obvious anymore if `$init` should be subject to temporary lifetime extension. (Do we want both `let _ = _ in ..` and `super let _ = _ in ..`?) ## Interesting result 2 What about `super let x;` without initializer? ```rust let a = { super let x; x = temp(); &x }; ``` This works fine with the implementation in this PR: `x` is extended to live as long as `a`. While it matches my expectations, a somewhat interesting thing to realize is that these are _not_ equivalent: - `super let x = $expr;` - `super let x; x = $expr;` In the first case, all temporaries in $expr will live at least as long as (the result of) the surrounding block. In the second case, temporaries will be dropped at the end of the assignment statement. (Because the assignment statement itself "is not `super`".) This difference in behavior might be confusing, but it _might_ be useful. One might want to extend the lifetime of a variable without extending all the temporaries in the initializer expression. On the other hand, that can also be expressed as: - `let x = $expr; super let x = x;` (w/o temporary lifetime extension), or - `super let x = { $expr };` (w/ temporary lifetime extension) So, this raises these questions: - Do we want to accept `super let x;` without initializer at all? - Does it make sense for statements other than let statements to be "super"? An expression statement also drops temporaries at its `;`, so now that we discovered that `super let` basically disables that `;` (see interesting result 1), is there a use to having other statements without their own scope? (I don't think that's ever useful?) ## Interesting result 3 This works now: ```rust super let Some(x) = a.get(i) else { return }; ``` I didn't put in any special cases for `super let else`. This is just the behavior that 'naturally' falls out when implementing `super let` without thinking of the `let else` case. - Should `super let else` work? ## Interesting result 4 This 'works': ```rust fn main() { super let a = 123; } ``` I didn't put in any special cases for `super let` at function scope. I had expected the code to cause an ICE or other weird failure when used at function body scope, because there's no way to let the variable live as long as the result of the function. This raises the question: - Does this mean that this behavior is the natural/expected behavior when `super let` is used at function scope? Or is this just a quirk and should we explicitly disallow `super let` in a function body? (Probably the latter.) --- The questions above do not need an answer to land this PR. These questions should be considered when redesigning/rfc'ing/stabilizing the feature.
2025-04-07Rollup merge of #139035 - nnethercote:PatKind-Missing, r=oli-obkStuart Cook-0/+1
Add new `PatKind::Missing` variants To avoid some ugly uses of `kw::Empty` when handling "missing" patterns, e.g. in bare fn tys. Helps with #137978. Details in the individual commits. r? ``@oli-obk``
2025-04-07More trivial tweaksMichael Goulet-7/+4
2025-04-04Boolean hate.Mara Bos-5/+15
2025-04-04Implement `super let`.Mara Bos-16/+75
2025-03-28Remove `terminating_scopes` hash set.Mara Bos-164/+95
2025-03-28Remove outdated comment.Mara Bos-1/+0
2025-03-28Remove ScopeDepth entirely.Mara Bos-24/+14
The scope depth was tracked, but never actually used for anything.
2025-03-28Add `{ast,hir,thir}::PatKind::Missing` variants.Nicholas Nethercote-0/+1
"Missing" patterns are possible in bare fn types (`fn f(u32)`) and similar places. Currently these are represented in the AST with `ast::PatKind::Ident` with no `by_ref`, no `mut`, an empty ident, and no sub-pattern. This flows through to `{hir,thir}::PatKind::Binding` for HIR and THIR. This is a bit nasty. It's very non-obvious, and easy to forget to check for the exceptional empty identifier case. This commit adds a new variant, `PatKind::Missing`, to do it properly. The process I followed: - Add a `Missing` variant to `{ast,hir,thir}::PatKind`. - Chang `parse_param_general` to produce `ast::PatKind::Missing` instead of `ast::PatKind::Missing`. - Look through `kw::Empty` occurrences to find functions where an existing empty ident check needs replacing with a `PatKind::Missing` check: `print_param`, `check_trait_item`, `is_named_param`. - Add a `PatKind::Missing => unreachable!(),` arm to every exhaustive match identified by the compiler. - Find which arms are actually reachable by running the test suite, changing them to something appropriate, usually by looking at what would happen to a `PatKind::Ident`/`PatKind::Binding` with no ref, no `mut`, an empty ident, and no subpattern. Quite a few of the `unreachable!()` arms were never reached. This makes sense because `PatKind::Missing` can't happen in every pattern, only in places like bare fn tys and trait fn decls. I also tried an alternative approach: modifying `ast::Param::pat` to hold an `Option<P<Pat>>` instead of a `P<Pat>`, but that quickly turned into a very large and painful change. Adding `PatKind::Missing` is much easier.
2025-03-26Remove ScopeDepth from var_parent.Mara Bos-10/+16
It was never used.
2025-03-26Don't set cx.parent to None; it seems unnecessary.Mara Bos-2/+1
2025-03-26Simplify RvalueCandidateType.Mara Bos-8/+2
There is no difference between the Patternand Borrow cases. Reduce it to a simple struct.
2025-03-26Don't record child scopes for patterns.Mara Bos-2/+0
They are unused.
2025-02-18Move methods from `Map` to `TyCtxt`, part 2.Nicholas Nethercote-3/+3
Continuing the work started in #136466. Every method gains a `hir_` prefix, though for the ones that already have a `par_` or `try_par_` prefix I added the `hir_` after that.
2025-02-17Move some `Map` methods onto `TyCtxt`.Nicholas Nethercote-2/+2
The end goal is to eliminate `Map` altogether. I added a `hir_` prefix to all of them, that seemed simplest. The exceptions are `module_items` which became `hir_module_free_items` because there was already a `hir_module_items`, and `items` which became `hir_free_items` for consistency with `hir_module_free_items`.
2025-01-29Eliminate PatKind::PathOli Scherer-1/+0
2025-01-09Rename RegionResolutionVisitor to ScopeResolutionVisitorMichael Goulet-12/+12
2025-01-09Make sure to walk into nested const blocks in RegionResolutionVisitorMichael Goulet-4/+8
2025-01-08Rename PatKind::Lit to ExprOli Scherer-1/+1
2024-12-31add guard patterns to HIR and implement loweringMax Niederman-1/+4
2024-12-19Rename Scope.id to Scope.local_id, remove trivial accessorMichael Goulet-11/+11
2024-11-20reduce false positives of tail-expr-drop-order from consumed valuesDing Xiang Fei-2/+18
take 2 open up coroutines tweak the wordings the lint works up until 2021 We were missing one case, for ADTs, which was causing `Result` to yield incorrect results. only include field spans with significant types deduplicate and eliminate field spans switch to emit spans to impl Drops Co-authored-by: Niko Matsakis <nikomat@amazon.com> collect drops instead of taking liveness diff apply some suggestions and add explantory notes small fix on the cache let the query recurse through coroutine new suggestion format with extracted variable name fine-tune the drop span and messages bugfix on runtime borrows tweak message wording filter out ecosystem types earlier apply suggestions clippy check lint level at session level further restrict applicability of the lint translate bid into nop for stable mir detect cycle in type structure
2024-10-29Rollup merge of #131984 - dingxiangfei2009:stabilize-if-let-rescope, ↵Matthias Krüger-4/+2
r=traviscross,lcnr Stabilize if_let_rescope Close #131154 Tracked by #124085
2024-10-24stabilize if_let_rescopeDing Xiang Fei-4/+2
2024-10-24stabilize shorter-tail-lifetimesDing Xiang Fei-3/+1
2024-10-23nightly feature tracking: get rid of the per-feature bool fieldsRalf Jung-3/+5
2024-09-13Disallow hidden references to mutable staticObei Sideg-6/+0
2024-09-11rescope temp lifetime in let-chain into IfElseDing Xiang Fei-2/+12
apply rules by span edition
2024-08-30Remove `#[macro_use] extern crate tracing` from `rustc_hir_analysis`.Nicholas Nethercote-0/+1
2024-08-27Add `warn(unreachable_pub)` to `rustc_hir_analysis`.Nicholas Nethercote-2/+2
2024-07-29Reformat `use` declarations.Nicholas Nethercote-2/+2
The previous commit updated `rustfmt.toml` appropriately. This commit is the outcome of running `x fmt --all` with the new formatting options.
2024-06-18tail expression behind terminating scopeDing Xiang Fei-2/+8
2024-06-07Revert "Create const block DefIds in typeck instead of ast lowering"Oli Scherer-5/+2
This reverts commit ddc5f9b6c1f21da5d4596bf7980185a00984ac42.
2024-05-29Make `body_owned_by` return the body directly.Oli Scherer-3/+2
Almost all callers want this anyway, and now we can use it to also return fed bodies
2024-05-29Don't require `visit_body` to take a lifetime that must outlive the function ↵Oli Scherer-1/+1
call
2024-05-28Create const block DefIds in typeck instead of ast loweringOli Scherer-2/+5
2024-05-28Make body-visiting logic reusableOli Scherer-53/+59
2024-05-15Fix more new for_loops_over_fallibles hits in compiler.Zachary S-2/+2
2024-05-13Remove `extern crate rustc_middle` from `rustc_hir_analysis`.Nicholas Nethercote-0/+1
2024-04-17Rename `BindingAnnotation` to `BindingMode`Jules Bertholet-2/+2