Skip to content

Commit cc3f7c6

Browse files
committed
Arbitrary self types v2: new diagnostics
The RFC for arbitrary self types v2 says we'll produce the following diagnostics in the event that a receiver isn't valid: 1. Specific diagnostics for raw pointers 2. Specific diagnostics for Weak 3. Specific diagnostics for NonNull 4. Specific diagnostics if Receiver is implemented for SmartPtr<T>, but not SmartPtr<T: ?Sized> This commit implements 2 and 3, adjusts the existing diagnostics for case 1. For other cases, including case 4, we now instead generate additional errors explaining why the type can't implement Receiver. This uses existing code which is sensitive to lots of cases about why a trait may not be implemented, and makes many helpful suggestions. At present this emits additional separate errors.
1 parent 560a529 commit cc3f7c6

23 files changed

+401
-32
lines changed

compiler/rustc_hir_analysis/messages.ftl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,12 @@ hir_analysis_invalid_receiver_ty = invalid `self` parameter type: `{$receiver_ty
246246
hir_analysis_invalid_receiver_ty_help =
247247
consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box<Self>`, `self: Rc<Self>`, or `self: Arc<Self>`
248248
249+
hir_analysis_invalid_receiver_ty_help_nonnull_note =
250+
`NonNull` does not implement `Receiver`; consider wrapping your `NonNull` in a newtype wrapper for which you implement `Receiver`
251+
252+
hir_analysis_invalid_receiver_ty_help_weak_note =
253+
`Weak` does not implement `Receiver`; consider wrapping your `Weak` in a newtype wrapper for which you implement `Receiver`
254+
249255
hir_analysis_invalid_union_field =
250256
field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
251257
.note = union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>`

compiler/rustc_hir_analysis/src/autoderef.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use rustc_infer::infer::InferCtxt;
2-
use rustc_infer::traits::PredicateObligations;
3-
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
2+
use rustc_infer::traits::{Obligation, PredicateObligations};
3+
use rustc_middle::ty::{self, Predicate, Ty, TyCtxt, TypeVisitableExt};
44
use rustc_session::Limit;
55
use rustc_span::Span;
66
use rustc_span::def_id::{LOCAL_CRATE, LocalDefId};
@@ -44,6 +44,9 @@ pub struct Autoderef<'a, 'tcx> {
4444
include_raw_pointers: bool,
4545
use_receiver_trait: bool,
4646
silence_errors: bool,
47+
48+
// For diagnostics
49+
unmet_obligation: Option<Obligation<'tcx, Predicate<'tcx>>>,
4750
}
4851

4952
impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> {
@@ -135,6 +138,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
135138
include_raw_pointers: false,
136139
use_receiver_trait: false,
137140
silence_errors: false,
141+
unmet_obligation: None,
138142
}
139143
}
140144

@@ -162,6 +166,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
162166
);
163167
if !self.infcx.predicate_may_hold(&obligation) {
164168
debug!("overloaded_deref_ty: cannot match obligation");
169+
self.unmet_obligation = Some(obligation);
165170
return None;
166171
}
167172

@@ -236,6 +241,10 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
236241
self.state.reached_recursion_limit
237242
}
238243

244+
pub fn unmet_obligation(&mut self) -> Option<Obligation<'tcx, Predicate<'tcx>>> {
245+
self.unmet_obligation.take()
246+
}
247+
239248
/// also dereference through raw pointer types
240249
/// e.g., assuming ptr_to_Foo is the type `*const Foo`
241250
/// fcx.autoderef(span, ptr_to_Foo) => [*const Foo]

compiler/rustc_hir_analysis/src/check/wfcheck.rs

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_hir::lang_items::LangItem;
1313
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
1414
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
1515
use rustc_macros::LintDiagnostic;
16-
use rustc_middle::query::Providers;
16+
use rustc_middle::query::{Key, Providers};
1717
use rustc_middle::ty::print::with_no_trimmed_paths;
1818
use rustc_middle::ty::trait_def::TraitSpecializationKind;
1919
use rustc_middle::ty::{
@@ -43,6 +43,7 @@ use {rustc_ast as ast, rustc_hir as hir};
4343
use crate::autoderef::Autoderef;
4444
use crate::collect::CollectItemTypesVisitor;
4545
use crate::constrained_generic_params::{Parameter, identify_constrained_generic_params};
46+
use crate::errors::InvalidReceiverTyHint;
4647
use crate::{errors, fluent_generated as fluent};
4748

4849
pub(super) struct WfCheckingCtxt<'a, 'tcx> {
@@ -1736,7 +1737,24 @@ fn check_method_receiver<'tcx>(
17361737
{
17371738
match receiver_validity_err {
17381739
ReceiverValidityError::DoesNotDeref => {
1739-
tcx.dcx().emit_err(errors::InvalidReceiverTy { span, receiver_ty })
1740+
let hint = match receiver_ty
1741+
.ty_def_id()
1742+
.and_then(|defid| tcx.get_diagnostic_name(defid))
1743+
{
1744+
Some(sym::RcWeak | sym::ArcWeak) => Some(InvalidReceiverTyHint::Weak),
1745+
Some(sym::NonNull) => Some(InvalidReceiverTyHint::NonNull),
1746+
_ => {
1747+
emit_receiver_not_implemented_errors(
1748+
wfcx,
1749+
span,
1750+
receiver_ty,
1751+
arbitrary_self_types_level,
1752+
);
1753+
None
1754+
}
1755+
};
1756+
1757+
tcx.dcx().emit_err(errors::InvalidReceiverTy { span, receiver_ty, hint })
17401758
}
17411759
ReceiverValidityError::MethodGenericParamUsed => {
17421760
tcx.dcx().emit_err(errors::InvalidGenericReceiverTy { span, receiver_ty })
@@ -1891,6 +1909,40 @@ fn legacy_receiver_is_implemented<'tcx>(
18911909
}
18921910
}
18931911

1912+
/// Determine whether it _would have_ been possible to use `receiver_ty` as
1913+
/// a self type if it had been `Sized`, so we can show a specialized diagnostic.
1914+
/// This is because it's an easy mistake to `impl<T> Receiver` instead of
1915+
/// `impl<T: ?Sized> Receiver`.
1916+
fn emit_receiver_not_implemented_errors<'tcx>(
1917+
wfcx: &WfCheckingCtxt<'_, 'tcx>,
1918+
span: Span,
1919+
receiver_ty: Ty<'tcx>,
1920+
arbitrary_self_types_enabled: Option<ArbitrarySelfTypesLevel>,
1921+
) {
1922+
let infcx = wfcx.infcx;
1923+
let mut autoderef = Autoderef::new(infcx, wfcx.param_env, wfcx.body_def_id, span, receiver_ty)
1924+
.use_receiver_trait();
1925+
1926+
if arbitrary_self_types_enabled.is_some() {
1927+
autoderef = autoderef.use_receiver_trait();
1928+
}
1929+
if arbitrary_self_types_enabled == Some(ArbitrarySelfTypesLevel::WithPointers) {
1930+
autoderef = autoderef.include_raw_pointers();
1931+
}
1932+
1933+
while let Some((_, _)) = autoderef.next() {
1934+
// Iterate as far along the `Receiver::Target` hop chain as we can.
1935+
}
1936+
if let Some(unmet_obligation) = autoderef.unmet_obligation() {
1937+
let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx);
1938+
ocx.register_obligation(unmet_obligation);
1939+
let errors = ocx.select_all_or_error();
1940+
if !errors.is_empty() {
1941+
infcx.err_ctxt().report_fulfillment_errors(errors);
1942+
}
1943+
}
1944+
}
1945+
18941946
fn check_variances_for_type_defn<'tcx>(
18951947
tcx: TyCtxt<'tcx>,
18961948
item: &'tcx hir::Item<'tcx>,

compiler/rustc_hir_analysis/src/errors.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1646,6 +1646,14 @@ pub(crate) struct NonConstRange {
16461646
pub span: Span,
16471647
}
16481648

1649+
#[derive(Subdiagnostic)]
1650+
pub(crate) enum InvalidReceiverTyHint {
1651+
#[note(hir_analysis_invalid_receiver_ty_help_weak_note)]
1652+
Weak,
1653+
#[note(hir_analysis_invalid_receiver_ty_help_nonnull_note)]
1654+
NonNull,
1655+
}
1656+
16491657
#[derive(Diagnostic)]
16501658
#[diag(hir_analysis_invalid_receiver_ty, code = E0307)]
16511659
#[note]
@@ -1654,6 +1662,8 @@ pub(crate) struct InvalidReceiverTy<'tcx> {
16541662
#[primary_span]
16551663
pub span: Span,
16561664
pub receiver_ty: Ty<'tcx>,
1665+
#[subdiagnostic]
1666+
pub hint: Option<InvalidReceiverTyHint>,
16571667
}
16581668

16591669
#[derive(Diagnostic)]

tests/ui/async-await/inference_var_self_argument.stderr

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
error[E0277]: the trait bound `dyn Foo: std::ops::Receiver` is not satisfied
2+
--> $DIR/inference_var_self_argument.rs:5:24
3+
|
4+
LL | async fn foo(self: &dyn Foo) {
5+
| ^^^^^^^^ the trait `Deref` is not implemented for `dyn Foo`
6+
|
7+
= note: required for `dyn Foo` to implement `std::ops::Receiver`
8+
19
error[E0307]: invalid `self` parameter type: `&dyn Foo`
210
--> $DIR/inference_var_self_argument.rs:5:24
311
|
@@ -22,7 +30,7 @@ LL | async fn foo(self: &dyn Foo) {
2230
| ^^^ ...because method `foo` is `async`
2331
= help: consider moving `foo` to another trait
2432

25-
error: aborting due to 2 previous errors
33+
error: aborting due to 3 previous errors
2634

27-
Some errors have detailed explanations: E0038, E0307.
35+
Some errors have detailed explanations: E0038, E0277, E0307.
2836
For more information about an error, try `rustc --explain E0038`.

tests/ui/async-await/issue-66312.stderr

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
error[E0277]: the trait bound `T: std::ops::Receiver` is not satisfied
2+
--> $DIR/issue-66312.rs:4:22
3+
|
4+
LL | fn is_some(self: T);
5+
| ^ the trait `Deref` is not implemented for `T`
6+
|
7+
= note: required for `T` to implement `std::ops::Receiver`
8+
help: consider restricting type parameter `T`
9+
|
10+
LL | trait Test<T: std::ops::Deref> {
11+
| +++++++++++++++++
12+
113
error[E0307]: invalid `self` parameter type: `T`
214
--> $DIR/issue-66312.rs:4:22
315
|
@@ -13,7 +25,7 @@ error[E0308]: mismatched types
1325
LL | if x.is_some() {
1426
| ^^^^^^^^^^^ expected `bool`, found `()`
1527

16-
error: aborting due to 2 previous errors
28+
error: aborting due to 3 previous errors
1729

18-
Some errors have detailed explanations: E0307, E0308.
19-
For more information about an error, try `rustc --explain E0307`.
30+
Some errors have detailed explanations: E0277, E0307, E0308.
31+
For more information about an error, try `rustc --explain E0277`.
Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
error[E0277]: the trait bound `Cell<&Self>: std::ops::Receiver` is not satisfied
2+
--> $DIR/feature-gate-dispatch-from-dyn-cell.rs:6:19
3+
|
4+
LL | fn cell(self: Cell<&Self>);
5+
| ^^^^^^^^^^^ the trait `Deref` is not implemented for `Cell<&Self>`
6+
|
7+
= note: required for `Cell<&Self>` to implement `std::ops::Receiver`
8+
19
error[E0307]: invalid `self` parameter type: `Cell<&Self>`
210
--> $DIR/feature-gate-dispatch-from-dyn-cell.rs:6:19
311
|
@@ -7,6 +15,7 @@ LL | fn cell(self: Cell<&Self>);
715
= note: type of `self` must be `Self` or some type implementing Receiver
816
= help: consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box<Self>`, `self: Rc<Self>`, or `self: Arc<Self>`
917

10-
error: aborting due to 1 previous error
18+
error: aborting due to 2 previous errors
1119

12-
For more information about this error, try `rustc --explain E0307`.
20+
Some errors have detailed explanations: E0277, E0307.
21+
For more information about an error, try `rustc --explain E0277`.

tests/ui/issues/issue-56806.stderr

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
error[E0277]: the trait bound `(dyn Trait + 'static): std::ops::Receiver` is not satisfied
2+
--> $DIR/issue-56806.rs:2:34
3+
|
4+
LL | fn dyn_instead_of_self(self: Box<dyn Trait>);
5+
| ^^^^^^^^^^^^^^ the trait `Deref` is not implemented for `(dyn Trait + 'static)`
6+
|
7+
= note: required for `(dyn Trait + 'static)` to implement `std::ops::Receiver`
8+
19
error[E0307]: invalid `self` parameter type: `Box<(dyn Trait + 'static)>`
210
--> $DIR/issue-56806.rs:2:34
311
|
@@ -7,6 +15,7 @@ LL | fn dyn_instead_of_self(self: Box<dyn Trait>);
715
= note: type of `self` must be `Self` or some type implementing Receiver
816
= help: consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box<Self>`, `self: Rc<Self>`, or `self: Arc<Self>`
917

10-
error: aborting due to 1 previous error
18+
error: aborting due to 2 previous errors
1119

12-
For more information about this error, try `rustc --explain E0307`.
20+
Some errors have detailed explanations: E0277, E0307.
21+
For more information about an error, try `rustc --explain E0277`.

tests/ui/self/arbitrary-self-opaque.stderr

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
error[E0277]: the trait bound `Bar: std::ops::Receiver` is not satisfied
2+
--> $DIR/arbitrary-self-opaque.rs:8:18
3+
|
4+
LL | fn foo(self: Bar) {}
5+
| ^^^ the trait `Deref` is not implemented for `Bar`
6+
|
7+
= note: required for `Bar` to implement `std::ops::Receiver`
8+
19
error[E0307]: invalid `self` parameter type: `Bar`
210
--> $DIR/arbitrary-self-opaque.rs:8:18
311
|
@@ -28,6 +36,7 @@ LL | type Bar = impl Sized;
2836
|
2937
= note: `Bar` must be used in combination with a concrete type within the same module
3038

31-
error: aborting due to 3 previous errors
39+
error: aborting due to 4 previous errors
3240

33-
For more information about this error, try `rustc --explain E0307`.
41+
Some errors have detailed explanations: E0277, E0307.
42+
For more information about an error, try `rustc --explain E0277`.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#[feature(arbitrary_self_types)]
2+
3+
struct A;
4+
5+
impl A {
6+
fn m(self: std::ptr::NonNull<Self>) {}
7+
//~^ ERROR: invalid `self` parameter type
8+
fn n(self: &std::ptr::NonNull<Self>) {}
9+
//~^ ERROR: invalid `self` parameter type
10+
}
11+
12+
fn main() {
13+
}

0 commit comments

Comments
 (0)