Skip to content

Commit 6d8d795

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 #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 #44874 r? @wesleywiser
1 parent 8b9f0f9 commit 6d8d795

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,E0801
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,E0801
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
@@ -540,6 +540,7 @@ E0797: 0797,
540540
E0798: 0798,
541541
E0799: 0799,
542542
E0800: 0800,
543+
E0801: 0801,
543544
);
544545
)
545546
}

compiler/rustc_hir_analysis/messages.ftl

+6
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,12 @@ hir_analysis_inherent_ty_outside_relevant = cannot define inherent `impl` for a
234234
.help = consider moving this inherent impl into the crate defining the type if possible
235235
.span_help = alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items
236236
237+
hir_analysis_invalid_generic_receiver_ty = invalid generic `self` parameter type: `{$receiver_ty}`
238+
.note = type of `self` must not be a method generic parameter type
239+
240+
hir_analysis_invalid_generic_receiver_ty_help =
241+
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`)
242+
237243
hir_analysis_invalid_receiver_ty = invalid `self` parameter type: `{$receiver_ty}`
238244
.note = type of `self` must be `Self` or a type that dereferences to it
239245

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::{FxHashSet, FxIndexMap, FxIndexSet};
67
use rustc_errors::codes::*;
78
use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err};
8-
use rustc_hir::ItemKind;
99
use rustc_hir::def::{DefKind, Res};
1010
use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
1111
use rustc_hir::lang_items::LangItem;
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;
@@ -378,7 +379,7 @@ fn check_trait_item<'tcx>(
378379
_ => (None, trait_item.span),
379380
};
380381
check_dyn_incompatible_self_trait_by_name(tcx, trait_item);
381-
let mut res = check_associated_item(tcx, def_id, span, method_sig);
382+
let mut res = check_associated_item(tcx, def_id, span, method_sig, None);
382383

383384
if matches!(trait_item.kind, hir::TraitItemKind::Fn(..)) {
384385
for &assoc_ty_def_id in tcx.associated_types_for_impl_traits_in_associated_fn(def_id) {
@@ -387,6 +388,7 @@ fn check_trait_item<'tcx>(
387388
assoc_ty_def_id.expect_local(),
388389
tcx.def_span(assoc_ty_def_id),
389390
None,
391+
None,
390392
));
391393
}
392394
}
@@ -904,8 +906,13 @@ fn check_impl_item<'tcx>(
904906
hir::ImplItemKind::Type(ty) if ty.span != DUMMY_SP => (None, ty.span),
905907
_ => (None, impl_item.span),
906908
};
907-
908-
check_associated_item(tcx, impl_item.owner_id.def_id, span, method_sig)
909+
check_associated_item(
910+
tcx,
911+
impl_item.owner_id.def_id,
912+
span,
913+
method_sig,
914+
Some(impl_item.generics),
915+
)
909916
}
910917

911918
fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(), ErrorGuaranteed> {
@@ -1038,6 +1045,7 @@ fn check_associated_item(
10381045
item_id: LocalDefId,
10391046
span: Span,
10401047
sig_if_method: Option<&hir::FnSig<'_>>,
1048+
generics: Option<&hir::Generics<'_>>,
10411049
) -> Result<(), ErrorGuaranteed> {
10421050
let loc = Some(WellFormedLoc::Ty(item_id));
10431051
enter_wf_checking_ctxt(tcx, span, item_id, |wfcx| {
@@ -1070,7 +1078,7 @@ fn check_associated_item(
10701078
hir_sig.decl,
10711079
item.def_id.expect_local(),
10721080
);
1073-
check_method_receiver(wfcx, hir_sig, item, self_ty)
1081+
check_method_receiver(wfcx, hir_sig, item, self_ty, generics)
10741082
}
10751083
ty::AssocKind::Type => {
10761084
if let ty::AssocItemContainer::TraitContainer = item.container {
@@ -1692,6 +1700,7 @@ fn check_method_receiver<'tcx>(
16921700
fn_sig: &hir::FnSig<'_>,
16931701
method: ty::AssocItem,
16941702
self_ty: Ty<'tcx>,
1703+
generics: Option<&hir::Generics<'_>>,
16951704
) -> Result<(), ErrorGuaranteed> {
16961705
let tcx = wfcx.tcx();
16971706

@@ -1726,7 +1735,9 @@ fn check_method_receiver<'tcx>(
17261735
None
17271736
};
17281737

1729-
if !receiver_is_valid(wfcx, span, receiver_ty, self_ty, arbitrary_self_types_level) {
1738+
let receiver_validity =
1739+
receiver_is_valid(wfcx, span, receiver_ty, self_ty, arbitrary_self_types_level, generics);
1740+
if let Err(receiver_validity_err) = receiver_validity {
17301741
return Err(match arbitrary_self_types_level {
17311742
// Wherever possible, emit a message advising folks that the features
17321743
// `arbitrary_self_types` or `arbitrary_self_types_pointers` might
@@ -1737,7 +1748,9 @@ fn check_method_receiver<'tcx>(
17371748
receiver_ty,
17381749
self_ty,
17391750
Some(ArbitrarySelfTypesLevel::Basic),
1740-
) =>
1751+
generics,
1752+
)
1753+
.is_ok() =>
17411754
{
17421755
// Report error; would have worked with `arbitrary_self_types`.
17431756
feature_err(
@@ -1759,7 +1772,9 @@ fn check_method_receiver<'tcx>(
17591772
receiver_ty,
17601773
self_ty,
17611774
Some(ArbitrarySelfTypesLevel::WithPointers),
1762-
) =>
1775+
generics,
1776+
)
1777+
.is_ok() =>
17631778
{
17641779
// Report error; would have worked with `arbitrary_self_types_pointers`.
17651780
feature_err(
@@ -1777,13 +1792,53 @@ fn check_method_receiver<'tcx>(
17771792
_ =>
17781793
// Report error; would not have worked with `arbitrary_self_types[_pointers]`.
17791794
{
1780-
tcx.dcx().emit_err(errors::InvalidReceiverTy { span, receiver_ty })
1795+
match receiver_validity_err {
1796+
ReceiverValidityError::DoesNotDeref => {
1797+
tcx.dcx().emit_err(errors::InvalidReceiverTy { span, receiver_ty })
1798+
}
1799+
ReceiverValidityError::MethodGenericParamUsed => {
1800+
tcx.dcx().emit_err(errors::InvalidGenericReceiverTy { span, receiver_ty })
1801+
}
1802+
}
17811803
}
17821804
});
17831805
}
17841806
Ok(())
17851807
}
17861808

1809+
/// Error cases which may be returned from `receiver_is_valid`. These error
1810+
/// cases are generated in this function as they may be unearthed as we explore
1811+
/// the `autoderef` chain, but they're converted to diagnostics in the caller.
1812+
enum ReceiverValidityError {
1813+
/// The self type does not get to the receiver type by following the
1814+
/// autoderef chain.
1815+
DoesNotDeref,
1816+
/// A type was found which is a method type parameter, and that's not allowed.
1817+
MethodGenericParamUsed,
1818+
}
1819+
1820+
/// Confirms that a type is not a type parameter referring to one of the
1821+
/// method's type params.
1822+
fn confirm_type_is_not_a_method_generic_param(
1823+
ty: Ty<'_>,
1824+
method_generics: Option<&hir::Generics<'_>>,
1825+
) -> Result<(), ReceiverValidityError> {
1826+
if let ty::Param(param) = ty.kind() {
1827+
if let Some(generics) = method_generics {
1828+
if generics
1829+
.params
1830+
.iter()
1831+
.filter(|g| matches!(g.kind, GenericParamKind::Type { .. }))
1832+
.map(|g| g.name.ident().name)
1833+
.contains(&param.name)
1834+
{
1835+
return Err(ReceiverValidityError::MethodGenericParamUsed);
1836+
}
1837+
}
1838+
}
1839+
Ok(())
1840+
}
1841+
17871842
/// Returns whether `receiver_ty` would be considered a valid receiver type for `self_ty`. If
17881843
/// `arbitrary_self_types` is enabled, `receiver_ty` must transitively deref to `self_ty`, possibly
17891844
/// through a `*const/mut T` raw pointer if `arbitrary_self_types_pointers` is also enabled.
@@ -1799,7 +1854,8 @@ fn receiver_is_valid<'tcx>(
17991854
receiver_ty: Ty<'tcx>,
18001855
self_ty: Ty<'tcx>,
18011856
arbitrary_self_types_enabled: Option<ArbitrarySelfTypesLevel>,
1802-
) -> bool {
1857+
generics: Option<&hir::Generics<'_>>,
1858+
) -> Result<(), ReceiverValidityError> {
18031859
let infcx = wfcx.infcx;
18041860
let tcx = wfcx.tcx();
18051861
let cause =
@@ -1811,9 +1867,11 @@ fn receiver_is_valid<'tcx>(
18111867
ocx.eq(&cause, wfcx.param_env, self_ty, receiver_ty)?;
18121868
if ocx.select_all_or_error().is_empty() { Ok(()) } else { Err(NoSolution) }
18131869
}) {
1814-
return true;
1870+
return Ok(());
18151871
}
18161872

1873+
confirm_type_is_not_a_method_generic_param(receiver_ty, generics)?;
1874+
18171875
let mut autoderef = Autoderef::new(infcx, wfcx.param_env, wfcx.body_def_id, span, receiver_ty);
18181876

18191877
// The `arbitrary_self_types_pointers` feature allows raw pointer receivers like `self: *const Self`.
@@ -1830,6 +1888,8 @@ fn receiver_is_valid<'tcx>(
18301888
potential_self_ty, self_ty
18311889
);
18321890

1891+
confirm_type_is_not_a_method_generic_param(potential_self_ty, generics)?;
1892+
18331893
// Check if the self type unifies. If it does, then commit the result
18341894
// since it may have region side-effects.
18351895
if let Ok(()) = wfcx.infcx.commit_if_ok(|_| {
@@ -1838,7 +1898,7 @@ fn receiver_is_valid<'tcx>(
18381898
if ocx.select_all_or_error().is_empty() { Ok(()) } else { Err(NoSolution) }
18391899
}) {
18401900
wfcx.register_obligations(autoderef.into_obligations());
1841-
return true;
1901+
return Ok(());
18421902
}
18431903

18441904
// Without `feature(arbitrary_self_types)`, we require that each step in the
@@ -1865,7 +1925,7 @@ fn receiver_is_valid<'tcx>(
18651925
}
18661926

18671927
debug!("receiver_is_valid: type `{:?}` does not deref to `{:?}`", receiver_ty, self_ty);
1868-
false
1928+
Err(ReceiverValidityError::DoesNotDeref)
18691929
}
18701930

18711931
fn receiver_is_implemented<'tcx>(

compiler/rustc_hir_analysis/src/errors.rs

+10
Original file line numberDiff line numberDiff line change
@@ -1623,6 +1623,16 @@ pub(crate) struct InvalidReceiverTy<'tcx> {
16231623
pub receiver_ty: Ty<'tcx>,
16241624
}
16251625

1626+
#[derive(Diagnostic)]
1627+
#[diag(hir_analysis_invalid_generic_receiver_ty, code = E0801)]
1628+
#[note]
1629+
#[help(hir_analysis_invalid_generic_receiver_ty_help)]
1630+
pub(crate) struct InvalidGenericReceiverTy<'tcx> {
1631+
#[primary_span]
1632+
pub span: Span,
1633+
pub receiver_ty: Ty<'tcx>,
1634+
}
1635+
16261636
#[derive(Diagnostic)]
16271637
#[diag(hir_analysis_cmse_inputs_stack_spill, code = E0798)]
16281638
#[note]

compiler/rustc_hir_typeck/src/method/confirm.rs

-3
Original file line numberDiff line numberDiff line change
@@ -533,9 +533,6 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
533533
self.register_predicates(obligations);
534534
}
535535
Err(terr) => {
536-
// FIXME(arbitrary_self_types): We probably should limit the
537-
// situations where this can occur by adding additional restrictions
538-
// to the feature, like the self type can't reference method args.
539536
if self.tcx.features().arbitrary_self_types() {
540537
self.err_ctxt()
541538
.report_mismatched_types(

src/tools/tidy/src/issues.txt

-1
Original file line numberDiff line numberDiff line change
@@ -4102,7 +4102,6 @@ ui/type-alias-impl-trait/issue-53678-coroutine-and-const-fn.rs
41024102
ui/type-alias-impl-trait/issue-55099-lifetime-inference.rs
41034103
ui/type-alias-impl-trait/issue-57188-associate-impl-capture.rs
41044104
ui/type-alias-impl-trait/issue-57611-trait-alias.rs
4105-
ui/type-alias-impl-trait/issue-57700.rs
41064105
ui/type-alias-impl-trait/issue-57807-associated-type.rs
41074106
ui/type-alias-impl-trait/issue-57961.rs
41084107
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 call non-const fn `<R as Deref>::deref` in constant function

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

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

18-
error[E0658]: `R` cannot be used as the type of `self` without the `arbitrary_self_types` feature
18+
error[E0801]: invalid generic `self` parameter type: `R`
1919
--> $DIR/arbitrary-self-from-method-substs-ice.rs:10:49
2020
|
2121
LL | const fn get<R: Deref<Target = Self>>(self: R) -> u32 {
2222
| ^
2323
|
24-
= note: see issue #44874 <https://github.com/rust-lang/rust/issues/44874> for more information
25-
= help: add `#![feature(arbitrary_self_types)]` to the crate attributes to enable
26-
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
27-
= 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`)
24+
= note: type of `self` must not be a method generic parameter type
25+
= 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`)
2826

2927
error: aborting due to 3 previous errors
3028

31-
Some errors have detailed explanations: E0015, E0493, E0658.
29+
Some errors have detailed explanations: E0015, E0493, E0801.
3230
For more information about an error, try `rustc --explain E0015`.

0 commit comments

Comments
 (0)