borrowck diagnostics: suggest borrowing function inputs in generic positions
# Summary
This generalizes borrowck's existing suggestions to borrow instead of moving when passing by-value to a function that's generic in that input. Previously, this was special-cased to `AsRef`/`Borrow`-like traits and `Fn`-like traits. This PR changes it to test if, for a moved place with type `T`, that the callee's signature and clauses don't break if you substitute in `&T` or `&mut T`. For instance, it now works with `Read`/`Write`-like traits.
Fixes https://github.com/rust-lang/rust/issues/131413
# Incidental changes
- No longer spuriously suggests mutable borrows of closures in some situations (see e.g. the tests in [tests/ui/closures/2229_closure_analysis/](https://github.com/rust-lang/rust/compare/master...dianne:rust:suggest-borrow-generic?expand=1#diff-8dfb200c559f0995d0f2ffa2f23bc6f8041b263e264e5c329a1f4171769787c0)).
- No longer suggests cloning closures that implement `Fn`, since they can be borrowed (see e.g. [tests/ui/moves/borrow-closures-instead-of-move.stderr](https://github.com/rust-lang/rust/compare/master...dianne:rust:suggest-borrow-generic?expand=1#diff-5db268aac405eec56d099a72d8b58ac46dab523cf013e29008104840168577fb)).
This keeps the behavior to suppress suggestions of `fn_once.clone()()`. I think it might make sense to suggest it with a "but this might not be your desired behavior" caveat, as is done when something is used after being consumed as the receiver for a method call. That's probably out of the scope of this PR though.
# Limitations and possible improvements
- This doesn't work for receivers of method calls. This is a small change, and I have it implemented locally, but I'm not sure it's useful on its own. In most cases I've found borrowing the receiver would change the call's output type (see below). In other cases (e.g. `Iterator::sum`), borrowing the receiver isn't useful since it's consumed.
- This doesn't work when it would change the call's output type. In general, I figure inserting references into the output type is an unwanted change. However, this also means it doesn't work in cases where the new output type would be effectively the same as the old one. For example, from the rand crate, the iterator returned by [`Rng::sample_iter`](https://docs.rs/rand/latest/rand/trait.Rng.html#method.sample_iter) is effectively the same (modulo regions) whether you borrow or consume the receiver `Rng`, so common usage involves borrowing it. I'm not sure whether the best approach is to add a more complex check of approximate equivalence, to forego checking the call's output type and give spurious suggestions, or to leave it as-is.
- This doesn't work when it would change the call's other input types. Instead, it could suggest borrowing any others that have the same parameter type (but only when suggesting shared borrows). I think this would be a pretty easy change, but I don't think it's very useful so long as the normalized output type can't change.
I'm happy to implement any of these (or other potential improvements to this), but I'm not sure which are common enough patterns to justify the added complexity. Please let me know if any sound worthwhile.
Rollup of 5 pull requests
Successful merges:
- #132010 (ci: Enable full `debuginfo-level=2` in `DEPLOY_ALT`)
- #132310 (compiletest: add `max-llvm-major-version` directive)
- #132773 (PassWrapper: disable UseOdrIndicator for Asan Win32)
- #133013 (compiletest: known-bug / crashes: allow for an "auxiliary" directory to contain files that do not have a "known-bug" directive)
- #133027 (Fix a copy-paste issue in the NuttX raw type definition)
r? `@ghost`
`@rustbot` modify labels: rollup
Fix a copy-paste issue in the NuttX raw type definition
This file is copied from the rtems as initial implementation, and forgot to change the OS name in the comment.
compiletest: known-bug / crashes: allow for an "auxiliary" directory to contain files that do not have a "known-bug" directive
Fixes#133009
r? `@jieyouxu`
PassWrapper: disable UseOdrIndicator for Asan Win32
As described in https://reviews.llvm.org/D137227 UseOdrIndicator should be disabled on Windows since link.exe does not support duplicate weak definitions.
Fixes https://github.com/rust-lang/rust/issues/124390.
Credits also belong to `@1c3t3a` who worked with me on this.
We are currently testing this on a Windows machine.
compiletest: add `max-llvm-major-version` directive
To complement existing `min-llvm-version` so contributors don't have to use `ignore-llvm-version: 20 - 99` to emulate `max-llvm-major-version: 19`.
Closes#132305.
cc `@workingjubilee` who suggested this.
### Implementation steps
- [x] 1. Implement the directive (this PR)
- [x] 2. Open an accompanying dev-guide PR to describe the directive (https://github.com/rust-lang/rustc-dev-guide/pull/2129)
r? bootstrap
ci: Enable full `debuginfo-level=2` in `DEPLOY_ALT`
It will be slower to build and produce larger artifacts, but hopefully
it will help catch debuginfo regressions sooner, especially for problems
that LLVM assertions would uncover.
try-job: dist-x86_64-linux
try-job: dist-x86_64-linux-alt
Rollup of 7 pull requests
Successful merges:
- #131304 (float types: move copysign, abs, signum to libcore)
- #132907 (Change intrinsic declarations to new style)
- #132971 (Handle infer vars in anon consts on stable)
- #133003 (Make `CloneToUninit` dyn-compatible)
- #133004 (btree: simplify the backdoor between set and map)
- #133008 (update outdated comment about test-float-parse)
- #133012 (Add test cases for #125918)
r? `@ghost`
`@rustbot` modify labels: rollup
This file is copied from the rtems as initial implementation, and
forgot to change the OS name in the comment.
Signed-off-by: Huang Qi <huangqi3@xiaomi.com>
btree: simplify the backdoor between set and map
The internal `btree::Recover` trait acted as a private API between
`BTreeSet` and `BTreeMap`, but we can use `pub(_)` restrictions these
days, and some of the methods don't need special handling anymore.
* `BTreeSet::get` can use `BTreeMap::get_key_value`
* `BTreeSet::take` can use `BTreeMap::remove_entry`
* `BTreeSet::replace` does need help, but this now uses a `pub(super)`
method on `BTreeMap` instead of the trait.
* `btree::Recover` is now removed.
Make `CloneToUninit` dyn-compatible
Make `CloneToUninit` dyn-compatible, by making `clone_to_uninit`'s `dst` parameter `*mut u8` instead of `*mut Self`, so the method does not reference `Self` except in the `self` parameter and is thus dispatchable from a trait object.
This allows, among other things, adding `CloneToUninit` as a supertrait bound for `trait Foo` to allow cloning `dyn Foo` in some containers. Currently, this means that `Rc::make_mut` and `Arc::make_mut` can work with `dyn Foo` where `trait Foo: CloneToUninit`.
<details><summary>Example</summary>
```rs
#![feature(clone_to_uninit)]
use std::clone::CloneToUninit;
use std::rc::Rc;
use std::fmt::Debug;
use std::borrow::BorrowMut;
trait Foo: BorrowMut<u32> + CloneToUninit + Debug {}
impl<T: BorrowMut<u32> + CloneToUninit + Debug> Foo for T {}
fn main() {
let foo: Rc<dyn Foo> = Rc::new(42_u32);
let mut bar = foo.clone();
*Rc::make_mut(&mut bar).borrow_mut() = 37;
dbg!(foo, bar); // 42, 37
}
```
</details>
Eventually, `Box::<T>::clone` is planned to be converted to use `T::clone_to_uninit`, which when combined with this change, will allow cloning `Box<dyn Foo>` where `trait Foo: CloneToUninit` without any additional `unsafe` code for the author of `trait Foo`.[^1]
This PR should have no stable side-effects, as `CloneToUninit` is unstable so cannot be mentioned on stable, and `CloneToUninit` is not used as a supertrait anywhere in the stdlib.
This change removes some length checks that could only fail if library UB was already hit (e.g. calling `<[T]>::clone_to_uninit` with a too-small-length `dst` is library UB and was previously detected[^2]; since `dst` does not have a length anymore, this now cannot be detected[^3]).
r? libs-api
-----
I chose to make the parameter `*mut u8` instead of `*mut ()` because that might make it simpler to pass the result of `alloc` to `clone_to_uninit`, but `*mut ()` would also make sense, and any `*mut ConcreteType` would *work*. The original motivation for [using specifically `*mut ()`](https://github.com/rust-lang/rust/pull/116113#discussion_r1335303908) appears to be `std::ptr::from_raw_parts_mut`, but that now [takes `*mut impl Thin`](https://doc.rust-lang.org/nightly/std/ptr/fn.from_raw_parts.html) instead of `*mut ()`. I have another branch where the parameter is `*mut ()`, if that is preferred.
It *could* also take something like `&mut [MaybeUninit<u8>]` to be dyn-compatible but still allow size-checking and in some cases safe writing, but this is already an `unsafe` API where misuse is UB, so I'm not sure how many guardrails it's worth adding here, and `&mut [MaybeUninit<u8>]` might be overly cumbersome to construct for callers compared to `*mut u8`
[^1]: Note that `impl<T: CloneToUninit + ?Sized> Clone for Box` must be added before or at the same time as when `CloneToUninit` becomes stable, due to `Box` being `#[fundamental]`, as if there is any stable gap between the stabilization of `CloneToUninit` and `impl<T: CloneToUninit + ?Sized> Clone for Box`, then users could implement both `CloneToUninit for dyn LocalTrait` and separately `Clone for Box<dyn LocalTrait>` during that gap, and be broken by the introduction of `impl<T: CloneToUninit + ?Sized> Clone for Box`.
[^2]: Using a `debug_assert_eq` in [`core::clone::uninit::CopySpec::clone_slice`](https://doc.rust-lang.org/nightly/src/core/clone/uninit.rs.html#28).
[^3]: This PR just uses [the metadata (length) from `self`](e0c1c8bc50/library/core/src/clone.rs (L286)) to construct the `*mut [T]` to pass to `CopySpec::clone_slice` in `<[T]>::clone_to_uninit`.
Handle infer vars in anon consts on stable
Fixes#132955
Diagnostics will sometimes try to replace generic parameters with inference variables in failing goals. This means that if we have some failing goal with an array repeat expr count anon const in it, we will wind up with some `ty::ConstKind::Unevaluated(anon_const_def, [?x])` during diagnostics which will then ICE if we do not handle inference variables correctly on stable when normalizing type system consts.
r? ```@compiler-errors```
Change intrinsic declarations to new style
Pr is for issue #132735
This changes the first `extern "rust-intrinsic"` block to the new style.
r? `@RalfJung`
float types: move copysign, abs, signum to libcore
These operations are explicitly specified to act "bitwise", i.e. they just act on the sign bit and do not even quiet signaling NaNs. We also list them as ["non-arithmetic operations"](https://doc.rust-lang.org/nightly/std/primitive.f32.html#nan-bit-patterns), and all the other non-arithmetic operations are in libcore. There's no reason to expect them to require any sort of runtime support, and from [these experiments](https://github.com/rust-lang/rust/issues/50145#issuecomment-997301250) it seems like LLVM indeed compiles them in a way that does not require any sort of runtime support.
Nominating for `@rust-lang/libs-api` since this change takes immediate effect on stable.
Part of https://github.com/rust-lang/rust/issues/50145.
This subsumes the suggestions to borrow arguments with `AsRef`/`Borrow` bounds and those to borrow
arguments with `Fn` and `FnMut` bounds. It works for other traits implemented on references as well,
such as `std::io::Read`, `std::io::Write`, and `core::fmt::Write`.
Incidentally, by making the logic for suggesting borrowing closures general, this removes some
spurious suggestions to mutably borrow `FnMut` closures in assignments, as well as an unhelpful
suggestion to add a `Clone` constraint to an `impl Fn` argument.
improve codegen of fmt_num to delete unreachable panic
it seems LLVM doesn't realize that `curr` is always decremented at least once in either loop formatting characters of the input string by their appropriate radix, and so the later `&buf[curr..]` generates a check for out-of-bounds access and panic. this is unreachable in reality as even for `x == T::zero()` we'll produce at least the character `Self::digit(T::zero())`, yielding at least one character output, and `curr` will always be at least one below `buf.len()`.
adjust `fmt_int` to make this fact more obvious to the compiler, which fortunately (or unfortunately) results in a measurable performance improvement for workloads heavy on formatting integers.
in the program i'd noticed this in, you can see the `cmp $0x80,%rdi; ja 7c` here, which branches to a slice index fail helper:
<img width="660" alt="before" src="https://github.com/rust-lang/rust/assets/4615790/ac482d54-21f8-494b-9c83-4beadc3ca0ef">
where after this change the function is broadly similar, but smaller, with one fewer registers updated in each pass through the loop in addition the never-taken `cmp/ja` being gone:
<img width="646" alt="after" src="https://github.com/rust-lang/rust/assets/4615790/1bee1d76-b674-43ec-9b21-4587364563aa">
this represents a ~2-3% difference in runtime in my [admittedly comically i32-formatting-bound](https://github.com/athre0z/disas-bench/blob/master/bench/yaxpeax/src/main.rs#L58-L67) use case (printing x86 instructions, including i32 displacements and immediates) as measured on a ryzen 9 3950x.
the impact on `<impl LowerHex for i8>::fmt` is both more dramatic and less impactful: it continues to have a loop that is evaluated at most twice, though the compiler doesn't know that to unroll it. the generated code there is identical to the impl for `i32`. there, the smaller loop body has less effect on runtime, and removing the never-taken slice bounds check is offset by whatever address recalculation is happening with the `lea/add/neg` at the end of the loop. it behaves about the same before and after.
---
i initially measured slightly better outcomes using `unreachable_unchecked()` here instead, but that was hacking on std and rebuilding with `-Z build-std` on an older rustc (nightly 5b377cece, 2023-06-30). it does not yield better outcomes now, so i see no reason to proceed with that approach at all.
<details>
<summary>initial notes about that, seemingly irrelevant on modern rustc</summary>
i went through a few tries at getting llvm to understand the bounds check isn't necessary, but i should mention the _best_ i'd seen here was actually from the existing `fmt_int` with a diff like
```diff
if x == zero {
// No more digits left to accumulate.
break;
};
}
}
+
+ if curr >= buf.len() {
+ unsafe { core::hint::unreachable_unchecked(); }
+ }
let buf = &buf[curr..];
```
posting a random PR to `rust-lang/rust` to do that without a really really compelling reason seemed a bit absurd, so i tried to work that into something that seems more palatable at a glance. but if you're interested, that certainly produced better (x86_64) code through LLVM. in that case with `buf.iter_mut().rev()` as the iterator, `<impl LowerHex for i8>::fmt` actually unrolls into something like
```
put_char(x & 0xf);
let mut len = 1;
if x > 0xf {
put_char((x >> 4) & 0xf);
len = 2;
}
pad_integral(buf[buf.len() - len..]);
```
it's pretty cool! `<impl LowerHex for i32>::fmt` also was slightly better. that all resulted in closer to an 6% difference in my use case.
</details>
---
i have not looked at formatters other than LowerHex/UpperHex with this change, though i'd be a bit shocked if any were _worse_.
(i have absolutely _no_ idea how you'd regression test this, but that might be just my not knowing what the right tool for that would be in rust-lang/rust. i'm of half a mind that this is small and fiddly enough to not be worth landing lest it quietly regress in the future anyway. but i didn't want to discard the idea without at least offering it upstream here)
Important: we know from the `parse_annotatable_with` call above the call
site that only some of the `Annotatable` variants are possible. The
remaining cases can be replaced with `unreachable!`.
They each have a single callsite, and the result is always unwrapped, so
the `Option<Annotatable>` return type is misleading.
Also, the comment at the `configure_annotatable` call site is wrong,
talking about a result vector, so this commit also removes that.
This is setup for unifing the logic for suggestions to borrow arguments in generic positions.
As of this commit, it's still special cases for `AsRef`/`Borrow`-like traits and `Fn`-like traits.
This also downgrades its applicability to MaybeIncorrect. Its suggestion can result in ill-typed
code when the type parameter it suggests providing a different generic argument for appears
elsewhere in the callee's signature or predicates.
As described here UseOdrIndicator should be disabled on Windows
since link.exe does not support duplicate weak definitions
(https://reviews.llvm.org/D137227).
Co-Authored-By: Bastian Kersting <bkersting@google.com>