Skip to content

Commit 5dfbacc

Browse files
committed
Reject generic self types.
The RFC for arbitrary self types v2 declares that we should reject "generic" self types. This commit does so. The definition of "generic" was unclear in the RFC, but has been explored in rust-lang#129147 and the conclusion is that "generic" means any `self` type which is a type parameter defined on the method itself, or references to such a type. This approach was chosen because other definitions of "generic" don't work. Specifically, * we can't filter out generic type _arguments_, because that would filter out Rc<Self> and all the other types of smart pointer we want to support; * we can't filter out all type params, because Self itself is a type param, and because existing Rust code depends on other type params declared on the type (as opposed to the method). This PR decides to make a new error code for this case, instead of reusing the existing E0307 error. This makes the code a bit more complex, but it seems we have an opportunity to provide specific diagnostics for this case so we should do so. This PR filters out generic self types whether or not the 'arbitrary self types' feature is enabled. However, it's believed that it can't have any effect on code which uses stable Rust, since there are no stable traits which can be used to indicate a valid generic receiver type, and thus it would have been impossible to write code which could trigger this new error case. It is however possible that this could break existing code which uses either of the unstable `arbitrary_self_types` or `receiver_trait` features. This breakage is intentional; as we move arbitrary self types towards stabilization we don't want to continue to support generic such types. This PR adds lots of extra tests to arbitrary-self-from-method-substs. Most of these are ways to trigger a "type mismatch" error which https://github.com/rust-lang/rust/blob/9b82580c7347f800c2550e6719e4218a60a80b28/compiler/rustc_hir_typeck/src/method/confirm.rs#L519 hopes can be minimized by filtering out generics in this way. We remove a FIXME from confirm.rs suggesting that we make this change. It's still possible to cause type mismatch errors, and a subsequent PR may be able to improve diagnostics in this area, but it's harder to cause these errors without contrived uses of the turbofish. This is a part of the arbitrary self types v2 project, rust-lang/rfcs#3519 rust-lang#44874 r? @wesleywiser
1 parent 7f4b270 commit 5dfbacc

14 files changed

+570
-61
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
The `self` parameter in a method has an invalid generic "receiver type".
2+
3+
Erroneous code example:
4+
5+
```compile_fail,E0799
6+
struct Foo;
7+
8+
impl Foo {
9+
fn foo<R: std::ops::Deref<Target=Self>>(self: R) {}
10+
}
11+
```
12+
13+
or alternatively,
14+
15+
```compile_fail,E0799
16+
struct Foo;
17+
18+
impl Foo {
19+
fn foo(self: impl std::ops::Deref<Target=Self>) {}
20+
}
21+
```
22+
23+
Methods take a special first parameter, termed `self`. It's normal to
24+
use `self`, `&self` or `&mut self`, which are syntactic sugar for
25+
`self: Self`, `self: &Self`, and `self: &mut Self` respectively.
26+
But it's also possible to use more sophisticated types of `self`
27+
parameter, for instance `std::rc::Rc<Self>`. The set of allowable
28+
`Self` types is extensible using the nightly feature
29+
[Arbitrary self types][AST].
30+
This will extend the valid set of `Self` types to anything which implements
31+
`std::ops::Deref<Target=Self>`, for example `Rc<Self>`, `Box<Self>`, or
32+
your own smart pointers that do the same.
33+
34+
However, even with that feature, the `self` type must be concrete.
35+
Generic `self` types are not permitted. Specifically, a `self` type will
36+
be rejected if it is a type parameter defined on the method.
37+
38+
These are OK:
39+
40+
```
41+
struct Foo;
42+
43+
impl Foo {
44+
fn foo(self) {}
45+
fn foo2(self: std::rc::Rc<Self>) {} // or some other similar
46+
// smart pointer if you enable arbitrary self types and
47+
// the pointer implements Deref<Target=Self>
48+
}
49+
```
50+
51+
[AST]: https://doc.rust-lang.org/unstable-book/language-features/arbitrary-self-types.html

compiler/rustc_error_codes/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,7 @@ E0795: 0795,
538538
E0796: 0796,
539539
E0797: 0797,
540540
E0798: 0798,
541+
E0799: 0799,
541542
);
542543
)
543544
}

compiler/rustc_hir_analysis/messages.ftl

+6
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,12 @@ hir_analysis_inherent_ty_outside_relevant = cannot define inherent `impl` for a
233233
.help = consider moving this inherent impl into the crate defining the type if possible
234234
.span_help = alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items
235235
236+
hir_analysis_invalid_generic_receiver_ty = invalid generic `self` parameter type: `{$receiver_ty}`
237+
.note = type of `self` must not be a method generic parameter type
238+
239+
hir_analysis_invalid_generic_receiver_ty_help =
240+
use a concrete type such as `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`)
241+
236242
hir_analysis_invalid_receiver_ty = invalid `self` parameter type: `{$receiver_ty}`
237243
.note = type of `self` must be `Self` or a type that dereferences to it
238244

compiler/rustc_hir_analysis/src/check/wfcheck.rs

+73-13
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ use std::cell::LazyCell;
22
use std::ops::{ControlFlow, Deref};
33

44
use hir::intravisit::{self, Visitor};
5+
use itertools::Itertools;
56
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
67
use rustc_errors::codes::*;
78
use rustc_errors::{pluralize, struct_span_code_err, Applicability, ErrorGuaranteed};
89
use rustc_hir::def::{DefKind, Res};
910
use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
1011
use rustc_hir::lang_items::LangItem;
11-
use rustc_hir::ItemKind;
12+
use rustc_hir::{GenericParamKind, ItemKind};
1213
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
1314
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
1415
use rustc_macros::LintDiagnostic;
@@ -375,7 +376,7 @@ fn check_trait_item<'tcx>(
375376
_ => (None, trait_item.span),
376377
};
377378
check_object_unsafe_self_trait_by_name(tcx, trait_item);
378-
let mut res = check_associated_item(tcx, def_id, span, method_sig);
379+
let mut res = check_associated_item(tcx, def_id, span, method_sig, None);
379380

380381
if matches!(trait_item.kind, hir::TraitItemKind::Fn(..)) {
381382
for &assoc_ty_def_id in tcx.associated_types_for_impl_traits_in_associated_fn(def_id) {
@@ -384,6 +385,7 @@ fn check_trait_item<'tcx>(
384385
assoc_ty_def_id.expect_local(),
385386
tcx.def_span(assoc_ty_def_id),
386387
None,
388+
None,
387389
));
388390
}
389391
}
@@ -903,8 +905,13 @@ fn check_impl_item<'tcx>(
903905
hir::ImplItemKind::Type(ty) if ty.span != DUMMY_SP => (None, ty.span),
904906
_ => (None, impl_item.span),
905907
};
906-
907-
check_associated_item(tcx, impl_item.owner_id.def_id, span, method_sig)
908+
check_associated_item(
909+
tcx,
910+
impl_item.owner_id.def_id,
911+
span,
912+
method_sig,
913+
Some(impl_item.generics),
914+
)
908915
}
909916

910917
fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(), ErrorGuaranteed> {
@@ -1035,6 +1042,7 @@ fn check_associated_item(
10351042
item_id: LocalDefId,
10361043
span: Span,
10371044
sig_if_method: Option<&hir::FnSig<'_>>,
1045+
generics: Option<&hir::Generics<'_>>,
10381046
) -> Result<(), ErrorGuaranteed> {
10391047
let loc = Some(WellFormedLoc::Ty(item_id));
10401048
enter_wf_checking_ctxt(tcx, span, item_id, |wfcx| {
@@ -1067,7 +1075,7 @@ fn check_associated_item(
10671075
hir_sig.decl,
10681076
item.def_id.expect_local(),
10691077
);
1070-
check_method_receiver(wfcx, hir_sig, item, self_ty)
1078+
check_method_receiver(wfcx, hir_sig, item, self_ty, generics)
10711079
}
10721080
ty::AssocKind::Type => {
10731081
if let ty::AssocItemContainer::TraitContainer = item.container {
@@ -1665,6 +1673,7 @@ fn check_method_receiver<'tcx>(
16651673
fn_sig: &hir::FnSig<'_>,
16661674
method: ty::AssocItem,
16671675
self_ty: Ty<'tcx>,
1676+
generics: Option<&hir::Generics<'_>>,
16681677
) -> Result<(), ErrorGuaranteed> {
16691678
let tcx = wfcx.tcx();
16701679

@@ -1699,7 +1708,9 @@ fn check_method_receiver<'tcx>(
16991708
None
17001709
};
17011710

1702-
if !receiver_is_valid(wfcx, span, receiver_ty, self_ty, arbitrary_self_types_level) {
1711+
let receiver_validity =
1712+
receiver_is_valid(wfcx, span, receiver_ty, self_ty, arbitrary_self_types_level, generics);
1713+
if let Err(receiver_validity_err) = receiver_validity {
17031714
return Err(match arbitrary_self_types_level {
17041715
// Wherever possible, emit a message advising folks that the features
17051716
// `arbitrary_self_types` or `arbitrary_self_types_pointers` might
@@ -1710,7 +1721,9 @@ fn check_method_receiver<'tcx>(
17101721
receiver_ty,
17111722
self_ty,
17121723
Some(ArbitrarySelfTypesLevel::Basic),
1713-
) =>
1724+
generics,
1725+
)
1726+
.is_ok() =>
17141727
{
17151728
// Report error; would have worked with `arbitrary_self_types`.
17161729
feature_err(
@@ -1732,7 +1745,9 @@ fn check_method_receiver<'tcx>(
17321745
receiver_ty,
17331746
self_ty,
17341747
Some(ArbitrarySelfTypesLevel::WithPointers),
1735-
) =>
1748+
generics,
1749+
)
1750+
.is_ok() =>
17361751
{
17371752
// Report error; would have worked with `arbitrary_self_types_pointers`.
17381753
feature_err(
@@ -1750,13 +1765,53 @@ fn check_method_receiver<'tcx>(
17501765
_ =>
17511766
// Report error; would not have worked with `arbitrary_self_types[_pointers]`.
17521767
{
1753-
tcx.dcx().emit_err(errors::InvalidReceiverTy { span, receiver_ty })
1768+
match receiver_validity_err {
1769+
ReceiverValidityError::DoesNotDeref => {
1770+
tcx.dcx().emit_err(errors::InvalidReceiverTy { span, receiver_ty })
1771+
}
1772+
ReceiverValidityError::MethodGenericParamUsed => {
1773+
tcx.dcx().emit_err(errors::InvalidGenericReceiverTy { span, receiver_ty })
1774+
}
1775+
}
17541776
}
17551777
});
17561778
}
17571779
Ok(())
17581780
}
17591781

1782+
/// Error cases which may be returned from `receiver_is_valid`. These error
1783+
/// cases are generated in this function as they may be unearthed as we explore
1784+
/// the `autoderef` chain, but they're converted to diagnostics in the caller.
1785+
enum ReceiverValidityError {
1786+
/// The self type does not get to the receiver type by following the
1787+
/// autoderef chain.
1788+
DoesNotDeref,
1789+
/// A type was found which is a method type parameter, and that's not allowed.
1790+
MethodGenericParamUsed,
1791+
}
1792+
1793+
/// Confirms that a type is not a type parameter referring to one of the
1794+
/// method's type params.
1795+
fn confirm_type_is_not_a_method_generic_param(
1796+
ty: Ty<'_>,
1797+
method_generics: Option<&hir::Generics<'_>>,
1798+
) -> Result<(), ReceiverValidityError> {
1799+
if let ty::Param(param) = ty.kind() {
1800+
if let Some(generics) = method_generics {
1801+
if generics
1802+
.params
1803+
.iter()
1804+
.filter(|g| matches!(g.kind, GenericParamKind::Type { .. }))
1805+
.map(|g| g.name.ident().name)
1806+
.contains(&param.name)
1807+
{
1808+
return Err(ReceiverValidityError::MethodGenericParamUsed);
1809+
}
1810+
}
1811+
}
1812+
Ok(())
1813+
}
1814+
17601815
/// Returns whether `receiver_ty` would be considered a valid receiver type for `self_ty`. If
17611816
/// `arbitrary_self_types` is enabled, `receiver_ty` must transitively deref to `self_ty`, possibly
17621817
/// through a `*const/mut T` raw pointer if `arbitrary_self_types_pointers` is also enabled.
@@ -1772,7 +1827,8 @@ fn receiver_is_valid<'tcx>(
17721827
receiver_ty: Ty<'tcx>,
17731828
self_ty: Ty<'tcx>,
17741829
arbitrary_self_types_enabled: Option<ArbitrarySelfTypesLevel>,
1775-
) -> bool {
1830+
generics: Option<&hir::Generics<'_>>,
1831+
) -> Result<(), ReceiverValidityError> {
17761832
let infcx = wfcx.infcx;
17771833
let tcx = wfcx.tcx();
17781834
let cause =
@@ -1784,9 +1840,11 @@ fn receiver_is_valid<'tcx>(
17841840
ocx.eq(&cause, wfcx.param_env, self_ty, receiver_ty)?;
17851841
if ocx.select_all_or_error().is_empty() { Ok(()) } else { Err(NoSolution) }
17861842
}) {
1787-
return true;
1843+
return Ok(());
17881844
}
17891845

1846+
confirm_type_is_not_a_method_generic_param(receiver_ty, generics)?;
1847+
17901848
let mut autoderef = Autoderef::new(infcx, wfcx.param_env, wfcx.body_def_id, span, receiver_ty);
17911849

17921850
// The `arbitrary_self_types_pointers` feature allows raw pointer receivers like `self: *const Self`.
@@ -1803,6 +1861,8 @@ fn receiver_is_valid<'tcx>(
18031861
potential_self_ty, self_ty
18041862
);
18051863

1864+
confirm_type_is_not_a_method_generic_param(potential_self_ty, generics)?;
1865+
18061866
// Check if the self type unifies. If it does, then commit the result
18071867
// since it may have region side-effects.
18081868
if let Ok(()) = wfcx.infcx.commit_if_ok(|_| {
@@ -1811,7 +1871,7 @@ fn receiver_is_valid<'tcx>(
18111871
if ocx.select_all_or_error().is_empty() { Ok(()) } else { Err(NoSolution) }
18121872
}) {
18131873
wfcx.register_obligations(autoderef.into_obligations());
1814-
return true;
1874+
return Ok(());
18151875
}
18161876

18171877
// Without `feature(arbitrary_self_types)`, we require that each step in the
@@ -1838,7 +1898,7 @@ fn receiver_is_valid<'tcx>(
18381898
}
18391899

18401900
debug!("receiver_is_valid: type `{:?}` does not deref to `{:?}`", receiver_ty, self_ty);
1841-
false
1901+
Err(ReceiverValidityError::DoesNotDeref)
18421902
}
18431903

18441904
fn receiver_is_implemented<'tcx>(

compiler/rustc_hir_analysis/src/errors.rs

+10
Original file line numberDiff line numberDiff line change
@@ -1712,6 +1712,16 @@ pub(crate) struct InvalidReceiverTy<'tcx> {
17121712
pub receiver_ty: Ty<'tcx>,
17131713
}
17141714

1715+
#[derive(Diagnostic)]
1716+
#[diag(hir_analysis_invalid_generic_receiver_ty, code = E0799)]
1717+
#[note]
1718+
#[help(hir_analysis_invalid_generic_receiver_ty_help)]
1719+
pub(crate) struct InvalidGenericReceiverTy<'tcx> {
1720+
#[primary_span]
1721+
pub span: Span,
1722+
pub receiver_ty: Ty<'tcx>,
1723+
}
1724+
17151725
#[derive(Diagnostic)]
17161726
#[diag(hir_analysis_effects_without_next_solver)]
17171727
#[note]

compiler/rustc_hir_typeck/src/method/confirm.rs

-3
Original file line numberDiff line numberDiff line change
@@ -516,9 +516,6 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
516516
self.register_predicates(obligations);
517517
}
518518
Err(terr) => {
519-
// FIXME(arbitrary_self_types): We probably should limit the
520-
// situations where this can occur by adding additional restrictions
521-
// to the feature, like the self type can't reference method args.
522519
if self.tcx.features().arbitrary_self_types {
523520
self.err_ctxt()
524521
.report_mismatched_types(&cause, method_self_ty, self_ty, terr)

src/tools/tidy/src/issues.txt

-1
Original file line numberDiff line numberDiff line change
@@ -4122,7 +4122,6 @@ ui/type-alias-impl-trait/issue-53678-coroutine-and-const-fn.rs
41224122
ui/type-alias-impl-trait/issue-55099-lifetime-inference.rs
41234123
ui/type-alias-impl-trait/issue-57188-associate-impl-capture.rs
41244124
ui/type-alias-impl-trait/issue-57611-trait-alias.rs
4125-
ui/type-alias-impl-trait/issue-57700.rs
41264125
ui/type-alias-impl-trait/issue-57807-associated-type.rs
41274126
ui/type-alias-impl-trait/issue-57961.rs
41284127
ui/type-alias-impl-trait/issue-58662-coroutine-with-lifetime.rs

tests/ui/self/arbitrary-self-from-method-substs-ice.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::ops::Deref;
88
struct Foo(u32);
99
impl Foo {
1010
const fn get<R: Deref<Target = Self>>(self: R) -> u32 {
11-
//~^ ERROR: `R` cannot be used as the type of `self`
11+
//~^ ERROR invalid generic `self` parameter type
1212
//~| ERROR destructor of `R` cannot be evaluated at compile-time
1313
self.0
1414
//~^ ERROR cannot borrow here, since the borrowed element may contain interior mutability

tests/ui/self/arbitrary-self-from-method-substs-ice.stderr

+4-6
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,16 @@ LL | const fn get<R: Deref<Target = Self>>(self: R) -> u32 {
2929
LL | }
3030
| - value is dropped here
3131

32-
error[E0658]: `R` cannot be used as the type of `self` without the `arbitrary_self_types` feature
32+
error[E0799]: invalid generic `self` parameter type: `R`
3333
--> $DIR/arbitrary-self-from-method-substs-ice.rs:10:49
3434
|
3535
LL | const fn get<R: Deref<Target = Self>>(self: R) -> u32 {
3636
| ^
3737
|
38-
= note: see issue #44874 <https://github.com/rust-lang/rust/issues/44874> for more information
39-
= help: add `#![feature(arbitrary_self_types)]` to the crate attributes to enable
40-
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
41-
= help: consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`)
38+
= note: type of `self` must not be a method generic parameter type
39+
= help: use a concrete type such as `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`)
4240

4341
error: aborting due to 4 previous errors
4442

45-
Some errors have detailed explanations: E0015, E0493, E0658.
43+
Some errors have detailed explanations: E0015, E0493, E0658, E0799.
4644
For more information about an error, try `rustc --explain E0015`.

0 commit comments

Comments
 (0)