Skip to content

Commit fcb533d

Browse files
committed
Receiver trait with target: support NotNull Receiver.
This commit allows us to support Receivers which are types that may have methods implemented upon them in future. In the event of a conflict between SmartPtr::SomeMethod and Pointee::Method, favor Pointee::Method because it was probably added first. Emit a warning (yet to do). The rationale here is that we want to be able to support struct Foo; impl Foo { fn method(self: NotNull<Foo>) { } } while reserving the right to add future methods to NotNull. To do that, we have to ensure that any future methods added to NotNull do not shadow methods added to Foo. This works by switching some existing hard errors into warnings; where we previously had two possible method candidates we will now pick the "innermost" candidate. This doesn't currently quite work as it produces problems with generics.
1 parent 62501e4 commit fcb533d

File tree

4 files changed

+112
-18
lines changed

4 files changed

+112
-18
lines changed

compiler/rustc_hir_typeck/src/method/probe.rs

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ pub(crate) struct Candidate<'tcx> {
135135
pub(crate) item: ty::AssocItem,
136136
pub(crate) kind: CandidateKind<'tcx>,
137137
pub(crate) import_ids: SmallVec<[LocalDefId; 1]>,
138+
depth: usize,
138139
}
139140

140141
#[derive(Debug, Clone)]
@@ -670,11 +671,15 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
670671

671672
fn assemble_inherent_candidates(&mut self) {
672673
for step in self.steps.iter() {
673-
self.assemble_probe(&step.self_ty);
674+
self.assemble_probe(&step.self_ty, step.autoderefs);
674675
}
675676
}
676677

677-
fn assemble_probe(&mut self, self_ty: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>) {
678+
fn assemble_probe(
679+
&mut self,
680+
self_ty: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>,
681+
depth: usize,
682+
) {
678683
debug!("assemble_probe: self_ty={:?}", self_ty);
679684
let raw_self_ty = self_ty.value.value;
680685
match *raw_self_ty.kind() {
@@ -699,27 +704,27 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
699704
let (QueryResponse { value: generalized_self_ty, .. }, _ignored_var_values) =
700705
self.fcx.instantiate_canonical_with_fresh_inference_vars(self.span, self_ty);
701706

702-
self.assemble_inherent_candidates_from_object(generalized_self_ty);
703-
self.assemble_inherent_impl_candidates_for_type(p.def_id());
707+
self.assemble_inherent_candidates_from_object(generalized_self_ty, depth);
708+
self.assemble_inherent_impl_candidates_for_type(p.def_id(), depth);
704709
if self.tcx.has_attr(p.def_id(), sym::rustc_has_incoherent_inherent_impls) {
705-
self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty);
710+
self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty, depth);
706711
}
707712
}
708713
ty::Adt(def, _) => {
709714
let def_id = def.did();
710-
self.assemble_inherent_impl_candidates_for_type(def_id);
715+
self.assemble_inherent_impl_candidates_for_type(def_id, depth);
711716
if self.tcx.has_attr(def_id, sym::rustc_has_incoherent_inherent_impls) {
712-
self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty);
717+
self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty, depth);
713718
}
714719
}
715720
ty::Foreign(did) => {
716-
self.assemble_inherent_impl_candidates_for_type(did);
721+
self.assemble_inherent_impl_candidates_for_type(did, depth);
717722
if self.tcx.has_attr(did, sym::rustc_has_incoherent_inherent_impls) {
718-
self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty);
723+
self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty, depth);
719724
}
720725
}
721726
ty::Param(p) => {
722-
self.assemble_inherent_candidates_from_param(p);
727+
self.assemble_inherent_candidates_from_param(p, depth);
723728
}
724729
ty::Bool
725730
| ty::Char
@@ -732,28 +737,28 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
732737
| ty::RawPtr(_)
733738
| ty::Ref(..)
734739
| ty::Never
735-
| ty::Tuple(..) => self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty),
740+
| ty::Tuple(..) => self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty, depth),
736741
_ => {}
737742
}
738743
}
739744

740-
fn assemble_inherent_candidates_for_incoherent_ty(&mut self, self_ty: Ty<'tcx>) {
745+
fn assemble_inherent_candidates_for_incoherent_ty(&mut self, self_ty: Ty<'tcx>, depth: usize) {
741746
let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsCandidateKey) else {
742747
bug!("unexpected incoherent type: {:?}", self_ty)
743748
};
744749
for &impl_def_id in self.tcx.incoherent_impls(simp) {
745-
self.assemble_inherent_impl_probe(impl_def_id);
750+
self.assemble_inherent_impl_probe(impl_def_id, depth);
746751
}
747752
}
748753

749-
fn assemble_inherent_impl_candidates_for_type(&mut self, def_id: DefId) {
754+
fn assemble_inherent_impl_candidates_for_type(&mut self, def_id: DefId, depth: usize) {
750755
let impl_def_ids = self.tcx.at(self.span).inherent_impls(def_id);
751756
for &impl_def_id in impl_def_ids.iter() {
752-
self.assemble_inherent_impl_probe(impl_def_id);
757+
self.assemble_inherent_impl_probe(impl_def_id, depth);
753758
}
754759
}
755760

756-
fn assemble_inherent_impl_probe(&mut self, impl_def_id: DefId) {
761+
fn assemble_inherent_impl_probe(&mut self, impl_def_id: DefId, depth: usize) {
757762
if !self.impl_dups.insert(impl_def_id) {
758763
return; // already visited
759764
}
@@ -801,13 +806,14 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
801806
item,
802807
kind: InherentImplCandidate(impl_args, obligations),
803808
import_ids: smallvec![],
809+
depth,
804810
},
805811
true,
806812
);
807813
}
808814
}
809815

810-
fn assemble_inherent_candidates_from_object(&mut self, self_ty: Ty<'tcx>) {
816+
fn assemble_inherent_candidates_from_object(&mut self, self_ty: Ty<'tcx>, depth: usize) {
811817
debug!("assemble_inherent_candidates_from_object(self_ty={:?})", self_ty);
812818

813819
let principal = match self_ty.kind() {
@@ -850,13 +856,14 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
850856
item,
851857
kind: ObjectCandidate,
852858
import_ids: smallvec![],
859+
depth,
853860
},
854861
true,
855862
);
856863
});
857864
}
858865

859-
fn assemble_inherent_candidates_from_param(&mut self, param_ty: ty::ParamTy) {
866+
fn assemble_inherent_candidates_from_param(&mut self, param_ty: ty::ParamTy, depth: usize) {
860867
// FIXME: do we want to commit to this behavior for param bounds?
861868
debug!("assemble_inherent_candidates_from_param(param_ty={:?})", param_ty);
862869

@@ -897,6 +904,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
897904
item,
898905
kind: WhereClauseCandidate(poly_trait_ref),
899906
import_ids: smallvec![],
907+
depth,
900908
},
901909
true,
902910
);
@@ -1014,6 +1022,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
10141022
item,
10151023
import_ids: import_ids.clone(),
10161024
kind: TraitCandidate(new_trait_ref),
1025+
depth: 0usize,
10171026
},
10181027
false,
10191028
);
@@ -1042,6 +1051,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
10421051
item,
10431052
import_ids: import_ids.clone(),
10441053
kind: TraitCandidate(trait_ref),
1054+
depth: 0usize,
10451055
},
10461056
false,
10471057
);
@@ -1361,6 +1371,28 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
13611371
});
13621372
}
13631373

1374+
// We want to avoid compatibility breaks if we have SmartPtr<Concrete>,
1375+
// the user is calling smart_ptr.wardrobe() expecting to call
1376+
// Concrete::wardrobe(), but then SmartPtr adds a wardrobe() method.
1377+
// So if there are multiple applicable candidates, and a single one
1378+
// of them involved more deref/receiver chain hops than all the others,
1379+
// we pick that one: but show a warning.
1380+
if applicable_candidates.len() > 1 {
1381+
let greatest_depth =
1382+
applicable_candidates.iter().map(|(candidate, _)| candidate.depth).max().unwrap();
1383+
let number_of_candidates_with_greatest_depth = applicable_candidates
1384+
.iter()
1385+
.filter(|(candidate, _)| candidate.depth == greatest_depth)
1386+
.count();
1387+
if number_of_candidates_with_greatest_depth == 1 {
1388+
// FIXME: emit warning here, unless this is something we should
1389+
// have done earlier e.g. in wfcheck.rs
1390+
info!("Multiple candidates detected, picking the innermost");
1391+
// Emit a warning, and retain a single candidate
1392+
applicable_candidates.retain(|(candidate, _)| candidate.depth == greatest_depth);
1393+
}
1394+
}
1395+
13641396
if applicable_candidates.len() > 1 {
13651397
let sources = candidates.iter().map(|p| self.candidate_source(p, self_ty)).collect();
13661398
return Some(Err(MethodError::Ambiguity(sources)));

library/core/src/ptr/non_null.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ use crate::ops::{CoerceUnsized, DispatchFromDyn};
1010
use crate::ptr::Unique;
1111
use crate::slice::{self, SliceIndex};
1212

13+
#[cfg(not(bootstrap))]
14+
use crate::ops::Receiver;
15+
1316
/// `*mut T` but non-zero and [covariant].
1417
///
1518
/// This is often the correct thing to use when building data structures using
@@ -846,3 +849,9 @@ impl<T: ?Sized> From<&T> for NonNull<T> {
846849
unsafe { NonNull { pointer: reference as *const T } }
847850
}
848851
}
852+
853+
#[cfg(not(bootstrap))]
854+
#[unstable(feature = "receiver_trait", issue = "none")]
855+
impl<T: ?Sized> Receiver for NonNull<T> {
856+
type Target = T;
857+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// run-pass
2+
3+
#![feature(receiver_trait)]
4+
#![allow(dead_code)]
5+
6+
struct Foo;
7+
struct Ptr<T>(T);
8+
9+
impl<T> std::ops::Receiver for Ptr<T> {
10+
type Target = T;
11+
}
12+
13+
impl<T> Ptr<T> {
14+
fn method_ref(&self) -> u32 { 2 }
15+
fn method_ref2(&self) -> u32 { 2 }
16+
fn method_value(self) -> u32 { 2 }
17+
}
18+
19+
impl Foo {
20+
fn method_ref(self: Ptr<Self>) -> u32 { 1 }
21+
fn method_ref2(self: &Ptr<Self>) -> u32 { 1 }
22+
fn method_value(self: Ptr<Self>) -> u32 { 1 }
23+
}
24+
25+
fn main() {
26+
let a = Ptr(Foo);
27+
assert_eq!(a.method_ref(), 1);
28+
let a = Ptr(Foo);
29+
assert_eq!(a.method_ref2(), 1);
30+
let a = Ptr(Foo);
31+
assert_eq!(a.method_value(), 1);
32+
}

tests/ui/self/not_null_self.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// run-pass
2+
3+
#![feature(receiver_trait)]
4+
5+
use std::ptr::NonNull;
6+
7+
struct Foo(usize);
8+
9+
impl Foo {
10+
fn bar(self: NonNull<Self>) -> i32 { 3 }
11+
fn as_mut(self: &mut NonNull<Self>) -> i32 { 4 }
12+
}
13+
14+
fn main() {
15+
let mut foo = Foo(3);
16+
let mut ptr = std::ptr::NonNull::new(&mut foo as *mut Foo).unwrap();
17+
// FIXME: Should call Foo::as_mut but warn about ambiguity
18+
// once we have implemented warnings
19+
assert_eq!(ptr.as_mut(), 4);
20+
assert_eq!(ptr.bar(), 3);
21+
}

0 commit comments

Comments
 (0)