Dumb NRVO
This is a very simple version of an NRVO pass, which scans backwards from the `return` terminator to see if there is an an assignment like `_0 = _1`. If a basic block with two or more predecessors is encountered during this scan without first seeing an assignment to the return place, we bail out. This avoids running a full "reaching definitions" dataflow analysis.
I wanted to see how much `rustc` would benefit from even a very limited version of this optimization. We should be able to use this as a point of comparison for more advanced versions that are based on live ranges.
r? @ghost
I also added test cases to make sure the optimization can fire on all of
these cases:
```rust
fn case_1(o: Option<u8>) -> Option<u8> {
match o {
Some(u) => Some(u),
None => None,
}
}
fn case2(r: Result<u8, i32>) -> Result<u8, i32> {
match r {
Ok(u) => Ok(u),
Err(i) => Err(i),
}
}
fn case3(r: Result<u8, i32>) -> Result<u8, i32> {
let u = r?;
Ok(u)
}
```
Without MIR inlining, this still does not completely optimize away the
`?` operator because the `Try::into_result()`, `From::from()` and
`Try::from_error()` calls still exist. This does move us a bit closer to
that goal though because:
- We can now run the pass on mir-opt-level=1
- We no longer depend on the copy propagation pass running which is
unlikely to stabilize anytime soon.
Const prop aggregates even if partially or fully modified
r? @wesleywiser
cc @rust-lang/wg-mir-opt I'm moderately scared of this change, but I'm confident in having reviewed all the cases.
Added MIR constant propagation of Scalars into function call arguments
Now for the function call arguments!
Caveats:
1. It's only being enabled at `mir-opt-2` or higher, because currently codegen gives performance regressions with this optimization.
2. Only propagates Scalars. Tuples and references (references are `Indirect`, right??) are not being propagated into as of this PR.
3. Maybe more tests would be nice?
4. I need (shamefully) to ask @wesleywiser to write in his words (or explain to me, and then I can write it down) why we want to ignore propagation into `ScalarPairs` and `Indirect` arguments.
r? @wesleywiser
MIR dump: print pointers consistently with Miri output
This makes MIR allocation dump pointer printing consistent with Miri output: both use hexadecimal offsets with a `0x` prefix. To save some space, MIR dump replaces the `alloc` prefix by `a` when necessary.
I also made AllocId/Pointer printing more consistent in their Debug/Display handling, and adjusted Display printing for Scalar a bit to avoid using decimal printing when we do not know the sign with which to interpret the value (IMO using decimal then is misleading).
extend NLL checker to understand `'empty` combined with universes
This PR extends the NLL region checker to understand `'empty` combined with universes. In particular, it means that the NLL region checker no longer considers `exists<R2> { forall<R1> { R1: R2 } }` to be provable. This is work towards https://github.com/rust-lang/rust/issues/59490, but we're not all the way there. One thing in particular it does not address is error messages.
The modifications to the NLL region inference code turned out to be simpler than expected. The main change is to require that if `R1: R2` then `universe(R1) <= universe(R2)`.
This constraint follows from the region lattice (shown below), because we assume then that `R2` is "at least" `empty(Universe(R2))`, and hence if `R1: R2` (i.e., `R1 >= R2` on the lattice) then `R1` must be in some universe that can name `'empty(Universe(R2))`, which requires that `Universe(R1) <= Universe(R2)`.
```
static ----------+-----...------+ (greatest)
| | |
early-bound and | |
free regions | |
| | |
scope regions | |
| | |
empty(root) placeholder(U1) |
| / |
| / placeholder(Un)
empty(U1) -- /
| /
... /
| /
empty(Un) -------- (smallest)
```
I also made what turned out to be a somewhat unrelated change to add a special region to represent `'empty(U0)`, which we use (somewhat hackily) to indicate well-formedness checks in some parts of the compiler. This fixes#68550.
I did some investigation into fixing the error message situation. That's a bit trickier: the existing "nice region error" code around placeholders relies on having better error tracing than NLL currently provides, so that it knows (e.g.) that the constraint arose from applying a trait impl and things like that. I feel like I was hoping *not* to do such fine-grained tracing in NLL, and it seems like we...largely...got away with that. I'm not sure yet if we'll have to add more tracing information or if there is some sort of alternative.
It's worth pointing out though that I've not kind of shifted my opinion on whose job it should be to enforce lifetimes: I tend to think we ought to be moving back towards *something like* the leak-check (just not the one we *had*). If we took that approach, it would actually resolve this aspect of the error message problem, because we would be resolving 'higher-ranked errors' in the trait solver itself, and hence we wouldn't have to thread as much causal information back to the region checker. I think it would also help us with removing the leak check while not breaking some of the existing crates out there.
Regardless, I think it's worth landing this change, because it was relatively simple and it aligns the set of programs that NLL accepts with those that are accepted by the main region checker, and hence should at least *help* us in migration (though I guess we still also have to resolve the existing crates that rely on leak check for coherence).
r? @matthewjasper
Remove -Z no-landing-pads flag
Since #67502, `-Z no-landing-pads` will cause all attempted unwinds to abort since we don't generate a `try` / `catch`. This previously worked because `__rust_try` was located in libpanic_unwind which is always compiled with `-C panic=unwind`, but `__rust_try` is now directly inline into the crate that uses `catch_unwind`.
As such, `-Z no-landing-pads` is now mostly useless and people should use `-C panic=abort` instead.
Fix span of while (let) expressions after lowering
Credit goes to @alex-700 who found this while trying to fix a suggestion in Clippy.
While `if`, `try`, `for` and `await` expressions get the span of the original expression when desugared, `while` loops got the span of the scrutinee, which lead to weird code, when building the suggestion, that randomly worked: https://github.com/rust-lang/rust-clippy/pull/5511/files#diff-df4e9d2bf840a5f2e3b580bef73da3bcR106-R108
I'm wondering, if `DesugaringKind` should get a variant `WhileLoop` and instead of using the span of the `ast::ExprKind::While` expr directly, a new span with `self.mark_span_with_reason` should be used, like it is done with `for` loops.
There was some fallout, but I think that is acceptable. If not, I need some help to find out where this can be fixed.
Add leading 0x to offset in Debug fmt of Pointer
Currently the `Debug` format for `Pointer` prints its offset in hexadecimal, for example, `alloc38657819+e2` or `alloc35122748+64`. This PR adds a leading `0x` to the offset, in order to make it apparent that it is indeed a hexadecimal number. This came up during discussion of rust-lang/miri#1354. r? @RalfJung