diff options
| author | Gary Guo <gary@garyguo.net> | 2021-10-26 23:24:23 +0100 |
|---|---|---|
| committer | Gary Guo <gary@garyguo.net> | 2021-11-15 02:14:54 +0000 |
| commit | 6a207f23eb5570d10b98dcfa669c17d5ab94e8af (patch) | |
| tree | 350b99df3c32e0cc5c187cc268d1892c975b9fb2 | |
| parent | 9740050726d8184b614b3150c7efbb63ea2288d9 (diff) | |
| download | rust-6a207f23eb5570d10b98dcfa669c17d5ab94e8af.tar.gz rust-6a207f23eb5570d10b98dcfa669c17d5ab94e8af.zip | |
Try all stable candidates first before trying unstable ones
| -rw-r--r-- | compiler/rustc_interface/src/tests.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_session/src/options.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_typeck/src/check/method/probe.rs | 121 |
3 files changed, 105 insertions, 19 deletions
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index eed2e07e890..015e7acb60a 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -751,6 +751,7 @@ fn test_debugging_options_tracking_hash() { tracked!(panic_abort_tests, true); tracked!(panic_in_drop, PanicStrategy::Abort); tracked!(partially_uninit_const_threshold, Some(123)); + tracked!(pick_stable_methods_before_any_unstable, false); tracked!(plt, Some(true)); tracked!(polonius, true); tracked!(precise_enum_drop_elaboration, false); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index d1d8606a75a..95ff5011180 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1233,6 +1233,8 @@ options! { and set the maximum total size of a const allocation for which this is allowed (default: never)"), perf_stats: bool = (false, parse_bool, [UNTRACKED], "print some performance-related statistics (default: no)"), + pick_stable_methods_before_any_unstable: bool = (true, parse_bool, [TRACKED], + "try to pick stable methods first before picking any unstable methods (default: yes)"), plt: Option<bool> = (None, parse_opt_bool, [TRACKED], "whether to use the PLT when calling into shared libraries; only has effect for PIC code on systems with ELF binaries diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs index 8265887f614..95fe6c9b93c 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_typeck/src/check/method/probe.rs @@ -92,7 +92,7 @@ impl<'a, 'tcx> Deref for ProbeContext<'a, 'tcx> { } } -#[derive(Debug)] +#[derive(Debug, Clone)] struct Candidate<'tcx> { // Candidates are (I'm not quite sure, but they are mostly) basically // some metadata on top of a `ty::AssocItem` (without substs). @@ -132,7 +132,7 @@ struct Candidate<'tcx> { import_ids: SmallVec<[LocalDefId; 1]>, } -#[derive(Debug)] +#[derive(Debug, Clone)] enum CandidateKind<'tcx> { InherentImplCandidate( SubstsRef<'tcx>, @@ -1102,13 +1102,37 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } fn pick_core(&mut self) -> Option<PickResult<'tcx>> { - let steps = self.steps.clone(); + let mut unstable_candidates = Vec::new(); + let pick = self.pick_all_method(Some(&mut unstable_candidates)); + + // In this case unstable picking is done by `pick_method`. + if !self.tcx.sess.opts.debugging_opts.pick_stable_methods_before_any_unstable { + return pick; + } - // find the first step that works + match pick { + // Emit a lint if there are unstable candidates alongside the stable ones. + // + // We suppress warning if we're picking the method only because it is a + // suggestion. + Some(Ok(ref p)) if !self.is_suggestion.0 && !unstable_candidates.is_empty() => { + self.emit_unstable_name_collision_hint(p, &unstable_candidates); + pick + } + Some(_) => pick, + None => self.pick_all_method(None), + } + } + + fn pick_all_method( + &mut self, + mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + ) -> Option<PickResult<'tcx>> { + let steps = self.steps.clone(); steps .iter() .filter(|step| { - debug!("pick_core: step={:?}", step); + debug!("pick_all_method: step={:?}", step); // skip types that are from a type error or that would require dereferencing // a raw pointer !step.self_ty.references_error() && !step.from_unsafe_deref @@ -1124,11 +1148,30 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { .unwrap_or_else(|_| { span_bug!(self.span, "{:?} was applicable but now isn't?", step.self_ty) }); - self.pick_by_value_method(step, self_ty).or_else(|| { - self.pick_autorefd_method(step, self_ty, hir::Mutability::Not) - .or_else(|| self.pick_autorefd_method(step, self_ty, hir::Mutability::Mut)) - .or_else(|| self.pick_const_ptr_method(step, self_ty)) - }) + self.pick_by_value_method(step, self_ty, unstable_candidates.as_deref_mut()) + .or_else(|| { + self.pick_autorefd_method( + step, + self_ty, + hir::Mutability::Not, + unstable_candidates.as_deref_mut(), + ) + .or_else(|| { + self.pick_autorefd_method( + step, + self_ty, + hir::Mutability::Mut, + unstable_candidates.as_deref_mut(), + ) + }) + .or_else(|| { + self.pick_const_ptr_method( + step, + self_ty, + unstable_candidates.as_deref_mut(), + ) + }) + }) }) .next() } @@ -1143,12 +1186,13 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &mut self, step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, + unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, ) -> Option<PickResult<'tcx>> { if step.unsize { return None; } - self.pick_method(self_ty).map(|r| { + self.pick_method(self_ty, unstable_candidates).map(|r| { r.map(|mut pick| { pick.autoderefs = step.autoderefs; @@ -1171,6 +1215,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, mutbl: hir::Mutability, + unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, ) -> Option<PickResult<'tcx>> { let tcx = self.tcx; @@ -1178,7 +1223,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let region = tcx.lifetimes.re_erased; let autoref_ty = tcx.mk_ref(region, ty::TypeAndMut { ty: self_ty, mutbl }); - self.pick_method(autoref_ty).map(|r| { + self.pick_method(autoref_ty, unstable_candidates).map(|r| { r.map(|mut pick| { pick.autoderefs = step.autoderefs; pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::Autoref { @@ -1197,6 +1242,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &mut self, step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, + unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, ) -> Option<PickResult<'tcx>> { // Don't convert an unsized reference to ptr if step.unsize { @@ -1210,7 +1256,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let const_self_ty = ty::TypeAndMut { ty, mutbl: hir::Mutability::Not }; let const_ptr_ty = self.tcx.mk_ptr(const_self_ty); - self.pick_method(const_ptr_ty).map(|r| { + self.pick_method(const_ptr_ty, unstable_candidates).map(|r| { r.map(|mut pick| { pick.autoderefs = step.autoderefs; pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::ToConstPtr); @@ -1219,8 +1265,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { }) } - fn pick_method(&mut self, self_ty: Ty<'tcx>) -> Option<PickResult<'tcx>> { - debug!("pick_method(self_ty={})", self.ty_to_string(self_ty)); + fn pick_method_with_unstable(&mut self, self_ty: Ty<'tcx>) -> Option<PickResult<'tcx>> { + debug!("pick_method_with_unstable(self_ty={})", self.ty_to_string(self_ty)); let mut possibly_unsatisfied_predicates = Vec::new(); let mut unstable_candidates = Vec::new(); @@ -1252,7 +1298,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { debug!("searching unstable candidates"); let res = self.consider_candidates( self_ty, - unstable_candidates.into_iter().map(|(c, _)| c), + unstable_candidates.iter().map(|(c, _)| c), &mut possibly_unsatisfied_predicates, None, ); @@ -1262,6 +1308,42 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { res } + fn pick_method( + &mut self, + self_ty: Ty<'tcx>, + mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + ) -> Option<PickResult<'tcx>> { + if !self.tcx.sess.opts.debugging_opts.pick_stable_methods_before_any_unstable { + return self.pick_method_with_unstable(self_ty); + } + + debug!("pick_method(self_ty={})", self.ty_to_string(self_ty)); + + let mut possibly_unsatisfied_predicates = Vec::new(); + + for (kind, candidates) in + &[("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)] + { + debug!("searching {} candidates", kind); + let res = self.consider_candidates( + self_ty, + candidates.iter(), + &mut possibly_unsatisfied_predicates, + unstable_candidates.as_deref_mut(), + ); + if let Some(pick) = res { + return Some(pick); + } + } + + // `pick_method` may be called twice for the same self_ty if no stable methods + // match. Only extend once. + if unstable_candidates.is_some() { + self.unsatisfied_predicates.extend(possibly_unsatisfied_predicates); + } + None + } + fn consider_candidates<'b, ProbesIter>( &self, self_ty: Ty<'tcx>, @@ -1270,10 +1352,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, )>, - unstable_candidates: Option<&mut Vec<(&'b Candidate<'tcx>, Symbol)>>, + unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, ) -> Option<PickResult<'tcx>> where ProbesIter: Iterator<Item = &'b Candidate<'tcx>> + Clone, + 'tcx: 'b, { let mut applicable_candidates: Vec<_> = probes .clone() @@ -1298,7 +1381,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { if let stability::EvalResult::Deny { feature, .. } = self.tcx.eval_stability(p.item.def_id, None, self.span, None) { - uc.push((p, feature)); + uc.push((p.clone(), feature)); return false; } true @@ -1322,7 +1405,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn emit_unstable_name_collision_hint( &self, stable_pick: &Pick<'_>, - unstable_candidates: &[(&Candidate<'tcx>, Symbol)], + unstable_candidates: &[(Candidate<'tcx>, Symbol)], ) { self.tcx.struct_span_lint_hir( lint::builtin::UNSTABLE_NAME_COLLISIONS, |
