Enforce tail call type is related to body return type in borrowck
Like all call terminators, tail call terminators instantiate the binder of the callee signature with region variables and equate the arg operand types with that signature's args to ensure that the call is valid.
However, unlike normal call terminators, we were forgetting to also relate the return type of the call terminator to anything. In the case of tail call terminators, the correct thing is to relate it to the return type of the caller function (or in other words, the return local `_0`).
This meant that if the caller's return type had some lifetime constraint, then that constraint wouldn't flow through the signature and affect the args.
This is what's happening in the example test I committed:
```rust
fn link(x: &str) -> &'static str {
become passthrough(x);
}
fn passthrough<T>(t: T) -> T { t }
fn main() {
let x = String::from("hello, world");
let s = link(&x);
drop(x);
println!("{s}");
}
```
Specifically, the type `x` is `'?0 str`, where `'?0` is some *universal* arg. The type of `passthrough` is `fn(&'?1 str) -> &'?1 str`. Equating the args sets `'?0 = '?1`. However, we need to also equate the return type `&'?1 str` to `&'static str` so that we eventually require that `'?0 = 'static`, which is a borrowck error!
-----
Look at the first commit for the functional change, and the second commit is just a refactor because we don't need to pass `Option<BasicBlock>` to `check_call_dest`, but just whether or not the terminator is expected to be diverging (i.e. if the return type is `!`).
Fixesrust-lang/rust#144916
Currently they are mostly named `cx`, which is a terrible name for a
type that impls `Printer`/`PrettyPrinter`, and is easy to confuse with
other types like `TyCtxt`. This commit changes them to `p`. A couple of
existing `p` variables had to be renamed to make way.
Improve formatting of doc code blocks
We don't currently apply automatic formatting to doc comment code blocks. As a
result, it has built up various idiosyncracies, which make such automatic
formatting difficult. Some of those idiosyncracies also make things harder for
human readers or other tools.
This PR makes a few improvements to doc code formatting, in the hopes of making
future automatic formatting easier, as well as in many cases providing net
readability improvements.
I would suggest reading each commit separately, as each commit contains one
class of changes.
Remove the witness type from coroutine *args* (without actually removing the type)
This does as much of rust-lang/rust#144157 as we can without having to break rust-lang/rust#143545 and/or introduce some better way of handling higher ranked assumptions.
Namely, it:
* Stalls coroutines based off of the *coroutine* type rather than the witness type.
* Reworks the dtorck constraint hack to not rely on the witness type.
* Removes the witness type from the args of the coroutine, eagerly creating the type for nested obligations when needed (auto/clone impls).
I'll experiment with actually removing the witness type in a follow-up.
r? lcnr
This commit changes it to store a `Region` instead of a `RegionVid` for the `Var` cases:
- We avoid having to call `Region::new_var` to re-create `Region`s from
`RegionVid`s in a few places, avoiding the interning process, giving a
small perf win. (At the cost of the type allowing some invalid
combinations of values.)
- All the cases now store two `Region`s, so the commit also separates
the `ConstraintKind` (a new type) from the `sub` and `sup` arguments
in `Constraint`.
Currently there is `Ty` and `BoundTy`, and `Region` and `BoundRegion`,
and `Const` and... `BoundVar`. An annoying inconsistency.
This commit repurposes the existing `BoundConst`, which was barely used,
so it's the partner to `Const`. Unlike `BoundTy`/`BoundRegion` it lacks
a `kind` field but it's still nice to have because it makes the const
code more similar to the ty/region code everywhere.
The commit also removes `impl From<BoundVar> for BoundTy`, which has a
single use and doesn't seem worth it.
These changes fix the "FIXME: We really should have a separate
`BoundConst` for consts".
Make sure to account for the right item universal regions in borrowck
Fixes https://github.com/rust-lang/rust/issues/144608.
The ICE comes from a mismatch between the liberated late bound regions (i.e. "`ReLateParam`"s) that come from promoting closure outlives, and the regions we have in our region vid mapping from `UniversalRegions`.
When building `UniversalRegions`, we end up using the liberated regions from the binder of the closure's signature:
c8bb4e8a12/compiler/rustc_borrowck/src/universal_regions.rs (L521)
Notably, this signature may be anonymized if the closure signature being deduced comes from an external constraints:
c8bb4e8a12/compiler/rustc_hir_typeck/src/closure.rs (L759-L762)
This is true in the test file I committed, where the signature is influenced by the `impl FnMut(&mut ())` RPIT.
However, when promoting a type outlives constraint we end up creating a late bound lifetime mapping that disagrees with those liberated late bound regions we constructed in `UniversalRegions`:
c8bb4e8a12/compiler/rustc_borrowck/src/universal_regions.rs (L299)
Specifically, in `for_each_late_bound_region_in_item` (which is called by `for_each_late_bound_region_in_recursive_scope`), we were using `tcx.late_bound_vars` which uses the late bound regions *from the HIR*. This query both undercounts the late bound regions (e.g. those that end up being deduced from bounds), and also doesn't account for the fact that we anonymize them in the signature as mentioned above.
c8bb4e8a12/compiler/rustc_borrowck/src/universal_regions.rs (L977)
This PR fixes that function to use the *correct signature*, which properly considers the bound vars that come from deducing the signature of the closure, and which comes from the closure's args from the `type_of` query.
These examples feature Rust code that's presented primarily to
illustrate how its compilation would be handled, and these examples are
formatted to highlight those aspects in ways that rustfmt wouldn't
preserve. Turn formatting off in those examples.
(Doc code isn't formatted yet, but this will make it easier to enable
doc code formatting in the future.)
When encountering a moved value of a type that isn't `Clone` because of unmet obligations, but where all the unmet predicates reference crate-local types, mention them and suggest cloning, as we do in other cases already:
```
error[E0507]: cannot move out of `foo`, a captured variable in an `Fn` closure
--> f111.rs:14:25
|
13 | fn do_stuff(foo: Option<Foo>) {
| --- captured outer variable
14 | require_fn_trait(|| async {
| -- ^^^^^ `foo` is moved here
| |
| captured by this `Fn` closure
15 | if foo.map_or(false, |f| f.foo()) {
| ---
| |
| variable moved due to use in coroutine
| move occurs because `foo` has type `Option<Foo>`, which does not implement the `Copy` trait
|
note: if `Foo` implemented `Clone`, you could clone the value
--> f111.rs:4:1
|
4 | struct Foo;
| ^^^^^^^^^^ consider implementing `Clone` for this type
...
15 | if foo.map_or(false, |f| f.foo()) {
| --- you could clone this value
```
Tweak output for non-`Clone` values moved into closures
When we encounter a non-`Clone` value being moved into a closure, try to find the corresponding type of the binding being moved, if it is a `let`-binding or a function parameter. If any of those cases, we point at them with the note explaining that the type is not `Copy`, instead of giving that label to the place where it is captured. When it is a `let`-binding with no explicit type, we point at the initializer (if it fits in a single line).
```
error[E0507]: cannot move out of `foo`, a captured variable in an `Fn` closure
--> f111.rs:14:25
|
13 | fn do_stuff(foo: Option<Foo>) {
| --- ----------- move occurs because `foo` has type `Option<Foo>`, which does not implement the `Copy` trait
| |
| captured outer variable
14 | require_fn_trait(|| async {
| -- ^^^^^ `foo` is moved here
| |
| captured by this `Fn` closure
15 | if foo.map_or(false, |f| f.foo()) {
| --- variable moved due to use in coroutine
```
instead of
```
error[E0507]: cannot move out of `foo`, a captured variable in an `Fn` closure
--> f111.rs:14:25
|
13 | fn do_stuff(foo: Option<Foo>) {
| --- captured outer variable
14 | require_fn_trait(|| async {
| -- ^^^^^ `foo` is moved here
| |
| captured by this `Fn` closure
15 | if foo.map_or(false, |f| f.foo()) {
| ---
| |
| variable moved due to use in coroutine
| move occurs because `foo` has type `Option<Foo>`, which does not implement the `Copy` trait
```
```
error[E0507]: cannot move out of `f`, a captured variable in an `FnMut` closure
--> $DIR/borrowck-call-is-borrow-issue-12224.rs:57:13
|
LL | let mut f = move |g: Box<dyn FnMut(isize)>, b: isize| {
| ----- captured outer variable
...
LL | f(Box::new(|a| {
| --- captured by this `FnMut` closure
LL |
LL | foo(f);
| ^ move occurs because `f` has type `{closure@$DIR/borrowck-call-is-borrow-issue-12224.rs:52:17: 52:58}`, which does not implement the `Copy` trait
```
instead of
```
error[E0507]: cannot move out of `f`, a captured variable in an `FnMut` closure
--> $DIR/borrowck-call-is-borrow-issue-12224.rs:57:13
|
LL | let mut f = move |g: Box<dyn FnMut(isize)>, b: isize| {
| _________-----___-
| | |
| | captured outer variable
LL | | let _ = s.len();
LL | | };
| |_____- move occurs because `f` has type `{closure@$DIR/borrowck-call-is-borrow-issue-12224.rs:52:17: 52:58}`, which does not implement the `Copy` trait
LL | f(Box::new(|a| {
| --- captured by this `FnMut` closure
LL |
LL | foo(f);
| ^ `f` is moved here
```
Account not only for `fn` parameters when moving non-`Copy` values into closure, but also for let bindings.
```
error[E0507]: cannot move out of `bar`, a captured variable in an `FnMut` closure
--> $DIR/borrowck-move-by-capture.rs:9:29
|
LL | let bar: Box<_> = Box::new(3);
| --- ------ move occurs because `bar` has type `Box<isize>`, which does not implement the `Copy` trait
| |
| captured outer variable
LL | let _g = to_fn_mut(|| {
| -- captured by this `FnMut` closure
LL | let _h = to_fn_once(move || -> isize { *bar });
| ^^^^^^^^^^^^^^^^ ---- variable moved due to use in closure
| |
| `bar` is moved here
|
help: consider cloning the value before moving it into the closure
|
LL ~ let value = bar.clone();
LL ~ let _h = to_fn_once(move || -> isize { value });
|
```
```
error[E0507]: cannot move out of `y`, a captured variable in an `Fn` closure
--> $DIR/unboxed-closures-move-upvar-from-non-once-ref-closure.rs:12:9
|
LL | let y = vec![format!("World")];
| - ---------------------- move occurs because `y` has type `Vec<String>`, which does not implement the `Copy` trait
| |
| captured outer variable
LL | call(|| {
| -- captured by this `Fn` closure
LL | y.into_iter();
| ^ ----------- `y` moved due to this method call
| |
| `y` is moved here
|
note: `into_iter` takes ownership of the receiver `self`, which moves `y`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
help: you can `clone` the value and consume it, but this might not be your desired behavior
|
LL | <Vec<String> as Clone>::clone(&y).into_iter();
| +++++++++++++++++++++++++++++++ +
help: consider cloning the value if the performance cost is acceptable
|
LL | y.clone().into_iter();
| ++++++++
```
When encountering a non-`Copy` value that is moved into a closure which is coming directly from a fn parameter, point at the parameter's type when mentioning it is not `Copy`.
Before:
```
error[E0507]: cannot move out of `foo`, a captured variable in an `Fn` closure
--> f111.rs:14:25
|
13 | fn do_stuff(foo: Option<Foo>) {
| --- captured outer variable
14 | require_fn_trait(|| async {
| -- ^^^^^ `foo` is moved here
| |
| captured by this `Fn` closure
15 | if foo.map_or(false, |f| f.foo()) {
| ---
| |
| variable moved due to use in coroutine
| move occurs because `foo` has type `Option<Foo>`, which does not implement the `Copy` trait
```
After:
```
error[E0507]: cannot move out of `foo`, a captured variable in an `Fn` closure
--> f111.rs:14:25
|
13 | fn do_stuff(foo: Option<Foo>) {
| --- ----------- move occurs because `foo` has type `Option<Foo>`, which does not implement the `Copy` trait
| |
| captured outer variable
14 | require_fn_trait(|| async {
| -- ^^^^^ `foo` is moved here
| |
| captured by this `Fn` closure
15 | if foo.map_or(false, |f| f.foo()) {
| --- variable moved due to use in coroutine
```
use `is_multiple_of` and `div_ceil`
In tricky logic, these functions are much more informative than the manual implementations. They also catch subtle bugs:
- the manual `is_multiple_of` often does not handle division by zero
- manual `div_ceil` often does not consider overflow
The transformation is free for `is_multiple_of` if the divisor is compile-time known to be non-zero. For `div_ceil` there is a small cost to considering overflow. Here is some assembly https://godbolt.org/z/5zP8KaE1d.
Remove support for `dyn*` from the compiler
This PR removes support for `dyn*` (https://github.com/rust-lang/rust/issues/102425), which are a currently un-RFC'd experiment that was opened a few years ago to explore a component that we thought was necessary for AFIDT (async fn in dyn trait).
It doesn't seem like we are going to need `dyn*` types -- even in an not-exposed-to-the-user way[^1] -- for us to implement AFIDT. Given that AFIDT was the original motivating purpose of `dyn*` types, I don't really see a compelling reason to have to maintain their implementation in the compiler.
[^1]: Compared to, e.g., generators whih are an unstable building block we use to implement stable syntax like `async {}`.
We've learned quite a lot from `dyn*`, but I think at this point its current behavior leads to more questions than answers. For example, `dyn*` support today remains somewhat fragile; it ICEs in many cases where the current "normal" `dyn Trait` types rely on their unsizedness for their vtable-based implementation to be sound I wouldn't be surprised if it's unsound in other ways, though I didn't play around with it too much. See the examples below.
```rust
#![feature(dyn_star)]
trait Foo {
fn hello(self);
}
impl Foo for usize {
fn hello(self) {
println!("hello, world");
}
}
fn main() {
let x: dyn* Foo = 1usize;
x.hello();
}
```
And:
```rust
#![feature(dyn_star)]
trait Trait {
type Out where Self: Sized;
}
fn main() {
let x: <dyn* Trait as Trait>::Out;
}
```
...and probably many more problems having to do with the intersection of dyn-compatibility and `Self: Sized` bounds that I was too lazy to look into like:
* GATs
* Methods with invalid signatures
* Associated consts
Generally, `dyn*` types also end up getting in the way of working with [normal `dyn` types](https://github.com/rust-lang/rust/issues/102425#issuecomment-1712604409) to an extent that IMO outweighs the benefit of experimentation.
I recognize that there are probably other, more creative usages of `dyn*` that are orthogonal to AFIDT. However, I think any work along those lines should first have to think through some of the more fundamental interactions between `dyn*` and dyn-compatibility before we think about reimplementing them in the type system.
---
I'm planning on removing the `DynKind` enum and the `PointerLike` built-in trait from the compiler after this PR lands.
Closesrust-lang/rust#102425.
cc `@eholk` `@rust-lang/lang` `@rust-lang/types`
Closesrust-lang/rust#116979.
Closesrust-lang/rust#119694.
Closesrust-lang/rust#134591.
Closesrust-lang/rust#104800.
Remove some glob imports from the type system
Namely, remove the glob imports for `BoundRegionConversionTime`, `RegionVariableOrigin`, `SubregionOrigin`, `TyOrConstInferVar`, `RegionResolutionError`, `SelectionError`, `ProjectionCandidate`, `ProjectionCandidateSet`, and some more specific scoped globs (like `Inserted` in the impl overlap graph construction.
These glob imports are IMO very low value, since they're not used nearly as often as other globs (like `TyKind`).
Suggest cloning `Arc` moved into closure
```
error[E0382]: borrow of moved value: `x`
--> $DIR/moves-based-on-type-capture-clause-bad.rs:9:20
|
LL | let x = "Hello world!".to_string();
| - move occurs because `x` has type `String`, which does not implement the `Copy` trait
LL | thread::spawn(move || {
| ------- value moved into closure here
LL | println!("{}", x);
| - variable moved due to use in closure
LL | });
LL | println!("{}", x);
| ^ value borrowed here after move
|
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider cloning the value before moving it into the closure
|
LL ~ let value = x.clone();
LL ~ thread::spawn(move || {
LL ~ println!("{}", value);
|
```
Fixrust-lang/rust#104232.
Add runtime check to avoid overwrite arg in `Diag`
## Origin PR description
At first, I set up a `debug_assert` check for the arg method to make sure that `args` in `Diag` aren't easily overwritten, and I added the `remove_arg()` method, so that if you do need to overwrite an arg, then you can explicitly call `remove_arg()` to remove it first, then call `arg()` to overwrite it.
For the code before the rust-lang/rust#142015 change, it won't compile because it will report an error
```
arg `instance`already exists.
```
This PR also modifies all diagnostics that fail the check to pass the check. There are two cases of check failure:
1. ~~Between *the parent diagnostic and the subdiagnostic*, or *between the subdiagnostics* have the same field between them. In this case, I renamed the conflicting fields.~~
2. ~~For subdiagnostics stored in `Vec`, the rendering may iteratively write the same arg over and over again. In this case, I changed the auto-generation with `derive(SubDiagnostic)` to manually implementing `SubDiagnostic` and manually rendered it with `eagerly_translate()`, similar to https://github.com/rust-lang/rust/issues/142031#issuecomment-2984812090, and after rendering it I manually deleted useless arg with the newly added `remove_arg` method.~~
## Final Decision
After trying and discussing, we made a final decision.
For `#[derive(Subdiagnostic)]`, This PR made two changes:
1. After the subdiagnostic is rendered, remove all args of this subdiagnostic, which allows for usage like `Vec<Subdiag>`.
2. Store `diag.args` before setting arguments, so that you can restore the contents of the main diagnostic after deleting the arguments after subdiagnostic is rendered, to avoid deleting the main diagnostic's arg when they have the same name args.