Skip to content

Commit 41243d2

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 7e499d1 commit 41243d2

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)]
@@ -171,6 +177,30 @@ struct PickDiagHints<'a, 'tcx> {
171177
)>,
172178
}
173179

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

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

195229
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -674,6 +708,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
674708
static_candidates: RefCell::new(Vec::new()),
675709
scope_expr_id,
676710
is_suggestion,
711+
receiver_trait_derefs: 0usize,
677712
}
678713
}
679714

@@ -706,7 +741,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
706741
import_ids: SmallVec<[LocalDefId; 1]>,
707742
is_inherent: bool,
708743
) {
709-
let candidate = Candidate { item, kind, import_ids };
744+
let candidate =
745+
Candidate { item, kind, import_ids, receiver_trait_derefs: self.receiver_trait_derefs };
710746
let is_accessible = if let Some(name) = self.method_name {
711747
let item = candidate.item;
712748
let hir_id = self.tcx.local_def_id_to_hir_id(self.body_id);
@@ -729,6 +765,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
729765

730766
fn assemble_inherent_candidates(&mut self) {
731767
for step in self.steps.iter() {
768+
self.receiver_trait_derefs = step.autoderefs;
732769
self.assemble_probe(&step.self_ty);
733770
}
734771
}
@@ -1196,8 +1233,13 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
11961233
return Some(by_value_pick);
11971234
}
11981235

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

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

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

1302-
self.pick_method(self_ty, pick_diag_hints).map(|r| {
1396+
self.pick_method(self_ty, pick_diag_hints, None).map(|r| {
13031397
r.map(|mut pick| {
13041398
pick.autoderefs = step.autoderefs;
13051399

@@ -1338,14 +1432,21 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
13381432
self_ty: Ty<'tcx>,
13391433
mutbl: hir::Mutability,
13401434
pick_diag_hints: &mut PickDiagHints<'b, 'tcx>,
1435+
pick_constraints: Option<&PickConstraintsForShadowed>,
13411436
) -> Option<PickResult<'tcx>> {
13421437
let tcx = self.tcx;
13431438

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

13471448
let autoref_ty = Ty::new_ref(tcx, region, self_ty, mutbl);
1348-
self.pick_method(autoref_ty, pick_diag_hints).map(|r| {
1449+
self.pick_method(autoref_ty, pick_diag_hints, pick_constraints).map(|r| {
13491450
r.map(|mut pick| {
13501451
pick.autoderefs = step.autoderefs;
13511452
pick.autoref_or_ptr_adjustment =
@@ -1382,7 +1483,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
13821483

13831484
let region = self.tcx.lifetimes.re_erased;
13841485
let autopin_ty = Ty::new_pinned_ref(self.tcx, region, inner_ty, hir::Mutability::Not);
1385-
self.pick_method(autopin_ty, pick_diag_hints).map(|r| {
1486+
self.pick_method(autopin_ty, pick_diag_hints, None).map(|r| {
13861487
r.map(|mut pick| {
13871488
pick.autoderefs = step.autoderefs;
13881489
pick.autoref_or_ptr_adjustment =
@@ -1411,7 +1512,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
14111512
};
14121513

14131514
let const_ptr_ty = Ty::new_imm_ptr(self.tcx, ty);
1414-
self.pick_method(const_ptr_ty, pick_diag_hints).map(|r| {
1515+
self.pick_method(const_ptr_ty, pick_diag_hints, None).map(|r| {
14151516
r.map(|mut pick| {
14161517
pick.autoderefs = step.autoderefs;
14171518
pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::ToConstPtr);
@@ -1424,22 +1525,24 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
14241525
&self,
14251526
self_ty: Ty<'tcx>,
14261527
pick_diag_hints: &mut PickDiagHints<'b, 'tcx>,
1528+
pick_constraints: Option<&PickConstraintsForShadowed>,
14271529
) -> Option<PickResult<'tcx>> {
14281530
debug!("pick_method(self_ty={})", self.ty_to_string(self_ty));
14291531

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

14401543
if self.private_candidate.get().is_none() {
14411544
if let Some(Ok(pick)) =
1442-
self.consider_candidates(self_ty, &self.private_candidates, pick_diag_hints)
1545+
self.consider_candidates(self_ty, &self.private_candidates, pick_diag_hints, None)
14431546
{
14441547
self.private_candidate.set(Some((pick.item.kind.as_def_kind(), pick.item.def_id)));
14451548
}
@@ -1452,9 +1555,20 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
14521555
self_ty: Ty<'tcx>,
14531556
candidates: &[Candidate<'tcx>],
14541557
pick_diag_hints: &mut PickDiagHints<'b, 'tcx>,
1558+
pick_constraints: Option<&PickConstraintsForShadowed>,
14551559
) -> Option<PickResult<'tcx>> {
14561560
let mut applicable_candidates: Vec<_> = candidates
14571561
.iter()
1562+
.filter(|candidate| {
1563+
pick_constraints
1564+
.map(|pick_constraints| {
1565+
pick_constraints.may_shadow_based_on_defid(candidate.item.def_id)
1566+
&& pick_constraints.may_shadow_based_on_receiver_trait_derefs(
1567+
candidate.receiver_trait_derefs,
1568+
)
1569+
})
1570+
.unwrap_or(true)
1571+
})
14581572
.map(|probe| {
14591573
(
14601574
probe,
@@ -1528,6 +1642,7 @@ impl<'tcx> Pick<'tcx> {
15281642
autoref_or_ptr_adjustment: _,
15291643
self_ty,
15301644
unstable_candidates: _,
1645+
receiver_trait_derefs: _,
15311646
} = *self;
15321647
self_ty != other.self_ty || def_id != other.item.def_id
15331648
}
@@ -1900,10 +2015,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
19002015
item: probes[0].0.item,
19012016
kind: TraitPick,
19022017
import_ids: probes[0].0.import_ids.clone(),
1903-
autoderefs: 0,
2018+
autoderefs: probes[0].0.receiver_trait_derefs,
19042019
autoref_or_ptr_adjustment: None,
19052020
self_ty,
19062021
unstable_candidates: vec![],
2022+
receiver_trait_derefs: 0,
19072023
})
19082024
}
19092025

@@ -2197,6 +2313,7 @@ impl<'tcx> Candidate<'tcx> {
21972313
autoref_or_ptr_adjustment: None,
21982314
self_ty,
21992315
unstable_candidates,
2316+
receiver_trait_derefs: self.receiver_trait_derefs,
22002317
}
22012318
}
22022319
}

0 commit comments

Comments
 (0)