mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
Auto merge of #134748 - DianQK:rollup-3y5fzcx, r=DianQK
Rollup of 3 pull requests Successful merges: - #134525 (Arbitrary self types v2: unstable doc updates.) - #134735 (Consider arm to diverge if guard diverges) - #134741 (Actually print all the relevant parts of a coroutine in verbose mode) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
@@ -77,12 +77,20 @@ pub(crate) fn check_expr_match(
|
||||
let mut prior_non_diverging_arms = vec![]; // Used only for diagnostics.
|
||||
let mut prior_arm = None;
|
||||
for arm in arms {
|
||||
self.diverges.set(Diverges::Maybe);
|
||||
|
||||
if let Some(e) = &arm.guard {
|
||||
self.diverges.set(Diverges::Maybe);
|
||||
self.check_expr_has_type_or_error(e, tcx.types.bool, |_| {});
|
||||
|
||||
// FIXME: If this is the first arm and the pattern is irrefutable,
|
||||
// e.g. `_` or `x`, and the guard diverges, then the whole match
|
||||
// may also be considered to diverge. We should warn on all subsequent
|
||||
// arms, too, just like we do for diverging scrutinees above.
|
||||
}
|
||||
|
||||
self.diverges.set(Diverges::Maybe);
|
||||
// N.B. We don't reset diverges here b/c we want to warn in the arm
|
||||
// if the guard diverges, like: `x if { loop {} } => f()`, and we
|
||||
// also want to consider the arm to diverge itself.
|
||||
|
||||
let arm_ty = self.check_expr_with_expectation(arm.body, expected);
|
||||
all_arms_diverge &= self.diverges.get();
|
||||
|
||||
@@ -841,6 +841,12 @@ fn pretty_print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError> {
|
||||
p!(
|
||||
" upvar_tys=",
|
||||
print(args.as_coroutine().tupled_upvars_ty()),
|
||||
" resume_ty=",
|
||||
print(args.as_coroutine().resume_ty()),
|
||||
" yield_ty=",
|
||||
print(args.as_coroutine().yield_ty()),
|
||||
" return_ty=",
|
||||
print(args.as_coroutine().return_ty()),
|
||||
" witness=",
|
||||
print(args.as_coroutine().witness())
|
||||
);
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# `arbitrary_self_types_pointers`
|
||||
|
||||
The tracking issue for this feature is: [#44874]
|
||||
|
||||
[#38788]: https://github.com/rust-lang/rust/issues/44874
|
||||
|
||||
------------------------
|
||||
|
||||
This extends the [arbitrary self types] feature to allow methods to
|
||||
receive `self` by pointer. For example:
|
||||
|
||||
```rust
|
||||
#![feature(arbitrary_self_types_pointers)]
|
||||
|
||||
struct A;
|
||||
|
||||
impl A {
|
||||
fn m(self: *const Self) {}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a = A;
|
||||
let a_ptr: *const A = &a as *const A;
|
||||
a_ptr.m();
|
||||
}
|
||||
```
|
||||
|
||||
In general this is not advised: it's thought to be better practice to wrap
|
||||
raw pointers in a newtype wrapper which implements the `core::ops::Receiver`
|
||||
trait, then you need "only" the `arbitrary_self_types` feature. For example:
|
||||
|
||||
```rust
|
||||
#![feature(arbitrary_self_types)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
struct A;
|
||||
|
||||
impl A {
|
||||
fn m(self: Wrapper<Self>) {} // can extract the pointer and do
|
||||
// what it needs
|
||||
}
|
||||
|
||||
struct Wrapper<T>(*const T);
|
||||
|
||||
impl<T> core::ops::Receiver for Wrapper<T> {
|
||||
type Target = T;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a = A;
|
||||
let a_ptr: *const A = &a as *const A;
|
||||
let a_wrapper = Wrapper(a_ptr);
|
||||
a_wrapper.m();
|
||||
}
|
||||
```
|
||||
|
||||
[arbitrary self types]: arbitrary-self-types.md
|
||||
@@ -0,0 +1,154 @@
|
||||
# `arbitrary_self_types`
|
||||
|
||||
The tracking issue for this feature is: [#44874]
|
||||
|
||||
[#38788]: https://github.com/rust-lang/rust/issues/44874
|
||||
|
||||
------------------------
|
||||
|
||||
Allows any type implementing `core::ops::Receiver<Target=T>` to be used as the type
|
||||
of `self` in a method belonging to `T`.
|
||||
|
||||
For example,
|
||||
|
||||
```rust
|
||||
#![feature(arbitrary_self_types)]
|
||||
|
||||
struct A;
|
||||
|
||||
impl A {
|
||||
fn f(self: SmartPtr<Self>) -> i32 { 1 } // note self type
|
||||
}
|
||||
|
||||
struct SmartPtr<T>(T);
|
||||
|
||||
impl<T> core::ops::Receiver for SmartPtr<T> {
|
||||
type Target = T;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let smart_ptr = SmartPtr(A);
|
||||
assert_eq!(smart_ptr.f(), 1);
|
||||
}
|
||||
```
|
||||
|
||||
The `Receiver` trait has a blanket implementation for all `T: Deref`, so in fact
|
||||
things like this work too:
|
||||
|
||||
```rust
|
||||
#![feature(arbitrary_self_types)]
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
struct A;
|
||||
|
||||
impl A {
|
||||
fn f(self: Rc<Self>) -> i32 { 1 } // Rc implements Deref
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let smart_ptr = Rc::new(A);
|
||||
assert_eq!(smart_ptr.f(), 1);
|
||||
}
|
||||
```
|
||||
|
||||
Interestingly, that works even without the `arbitrary_self_types` feature
|
||||
- but that's because certain types are _effectively_ hard coded, including
|
||||
`Rc`. ("Hard coding" isn't quite true; they use a lang-item called
|
||||
`LegacyReceiver` to denote their special-ness in this way). With the
|
||||
`arbitrary_self_types` feature, their special-ness goes away, and custom
|
||||
smart pointers can achieve the same.
|
||||
|
||||
## Changes to method lookup
|
||||
|
||||
Method lookup previously used to work by stepping through the `Deref`
|
||||
chain then using the resulting list of steps in two different ways:
|
||||
|
||||
* To identify types that might contribute methods via their `impl`
|
||||
blocks (inherent methods) or via traits
|
||||
* To identify the types that the method receiver (`a` in the above
|
||||
examples) can be converted to.
|
||||
|
||||
With this feature, these lists are created by instead stepping through
|
||||
the `Receiver` chain. However, a note is kept about whether the type
|
||||
can be reached also via the `Deref` chain.
|
||||
|
||||
The full chain (via `Receiver` hops) is used for the first purpose
|
||||
(identifying relevant `impl` blocks and traits); whereas the shorter
|
||||
list (reachable via `Deref`) is used for the second purpose. That's
|
||||
because, to convert the method target (`a` in `a.b()`) to the self
|
||||
type, Rust may need to be able to use `Deref::deref`. Type conversions,
|
||||
then, can only proceed as far as the end of the `Deref` chain whereas
|
||||
the longer `Receiver` chain can be used to explore more places where
|
||||
useful methods might reside.
|
||||
|
||||
## Types suitable for use as smart pointers
|
||||
|
||||
This feature allows the creation of customised smart pointers - for example
|
||||
your own equivalent to `Rc` or `Box` with whatever capabilities you like.
|
||||
Those smart pointers can either implement `Deref` (if it's safe to
|
||||
create a reference to the referent) or `Receiver` (if it isn't).
|
||||
|
||||
Either way, smart pointer types should mostly _avoid having methods_.
|
||||
Calling methods on a smart pointer leads to ambiguity about whether you're
|
||||
aiming for a method on the pointer, or on the referent.
|
||||
|
||||
Best practice is therefore to put smart pointer functionality into
|
||||
associated functions instead - that's what's done in all the smart pointer
|
||||
types within Rust's standard library which implement `Receiver`.
|
||||
|
||||
If you choose to add any methods to your smart pointer type, your users
|
||||
may run into errors from deshadowing, as described in the next section.
|
||||
|
||||
## Avoiding shadowing
|
||||
|
||||
With or without this feature, Rust emits an error if it finds two method
|
||||
candidates, like this:
|
||||
|
||||
```rust,compile_fail
|
||||
use std::pin::Pin;
|
||||
use std::pin::pin;
|
||||
|
||||
struct A;
|
||||
|
||||
impl A {
|
||||
fn get_ref(self: Pin<&A>) {}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let pinned_a: Pin<&A> = pin!(A).as_ref();
|
||||
let pinned_a: Pin<&A> = pinned_a.as_ref();
|
||||
pinned_a.get_ref(); // error[E0034]: multiple applicable items in scope
|
||||
}
|
||||
```
|
||||
|
||||
(this is why Rust's smart pointers are mostly carefully designed to avoid
|
||||
having methods at all, and shouldn't add new methods in future.)
|
||||
|
||||
With `arbitrary_self_types`, we take care to spot some other kinds of
|
||||
conflict:
|
||||
|
||||
```rust,compile_fail
|
||||
#![feature(arbitrary_self_types)]
|
||||
|
||||
use std::pin::Pin;
|
||||
use std::pin::pin;
|
||||
|
||||
struct A;
|
||||
|
||||
impl A {
|
||||
fn get_ref(self: &Pin<&A>) {} // note &Pin
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let pinned_a: Pin<&mut A> = pin!(A);
|
||||
let pinned_a: Pin<&A> = pinned_a.as_ref();
|
||||
pinned_a.get_ref();
|
||||
}
|
||||
```
|
||||
|
||||
This is to guard against the case where an inner (referent) type has a
|
||||
method of a given name, taking the smart pointer by reference, and then
|
||||
the smart pointer implementer adds a similar method taking self by value.
|
||||
As noted in the previous section, the safe option is simply
|
||||
not to add methods to smart pointers, and then these errors can't occur.
|
||||
@@ -5,11 +5,11 @@ LL | let x = async || {};
|
||||
| -- the expected `async` closure body
|
||||
LL |
|
||||
LL | let () = x();
|
||||
| ^^ --- this expression has type `{static main::{closure#0}::{closure#0}<?17t> upvar_tys=?16t witness=?6t}`
|
||||
| ^^ --- this expression has type `{static main::{closure#0}::{closure#0}<?17t> upvar_tys=?16t resume_ty=ResumeTy yield_ty=() return_ty=() witness=?6t}`
|
||||
| |
|
||||
| expected `async` closure body, found `()`
|
||||
|
|
||||
= note: expected `async` closure body `{static main::{closure#0}::{closure#0}<?17t> upvar_tys=?16t witness=?6t}`
|
||||
= note: expected `async` closure body `{static main::{closure#0}::{closure#0}<?17t> upvar_tys=?16t resume_ty=ResumeTy yield_ty=() return_ty=() witness=?6t}`
|
||||
found unit type `()`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
@@ -9,7 +9,7 @@ LL | | drop(a);
|
||||
LL | | });
|
||||
| |______^ coroutine is not `Sync`
|
||||
|
|
||||
= help: within `{main::{closure#0} upvar_tys=() witness={main::{closure#0}}}`, the trait `Sync` is not implemented for `NotSync`
|
||||
= help: within `{main::{closure#0} upvar_tys=() resume_ty=() yield_ty=() return_ty=() witness={main::{closure#0}}}`, the trait `Sync` is not implemented for `NotSync`
|
||||
note: coroutine is not `Sync` as this value is used across a yield
|
||||
--> $DIR/coroutine-print-verbose-2.rs:20:9
|
||||
|
|
||||
@@ -34,7 +34,7 @@ LL | | drop(a);
|
||||
LL | | });
|
||||
| |______^ coroutine is not `Send`
|
||||
|
|
||||
= help: within `{main::{closure#1} upvar_tys=() witness={main::{closure#1}}}`, the trait `Send` is not implemented for `NotSend`
|
||||
= help: within `{main::{closure#1} upvar_tys=() resume_ty=() yield_ty=() return_ty=() witness={main::{closure#1}}}`, the trait `Send` is not implemented for `NotSend`
|
||||
note: coroutine is not `Send` as this value is used across a yield
|
||||
--> $DIR/coroutine-print-verbose-2.rs:27:9
|
||||
|
|
||||
|
||||
@@ -11,7 +11,7 @@ LL | | };
|
||||
| |_____^ expected `()`, found coroutine
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found coroutine `{main::{closure#0} upvar_tys=?4t witness=?6t}`
|
||||
found coroutine `{main::{closure#0} upvar_tys=?4t resume_ty=() yield_ty=i32 return_ty=&'?1 str witness=?6t}`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
||||
@@ -21,9 +21,13 @@ fn d() {
|
||||
}
|
||||
|
||||
fn e() {
|
||||
// Here the compiler fails to figure out that the `println` is dead.
|
||||
match () { () if return => (), () => return }
|
||||
match () {
|
||||
() if return => (),
|
||||
//~^ ERROR unreachable expression
|
||||
() => return,
|
||||
}
|
||||
println!("I am dead");
|
||||
//~^ ERROR unreachable statement
|
||||
}
|
||||
|
||||
fn f() {
|
||||
|
||||
@@ -23,5 +23,27 @@ LL | println!("I am dead");
|
||||
|
|
||||
= note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error: unreachable expression
|
||||
--> $DIR/expr_match.rs:25:25
|
||||
|
|
||||
LL | () if return => (),
|
||||
| ------ ^^ unreachable expression
|
||||
| |
|
||||
| any code following this expression is unreachable
|
||||
|
||||
error: unreachable statement
|
||||
--> $DIR/expr_match.rs:29:5
|
||||
|
|
||||
LL | / match () {
|
||||
LL | | () if return => (),
|
||||
LL | |
|
||||
LL | | () => return,
|
||||
LL | | }
|
||||
| |_____- any code following this `match` expression is unreachable, as all arms diverge
|
||||
LL | println!("I am dead");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ unreachable statement
|
||||
|
|
||||
= note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
|
||||
Reference in New Issue
Block a user