Skip to content

Commit f4bc10b

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 55b7f8e commit f4bc10b

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
@@ -238,6 +238,12 @@ hir_analysis_inherent_ty_outside_relevant = cannot define inherent `impl` for a
238238
.help = consider moving this inherent impl into the crate defining the type if possible
239239
.span_help = alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items
240240
241+
hir_analysis_invalid_generic_receiver_ty = invalid generic `self` parameter type: `{$receiver_ty}`
242+
.note = type of `self` must not be a method generic parameter type
243+
244+
hir_analysis_invalid_generic_receiver_ty_help =
245+
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`)
246+
241247
hir_analysis_invalid_receiver_ty = invalid `self` parameter type: `{$receiver_ty}`
242248
.note = type of `self` must be `Self` or a type that dereferences to it
243249

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;
@@ -377,7 +378,7 @@ fn check_trait_item<'tcx>(
377378
_ => (None, trait_item.span),
378379
};
379380
check_dyn_incompatible_self_trait_by_name(tcx, trait_item);
380-
let mut res = check_associated_item(tcx, def_id, span, method_sig);
381+
let mut res = check_associated_item(tcx, def_id, span, method_sig, None);
381382

382383
if matches!(trait_item.kind, hir::TraitItemKind::Fn(..)) {
383384
for &assoc_ty_def_id in tcx.associated_types_for_impl_traits_in_associated_fn(def_id) {
@@ -386,6 +387,7 @@ fn check_trait_item<'tcx>(
386387
assoc_ty_def_id.expect_local(),
387388
tcx.def_span(assoc_ty_def_id),
388389
None,
390+
None,
389391
));
390392
}
391393
}
@@ -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> {
@@ -1042,6 +1049,7 @@ fn check_associated_item(
10421049
item_id: LocalDefId,
10431050
span: Span,
10441051
sig_if_method: Option<&hir::FnSig<'_>>,
1052+
generics: Option<&hir::Generics<'_>>,
10451053
) -> Result<(), ErrorGuaranteed> {
10461054
let loc = Some(WellFormedLoc::Ty(item_id));
10471055
enter_wf_checking_ctxt(tcx, span, item_id, |wfcx| {
@@ -1074,7 +1082,7 @@ fn check_associated_item(
10741082
hir_sig.decl,
10751083
item.def_id.expect_local(),
10761084
);
1077-
check_method_receiver(wfcx, hir_sig, item, self_ty)
1085+
check_method_receiver(wfcx, hir_sig, item, self_ty, generics)
10781086
}
10791087
ty::AssocKind::Type => {
10801088
if let ty::AssocItemContainer::TraitContainer = item.container {
@@ -1672,6 +1680,7 @@ fn check_method_receiver<'tcx>(
16721680
fn_sig: &hir::FnSig<'_>,
16731681
method: ty::AssocItem,
16741682
self_ty: Ty<'tcx>,
1683+
generics: Option<&hir::Generics<'_>>,
16751684
) -> Result<(), ErrorGuaranteed> {
16761685
let tcx = wfcx.tcx();
16771686

@@ -1706,7 +1715,9 @@ fn check_method_receiver<'tcx>(
17061715
None
17071716
};
17081717

1709-
if !receiver_is_valid(wfcx, span, receiver_ty, self_ty, arbitrary_self_types_level) {
1718+
let receiver_validity =
1719+
receiver_is_valid(wfcx, span, receiver_ty, self_ty, arbitrary_self_types_level, generics);
1720+
if let Err(receiver_validity_err) = receiver_validity {
17101721
return Err(match arbitrary_self_types_level {
17111722
// Wherever possible, emit a message advising folks that the features
17121723
// `arbitrary_self_types` or `arbitrary_self_types_pointers` might
@@ -1717,7 +1728,9 @@ fn check_method_receiver<'tcx>(
17171728
receiver_ty,
17181729
self_ty,
17191730
Some(ArbitrarySelfTypesLevel::Basic),
1720-
) =>
1731+
generics,
1732+
)
1733+
.is_ok() =>
17211734
{
17221735
// Report error; would have worked with `arbitrary_self_types`.
17231736
feature_err(
@@ -1739,7 +1752,9 @@ fn check_method_receiver<'tcx>(
17391752
receiver_ty,
17401753
self_ty,
17411754
Some(ArbitrarySelfTypesLevel::WithPointers),
1742-
) =>
1755+
generics,
1756+
)
1757+
.is_ok() =>
17431758
{
17441759
// Report error; would have worked with `arbitrary_self_types_pointers`.
17451760
feature_err(
@@ -1757,13 +1772,53 @@ fn check_method_receiver<'tcx>(
17571772
_ =>
17581773
// Report error; would not have worked with `arbitrary_self_types[_pointers]`.
17591774
{
1760-
tcx.dcx().emit_err(errors::InvalidReceiverTy { span, receiver_ty })
1775+
match receiver_validity_err {
1776+
ReceiverValidityError::DoesNotDeref => {
1777+
tcx.dcx().emit_err(errors::InvalidReceiverTy { span, receiver_ty })
1778+
}
1779+
ReceiverValidityError::MethodGenericParamUsed => {
1780+
tcx.dcx().emit_err(errors::InvalidGenericReceiverTy { span, receiver_ty })
1781+
}
1782+
}
17611783
}
17621784
});
17631785
}
17641786
Ok(())
17651787
}
17661788

1789+
/// Error cases which may be returned from `receiver_is_valid`. These error
1790+
/// cases are generated in this function as they may be unearthed as we explore
1791+
/// the `autoderef` chain, but they're converted to diagnostics in the caller.
1792+
enum ReceiverValidityError {
1793+
/// The self type does not get to the receiver type by following the
1794+
/// autoderef chain.
1795+
DoesNotDeref,
1796+
/// A type was found which is a method type parameter, and that's not allowed.
1797+
MethodGenericParamUsed,
1798+
}
1799+
1800+
/// Confirms that a type is not a type parameter referring to one of the
1801+
/// method's type params.
1802+
fn confirm_type_is_not_a_method_generic_param(
1803+
ty: Ty<'_>,
1804+
method_generics: Option<&hir::Generics<'_>>,
1805+
) -> Result<(), ReceiverValidityError> {
1806+
if let ty::Param(param) = ty.kind() {
1807+
if let Some(generics) = method_generics {
1808+
if generics
1809+
.params
1810+
.iter()
1811+
.filter(|g| matches!(g.kind, GenericParamKind::Type { .. }))
1812+
.map(|g| g.name.ident().name)
1813+
.contains(&param.name)
1814+
{
1815+
return Err(ReceiverValidityError::MethodGenericParamUsed);
1816+
}
1817+
}
1818+
}
1819+
Ok(())
1820+
}
1821+
17671822
/// Returns whether `receiver_ty` would be considered a valid receiver type for `self_ty`. If
17681823
/// `arbitrary_self_types` is enabled, `receiver_ty` must transitively deref to `self_ty`, possibly
17691824
/// through a `*const/mut T` raw pointer if `arbitrary_self_types_pointers` is also enabled.
@@ -1779,7 +1834,8 @@ fn receiver_is_valid<'tcx>(
17791834
receiver_ty: Ty<'tcx>,
17801835
self_ty: Ty<'tcx>,
17811836
arbitrary_self_types_enabled: Option<ArbitrarySelfTypesLevel>,
1782-
) -> bool {
1837+
generics: Option<&hir::Generics<'_>>,
1838+
) -> Result<(), ReceiverValidityError> {
17831839
let infcx = wfcx.infcx;
17841840
let tcx = wfcx.tcx();
17851841
let cause =
@@ -1791,9 +1847,11 @@ fn receiver_is_valid<'tcx>(
17911847
ocx.eq(&cause, wfcx.param_env, self_ty, receiver_ty)?;
17921848
if ocx.select_all_or_error().is_empty() { Ok(()) } else { Err(NoSolution) }
17931849
}) {
1794-
return true;
1850+
return Ok(());
17951851
}
17961852

1853+
confirm_type_is_not_a_method_generic_param(receiver_ty, generics)?;
1854+
17971855
let mut autoderef = Autoderef::new(infcx, wfcx.param_env, wfcx.body_def_id, span, receiver_ty);
17981856

17991857
// The `arbitrary_self_types_pointers` feature allows raw pointer receivers like `self: *const Self`.
@@ -1810,6 +1868,8 @@ fn receiver_is_valid<'tcx>(
18101868
potential_self_ty, self_ty
18111869
);
18121870

1871+
confirm_type_is_not_a_method_generic_param(potential_self_ty, generics)?;
1872+
18131873
// Check if the self type unifies. If it does, then commit the result
18141874
// since it may have region side-effects.
18151875
if let Ok(()) = wfcx.infcx.commit_if_ok(|_| {
@@ -1818,7 +1878,7 @@ fn receiver_is_valid<'tcx>(
18181878
if ocx.select_all_or_error().is_empty() { Ok(()) } else { Err(NoSolution) }
18191879
}) {
18201880
wfcx.register_obligations(autoderef.into_obligations());
1821-
return true;
1881+
return Ok(());
18221882
}
18231883

18241884
// Without `feature(arbitrary_self_types)`, we require that each step in the
@@ -1845,7 +1905,7 @@ fn receiver_is_valid<'tcx>(
18451905
}
18461906

18471907
debug!("receiver_is_valid: type `{:?}` does not deref to `{:?}`", receiver_ty, self_ty);
1848-
false
1908+
Err(ReceiverValidityError::DoesNotDeref)
18491909
}
18501910

18511911
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_effects_without_next_solver)]
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
@@ -19,18 +19,16 @@ LL | const fn get<R: Deref<Target = Self>>(self: R) -> u32 {
1919
LL | }
2020
| - value is dropped here
2121

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

3331
error: aborting due to 3 previous errors
3432

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

0 commit comments

Comments
 (0)