Skip to content

Commit 68b9e4f

Browse files
committed
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
2 parents 41f2f5c + 1d10117 commit 68b9e4f

File tree

9 files changed

+261
-10
lines changed

9 files changed

+261
-10
lines changed

compiler/rustc_hir_typeck/src/_match.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
7777
let mut prior_non_diverging_arms = vec![]; // Used only for diagnostics.
7878
let mut prior_arm = None;
7979
for arm in arms {
80+
self.diverges.set(Diverges::Maybe);
81+
8082
if let Some(e) = &arm.guard {
81-
self.diverges.set(Diverges::Maybe);
8283
self.check_expr_has_type_or_error(e, tcx.types.bool, |_| {});
84+
85+
// FIXME: If this is the first arm and the pattern is irrefutable,
86+
// e.g. `_` or `x`, and the guard diverges, then the whole match
87+
// may also be considered to diverge. We should warn on all subsequent
88+
// arms, too, just like we do for diverging scrutinees above.
8389
}
8490

85-
self.diverges.set(Diverges::Maybe);
91+
// N.B. We don't reset diverges here b/c we want to warn in the arm
92+
// if the guard diverges, like: `x if { loop {} } => f()`, and we
93+
// also want to consider the arm to diverge itself.
8694

8795
let arm_ty = self.check_expr_with_expectation(arm.body, expected);
8896
all_arms_diverge &= self.diverges.get();

compiler/rustc_middle/src/ty/print/pretty.rs

+6
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,12 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
841841
p!(
842842
" upvar_tys=",
843843
print(args.as_coroutine().tupled_upvars_ty()),
844+
" resume_ty=",
845+
print(args.as_coroutine().resume_ty()),
846+
" yield_ty=",
847+
print(args.as_coroutine().yield_ty()),
848+
" return_ty=",
849+
print(args.as_coroutine().return_ty()),
844850
" witness=",
845851
print(args.as_coroutine().witness())
846852
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# `arbitrary_self_types_pointers`
2+
3+
The tracking issue for this feature is: [#44874]
4+
5+
[#38788]: https://github.com/rust-lang/rust/issues/44874
6+
7+
------------------------
8+
9+
This extends the [arbitrary self types] feature to allow methods to
10+
receive `self` by pointer. For example:
11+
12+
```rust
13+
#![feature(arbitrary_self_types_pointers)]
14+
15+
struct A;
16+
17+
impl A {
18+
fn m(self: *const Self) {}
19+
}
20+
21+
fn main() {
22+
let a = A;
23+
let a_ptr: *const A = &a as *const A;
24+
a_ptr.m();
25+
}
26+
```
27+
28+
In general this is not advised: it's thought to be better practice to wrap
29+
raw pointers in a newtype wrapper which implements the `core::ops::Receiver`
30+
trait, then you need "only" the `arbitrary_self_types` feature. For example:
31+
32+
```rust
33+
#![feature(arbitrary_self_types)]
34+
#![allow(dead_code)]
35+
36+
struct A;
37+
38+
impl A {
39+
fn m(self: Wrapper<Self>) {} // can extract the pointer and do
40+
// what it needs
41+
}
42+
43+
struct Wrapper<T>(*const T);
44+
45+
impl<T> core::ops::Receiver for Wrapper<T> {
46+
type Target = T;
47+
}
48+
49+
fn main() {
50+
let a = A;
51+
let a_ptr: *const A = &a as *const A;
52+
let a_wrapper = Wrapper(a_ptr);
53+
a_wrapper.m();
54+
}
55+
```
56+
57+
[arbitrary self types]: arbitrary-self-types.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
# `arbitrary_self_types`
2+
3+
The tracking issue for this feature is: [#44874]
4+
5+
[#38788]: https://github.com/rust-lang/rust/issues/44874
6+
7+
------------------------
8+
9+
Allows any type implementing `core::ops::Receiver<Target=T>` to be used as the type
10+
of `self` in a method belonging to `T`.
11+
12+
For example,
13+
14+
```rust
15+
#![feature(arbitrary_self_types)]
16+
17+
struct A;
18+
19+
impl A {
20+
fn f(self: SmartPtr<Self>) -> i32 { 1 } // note self type
21+
}
22+
23+
struct SmartPtr<T>(T);
24+
25+
impl<T> core::ops::Receiver for SmartPtr<T> {
26+
type Target = T;
27+
}
28+
29+
fn main() {
30+
let smart_ptr = SmartPtr(A);
31+
assert_eq!(smart_ptr.f(), 1);
32+
}
33+
```
34+
35+
The `Receiver` trait has a blanket implementation for all `T: Deref`, so in fact
36+
things like this work too:
37+
38+
```rust
39+
#![feature(arbitrary_self_types)]
40+
41+
use std::rc::Rc;
42+
43+
struct A;
44+
45+
impl A {
46+
fn f(self: Rc<Self>) -> i32 { 1 } // Rc implements Deref
47+
}
48+
49+
fn main() {
50+
let smart_ptr = Rc::new(A);
51+
assert_eq!(smart_ptr.f(), 1);
52+
}
53+
```
54+
55+
Interestingly, that works even without the `arbitrary_self_types` feature
56+
- but that's because certain types are _effectively_ hard coded, including
57+
`Rc`. ("Hard coding" isn't quite true; they use a lang-item called
58+
`LegacyReceiver` to denote their special-ness in this way). With the
59+
`arbitrary_self_types` feature, their special-ness goes away, and custom
60+
smart pointers can achieve the same.
61+
62+
## Changes to method lookup
63+
64+
Method lookup previously used to work by stepping through the `Deref`
65+
chain then using the resulting list of steps in two different ways:
66+
67+
* To identify types that might contribute methods via their `impl`
68+
blocks (inherent methods) or via traits
69+
* To identify the types that the method receiver (`a` in the above
70+
examples) can be converted to.
71+
72+
With this feature, these lists are created by instead stepping through
73+
the `Receiver` chain. However, a note is kept about whether the type
74+
can be reached also via the `Deref` chain.
75+
76+
The full chain (via `Receiver` hops) is used for the first purpose
77+
(identifying relevant `impl` blocks and traits); whereas the shorter
78+
list (reachable via `Deref`) is used for the second purpose. That's
79+
because, to convert the method target (`a` in `a.b()`) to the self
80+
type, Rust may need to be able to use `Deref::deref`. Type conversions,
81+
then, can only proceed as far as the end of the `Deref` chain whereas
82+
the longer `Receiver` chain can be used to explore more places where
83+
useful methods might reside.
84+
85+
## Types suitable for use as smart pointers
86+
87+
This feature allows the creation of customised smart pointers - for example
88+
your own equivalent to `Rc` or `Box` with whatever capabilities you like.
89+
Those smart pointers can either implement `Deref` (if it's safe to
90+
create a reference to the referent) or `Receiver` (if it isn't).
91+
92+
Either way, smart pointer types should mostly _avoid having methods_.
93+
Calling methods on a smart pointer leads to ambiguity about whether you're
94+
aiming for a method on the pointer, or on the referent.
95+
96+
Best practice is therefore to put smart pointer functionality into
97+
associated functions instead - that's what's done in all the smart pointer
98+
types within Rust's standard library which implement `Receiver`.
99+
100+
If you choose to add any methods to your smart pointer type, your users
101+
may run into errors from deshadowing, as described in the next section.
102+
103+
## Avoiding shadowing
104+
105+
With or without this feature, Rust emits an error if it finds two method
106+
candidates, like this:
107+
108+
```rust,compile_fail
109+
use std::pin::Pin;
110+
use std::pin::pin;
111+
112+
struct A;
113+
114+
impl A {
115+
fn get_ref(self: Pin<&A>) {}
116+
}
117+
118+
fn main() {
119+
let pinned_a: Pin<&A> = pin!(A).as_ref();
120+
let pinned_a: Pin<&A> = pinned_a.as_ref();
121+
pinned_a.get_ref(); // error[E0034]: multiple applicable items in scope
122+
}
123+
```
124+
125+
(this is why Rust's smart pointers are mostly carefully designed to avoid
126+
having methods at all, and shouldn't add new methods in future.)
127+
128+
With `arbitrary_self_types`, we take care to spot some other kinds of
129+
conflict:
130+
131+
```rust,compile_fail
132+
#![feature(arbitrary_self_types)]
133+
134+
use std::pin::Pin;
135+
use std::pin::pin;
136+
137+
struct A;
138+
139+
impl A {
140+
fn get_ref(self: &Pin<&A>) {} // note &Pin
141+
}
142+
143+
fn main() {
144+
let pinned_a: Pin<&mut A> = pin!(A);
145+
let pinned_a: Pin<&A> = pinned_a.as_ref();
146+
pinned_a.get_ref();
147+
}
148+
```
149+
150+
This is to guard against the case where an inner (referent) type has a
151+
method of a given name, taking the smart pointer by reference, and then
152+
the smart pointer implementer adds a similar method taking self by value.
153+
As noted in the previous section, the safe option is simply
154+
not to add methods to smart pointers, and then these errors can't occur.

tests/ui/async-await/async-closures/def-path.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ LL | let x = async || {};
55
| -- the expected `async` closure body
66
LL |
77
LL | let () = x();
8-
| ^^ --- this expression has type `{static main::{closure#0}::{closure#0}<?17t> upvar_tys=?16t witness=?6t}`
8+
| ^^ --- this expression has type `{static main::{closure#0}::{closure#0}<?17t> upvar_tys=?16t resume_ty=ResumeTy yield_ty=() return_ty=() witness=?6t}`
99
| |
1010
| expected `async` closure body, found `()`
1111
|
12-
= note: expected `async` closure body `{static main::{closure#0}::{closure#0}<?17t> upvar_tys=?16t witness=?6t}`
12+
= note: expected `async` closure body `{static main::{closure#0}::{closure#0}<?17t> upvar_tys=?16t resume_ty=ResumeTy yield_ty=() return_ty=() witness=?6t}`
1313
found unit type `()`
1414

1515
error: aborting due to 1 previous error

tests/ui/coroutine/print/coroutine-print-verbose-2.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ LL | | drop(a);
99
LL | | });
1010
| |______^ coroutine is not `Sync`
1111
|
12-
= help: within `{main::{closure#0} upvar_tys=() witness={main::{closure#0}}}`, the trait `Sync` is not implemented for `NotSync`
12+
= 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`
1313
note: coroutine is not `Sync` as this value is used across a yield
1414
--> $DIR/coroutine-print-verbose-2.rs:20:9
1515
|
@@ -34,7 +34,7 @@ LL | | drop(a);
3434
LL | | });
3535
| |______^ coroutine is not `Send`
3636
|
37-
= help: within `{main::{closure#1} upvar_tys=() witness={main::{closure#1}}}`, the trait `Send` is not implemented for `NotSend`
37+
= 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`
3838
note: coroutine is not `Send` as this value is used across a yield
3939
--> $DIR/coroutine-print-verbose-2.rs:27:9
4040
|

tests/ui/coroutine/print/coroutine-print-verbose-3.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ LL | | };
1111
| |_____^ expected `()`, found coroutine
1212
|
1313
= note: expected unit type `()`
14-
found coroutine `{main::{closure#0} upvar_tys=?4t witness=?6t}`
14+
found coroutine `{main::{closure#0} upvar_tys=?4t resume_ty=() yield_ty=i32 return_ty=&'?1 str witness=?6t}`
1515

1616
error: aborting due to 1 previous error
1717

tests/ui/reachable/expr_match.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,13 @@ fn d() {
2121
}
2222

2323
fn e() {
24-
// Here the compiler fails to figure out that the `println` is dead.
25-
match () { () if return => (), () => return }
24+
match () {
25+
() if return => (),
26+
//~^ ERROR unreachable expression
27+
() => return,
28+
}
2629
println!("I am dead");
30+
//~^ ERROR unreachable statement
2731
}
2832

2933
fn f() {

tests/ui/reachable/expr_match.stderr

+23-1
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,27 @@ LL | println!("I am dead");
2323
|
2424
= note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
2525

26-
error: aborting due to 2 previous errors
26+
error: unreachable expression
27+
--> $DIR/expr_match.rs:25:25
28+
|
29+
LL | () if return => (),
30+
| ------ ^^ unreachable expression
31+
| |
32+
| any code following this expression is unreachable
33+
34+
error: unreachable statement
35+
--> $DIR/expr_match.rs:29:5
36+
|
37+
LL | / match () {
38+
LL | | () if return => (),
39+
LL | |
40+
LL | | () => return,
41+
LL | | }
42+
| |_____- any code following this `match` expression is unreachable, as all arms diverge
43+
LL | println!("I am dead");
44+
| ^^^^^^^^^^^^^^^^^^^^^ unreachable statement
45+
|
46+
= note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
47+
48+
error: aborting due to 4 previous errors
2749

0 commit comments

Comments
 (0)