Skip to content

Commit ef2c5e1

Browse files
committed
Arbitrary self types v2: deshadow quicker
A previous commit added a search for certain types of "shadowing" situation where one method (in an outer smart pointer type, typically) might hide or shadow the method in the pointee. Performance investigation showed that the naïve approach is too slow - this commit speeds it up, while being functionally the same. This still does not actually cause the deshadowing check to emit any errors; that comes in a subsequent commit which is where all the tests live.
1 parent 51d4cc7 commit ef2c5e1

File tree

1 file changed

+133
-16
lines changed
  • compiler/rustc_hir_typeck/src/method

1 file changed

+133
-16
lines changed

compiler/rustc_hir_typeck/src/method/probe.rs

+133-16
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ pub(crate) struct ProbeContext<'a, 'tcx> {
8585
/// machinery, since we don't particularly care about, for example, similarly named
8686
/// candidates if we're *reporting* similarly named candidates.
8787
is_suggestion: IsSuggestion,
88+
89+
/// Number of times we've hopped along the chain of `Receiver::Target`.
90+
/// Used to spot cases where an "outer" method in a smart pointer might
91+
/// "shadow" a pre-existing method in the pointee.
92+
receiver_trait_derefs: usize,
8893
}
8994

9095
impl<'a, 'tcx> Deref for ProbeContext<'a, 'tcx> {
@@ -99,6 +104,7 @@ pub(crate) struct Candidate<'tcx> {
99104
pub(crate) item: ty::AssocItem,
100105
pub(crate) kind: CandidateKind<'tcx>,
101106
pub(crate) import_ids: SmallVec<[LocalDefId; 1]>,
107+
receiver_trait_derefs: usize,
102108
}
103109

104110
#[derive(Debug, Clone)]
@@ -170,6 +176,30 @@ struct PickDiagHints<'a, 'tcx> {
170176
)>,
171177
}
172178

179+
/// Criteria to apply when searching for a given Pick. This is used during
180+
/// the search for potentially shadowed methods to ensure we don't search
181+
/// more candidates than strictly necessary.
182+
#[derive(Debug)]
183+
struct PickConstraintsForShadowed {
184+
autoderefs: usize,
185+
receiver_trait_derefs: usize,
186+
def_id: DefId,
187+
}
188+
189+
impl PickConstraintsForShadowed {
190+
fn may_shadow_based_on_autoderefs(&self, autoderefs: usize) -> bool {
191+
autoderefs == self.autoderefs
192+
}
193+
194+
fn may_shadow_based_on_receiver_trait_derefs(&self, receiver_trait_derefs: usize) -> bool {
195+
receiver_trait_derefs != self.receiver_trait_derefs
196+
}
197+
198+
fn may_shadow_based_on_defid(&self, def_id: DefId) -> bool {
199+
def_id != self.def_id
200+
}
201+
}
202+
173203
#[derive(Debug, Clone)]
174204
pub(crate) struct Pick<'tcx> {
175205
pub item: ty::AssocItem,
@@ -189,6 +219,10 @@ pub(crate) struct Pick<'tcx> {
189219

190220
/// Unstable candidates alongside the stable ones.
191221
unstable_candidates: Vec<(Candidate<'tcx>, Symbol)>,
222+
223+
/// Number of jumps along the `Receiver::Target` chain we followed
224+
/// to identify this method. Used only for deshadowing errors.
225+
pub receiver_trait_derefs: usize,
192226
}
193227

194228
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -673,6 +707,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
673707
static_candidates: RefCell::new(Vec::new()),
674708
scope_expr_id,
675709
is_suggestion,
710+
receiver_trait_derefs: 0usize,
676711
}
677712
}
678713

@@ -705,7 +740,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
705740
import_ids: SmallVec<[LocalDefId; 1]>,
706741
is_inherent: bool,
707742
) {
708-
let candidate = Candidate { item, kind, import_ids };
743+
let candidate =
744+
Candidate { item, kind, import_ids, receiver_trait_derefs: self.receiver_trait_derefs };
709745
let is_accessible = if let Some(name) = self.method_name {
710746
let item = candidate.item;
711747
let hir_id = self.tcx.local_def_id_to_hir_id(self.body_id);
@@ -728,6 +764,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
728764

729765
fn assemble_inherent_candidates(&mut self) {
730766
for step in self.steps.iter() {
767+
self.receiver_trait_derefs = step.autoderefs;
731768
self.assemble_probe(&step.self_ty);
732769
}
733770
}
@@ -1195,8 +1232,13 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
11951232
return Some(by_value_pick);
11961233
}
11971234

1198-
let autoref_pick =
1199-
self.pick_autorefd_method(step, self_ty, hir::Mutability::Not, pick_diag_hints);
1235+
let autoref_pick = self.pick_autorefd_method(
1236+
step,
1237+
self_ty,
1238+
hir::Mutability::Not,
1239+
pick_diag_hints,
1240+
None,
1241+
);
12001242
// Check for shadowing of a by-mut-ref method by a by-reference method (see comments on check_for_shadowing)
12011243
if let Some(autoref_pick) = autoref_pick {
12021244
if let Ok(autoref_pick) = autoref_pick.as_ref() {
@@ -1237,9 +1279,15 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
12371279
// in the pointee. In practice, methods taking raw pointers
12381280
// are rare, and it seems that it should be easily possible
12391281
// to avoid such compatibility breaks.
1240-
self.pick_autorefd_method(step, self_ty, hir::Mutability::Mut, pick_diag_hints)
1241-
.or_else(|| self.pick_const_ptr_method(step, self_ty, pick_diag_hints))
1242-
.or_else(|| self.pick_reborrow_pin_method(step, self_ty, pick_diag_hints))
1282+
self.pick_autorefd_method(
1283+
step,
1284+
self_ty,
1285+
hir::Mutability::Mut,
1286+
pick_diag_hints,
1287+
None,
1288+
)
1289+
.or_else(|| self.pick_const_ptr_method(step, self_ty, pick_diag_hints))
1290+
.or_else(|| self.pick_reborrow_pin_method(step, self_ty, pick_diag_hints))
12431291
})
12441292
}
12451293

@@ -1260,7 +1308,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
12601308
/// Report an error in this case.
12611309
fn check_for_shadowed_autorefd_method(
12621310
&self,
1263-
_possible_shadower: &Pick<'tcx>,
1311+
possible_shadower: &Pick<'tcx>,
12641312
step: &CandidateStep<'tcx>,
12651313
self_ty: Ty<'tcx>,
12661314
mutbl: hir::Mutability,
@@ -1274,8 +1322,54 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
12741322
unstable_candidates: if track_unstable_candidates { Some(Vec::new()) } else { None },
12751323
unsatisfied_predicates: &mut Vec::new(),
12761324
};
1277-
let _potentially_shadowed_pick =
1278-
self.pick_autorefd_method(step, self_ty, mutbl, &mut pick_diag_hints);
1325+
// Set criteria for how we find methods possibly shadowed by 'possible_shadower'
1326+
let pick_constraints = PickConstraintsForShadowed {
1327+
// It's the same `self` type...
1328+
autoderefs: possible_shadower.autoderefs,
1329+
// ... but the method was found in an impl block determined
1330+
// by searching further along the Receiver chain than the other,
1331+
// showing that it's a smart pointer type causing the problem...
1332+
receiver_trait_derefs: possible_shadower.receiver_trait_derefs,
1333+
// ... and they don't end up pointing to the same item in the
1334+
// first place (could happen with things like blanket impls for T)
1335+
def_id: possible_shadower.item.def_id,
1336+
};
1337+
// A note on the autoderefs above. Within pick_by_value_method, an extra
1338+
// autoderef may be applied in order to reborrow a reference with
1339+
// a different lifetime. That seems as though it would break the
1340+
// logic of these constraints, since the number of autoderefs could
1341+
// no longer be used to identify the fundamental type of the receiver.
1342+
// However, this extra autoderef is applied only to by-value calls
1343+
// where the receiver is already a reference. So this situation would
1344+
// only occur in cases where the shadowing looks like this:
1345+
// ```
1346+
// struct A;
1347+
// impl A {
1348+
// fn foo(self: &&NonNull<A>) {}
1349+
// // note this is by DOUBLE reference
1350+
// }
1351+
// ```
1352+
// then we've come along and added this method to `NonNull`:
1353+
// ```
1354+
// fn foo(&self) // note this is by single reference
1355+
// ```
1356+
// and the call is:
1357+
// ```
1358+
// let bar = NonNull<Foo>;
1359+
// let bar = &foo;
1360+
// bar.foo();
1361+
// ```
1362+
// In these circumstances, the logic is wrong, and we wouldn't spot
1363+
// the shadowing, because the autoderef-based maths wouldn't line up.
1364+
// This is a niche case and we can live without generating an error
1365+
// in the case of such shadowing.
1366+
let _potentially_shadowed_pick = self.pick_autorefd_method(
1367+
step,
1368+
self_ty,
1369+
mutbl,
1370+
&mut pick_diag_hints,
1371+
Some(&pick_constraints),
1372+
);
12791373

12801374
// At the moment, this function does no checks. A future
12811375
// commit will fill out the body here.
@@ -1298,7 +1392,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
12981392
return None;
12991393
}
13001394

1301-
self.pick_method(self_ty, pick_diag_hints).map(|r| {
1395+
self.pick_method(self_ty, pick_diag_hints, None).map(|r| {
13021396
r.map(|mut pick| {
13031397
pick.autoderefs = step.autoderefs;
13041398

@@ -1337,14 +1431,21 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
13371431
self_ty: Ty<'tcx>,
13381432
mutbl: hir::Mutability,
13391433
pick_diag_hints: &mut PickDiagHints<'b, 'tcx>,
1434+
pick_constraints: Option<&PickConstraintsForShadowed>,
13401435
) -> Option<PickResult<'tcx>> {
13411436
let tcx = self.tcx;
13421437

1438+
if let Some(pick_constraints) = pick_constraints {
1439+
if !pick_constraints.may_shadow_based_on_autoderefs(step.autoderefs) {
1440+
return None;
1441+
}
1442+
}
1443+
13431444
// In general, during probing we erase regions.
13441445
let region = tcx.lifetimes.re_erased;
13451446

13461447
let autoref_ty = Ty::new_ref(tcx, region, self_ty, mutbl);
1347-
self.pick_method(autoref_ty, pick_diag_hints).map(|r| {
1448+
self.pick_method(autoref_ty, pick_diag_hints, pick_constraints).map(|r| {
13481449
r.map(|mut pick| {
13491450
pick.autoderefs = step.autoderefs;
13501451
pick.autoref_or_ptr_adjustment =
@@ -1381,7 +1482,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
13811482

13821483
let region = self.tcx.lifetimes.re_erased;
13831484
let autopin_ty = Ty::new_pinned_ref(self.tcx, region, inner_ty, hir::Mutability::Not);
1384-
self.pick_method(autopin_ty, pick_diag_hints).map(|r| {
1485+
self.pick_method(autopin_ty, pick_diag_hints, None).map(|r| {
13851486
r.map(|mut pick| {
13861487
pick.autoderefs = step.autoderefs;
13871488
pick.autoref_or_ptr_adjustment =
@@ -1410,7 +1511,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
14101511
};
14111512

14121513
let const_ptr_ty = Ty::new_imm_ptr(self.tcx, ty);
1413-
self.pick_method(const_ptr_ty, pick_diag_hints).map(|r| {
1514+
self.pick_method(const_ptr_ty, pick_diag_hints, None).map(|r| {
14141515
r.map(|mut pick| {
14151516
pick.autoderefs = step.autoderefs;
14161517
pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::ToConstPtr);
@@ -1423,22 +1524,24 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
14231524
&self,
14241525
self_ty: Ty<'tcx>,
14251526
pick_diag_hints: &mut PickDiagHints<'b, 'tcx>,
1527+
pick_constraints: Option<&PickConstraintsForShadowed>,
14261528
) -> Option<PickResult<'tcx>> {
14271529
debug!("pick_method(self_ty={})", self.ty_to_string(self_ty));
14281530

14291531
for (kind, candidates) in
14301532
[("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)]
14311533
{
14321534
debug!("searching {} candidates", kind);
1433-
let res = self.consider_candidates(self_ty, candidates, pick_diag_hints);
1535+
let res =
1536+
self.consider_candidates(self_ty, candidates, pick_diag_hints, pick_constraints);
14341537
if let Some(pick) = res {
14351538
return Some(pick);
14361539
}
14371540
}
14381541

14391542
if self.private_candidate.get().is_none() {
14401543
if let Some(Ok(pick)) =
1441-
self.consider_candidates(self_ty, &self.private_candidates, pick_diag_hints)
1544+
self.consider_candidates(self_ty, &self.private_candidates, pick_diag_hints, None)
14421545
{
14431546
self.private_candidate.set(Some((pick.item.kind.as_def_kind(), pick.item.def_id)));
14441547
}
@@ -1451,9 +1554,20 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
14511554
self_ty: Ty<'tcx>,
14521555
candidates: &[Candidate<'tcx>],
14531556
pick_diag_hints: &mut PickDiagHints<'b, 'tcx>,
1557+
pick_constraints: Option<&PickConstraintsForShadowed>,
14541558
) -> Option<PickResult<'tcx>> {
14551559
let mut applicable_candidates: Vec<_> = candidates
14561560
.iter()
1561+
.filter(|candidate| {
1562+
pick_constraints
1563+
.map(|pick_constraints| {
1564+
pick_constraints.may_shadow_based_on_defid(candidate.item.def_id)
1565+
&& pick_constraints.may_shadow_based_on_receiver_trait_derefs(
1566+
candidate.receiver_trait_derefs,
1567+
)
1568+
})
1569+
.unwrap_or(true)
1570+
})
14571571
.map(|probe| {
14581572
(
14591573
probe,
@@ -1527,6 +1641,7 @@ impl<'tcx> Pick<'tcx> {
15271641
autoref_or_ptr_adjustment: _,
15281642
self_ty,
15291643
unstable_candidates: _,
1644+
receiver_trait_derefs: _,
15301645
} = *self;
15311646
self_ty != other.self_ty || def_id != other.item.def_id
15321647
}
@@ -1899,10 +2014,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
18992014
item: probes[0].0.item,
19002015
kind: TraitPick,
19012016
import_ids: probes[0].0.import_ids.clone(),
1902-
autoderefs: 0,
2017+
autoderefs: probes[0].0.receiver_trait_derefs,
19032018
autoref_or_ptr_adjustment: None,
19042019
self_ty,
19052020
unstable_candidates: vec![],
2021+
receiver_trait_derefs: 0,
19062022
})
19072023
}
19082024

@@ -2196,6 +2312,7 @@ impl<'tcx> Candidate<'tcx> {
21962312
autoref_or_ptr_adjustment: None,
21972313
self_ty,
21982314
unstable_candidates,
2315+
receiver_trait_derefs: self.receiver_trait_derefs,
21992316
}
22002317
}
22012318
}

0 commit comments

Comments
 (0)