Skip to content

Commit bdab60f

Browse files
authored
Rollup merge of rust-lang#77305 - lcnr:candidate_from_obligation, r=davidtwco
move candidate_from_obligation_no_cache It's only called from `candidate_from_obligation` which is already in this file.
2 parents 7d37b6d + db5b70f commit bdab60f

File tree

2 files changed

+161
-156
lines changed

2 files changed

+161
-156
lines changed

compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

+161-1
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,19 @@
77
//! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly
88
use rustc_hir as hir;
99
use rustc_infer::traits::{Obligation, SelectionError, TraitObligation};
10+
use rustc_middle::ty::print::with_no_trimmed_paths;
1011
use rustc_middle::ty::{self, TypeFoldable};
1112
use rustc_target::spec::abi::Abi;
1213

14+
use crate::traits::coherence::Conflict;
1315
use crate::traits::{util, SelectionResult};
16+
use crate::traits::{Overflow, Unimplemented};
1417

1518
use super::BuiltinImplConditions;
19+
use super::IntercrateAmbiguityCause;
20+
use super::OverflowError;
1621
use super::SelectionCandidate::{self, *};
17-
use super::{SelectionCandidateSet, SelectionContext, TraitObligationStack};
22+
use super::{EvaluatedCandidate, SelectionCandidateSet, SelectionContext, TraitObligationStack};
1823

1924
impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
2025
pub(super) fn candidate_from_obligation<'o>(
@@ -62,6 +67,161 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
6267
candidate
6368
}
6469

70+
fn candidate_from_obligation_no_cache<'o>(
71+
&mut self,
72+
stack: &TraitObligationStack<'o, 'tcx>,
73+
) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
74+
if let Some(conflict) = self.is_knowable(stack) {
75+
debug!("coherence stage: not knowable");
76+
if self.intercrate_ambiguity_causes.is_some() {
77+
debug!("evaluate_stack: intercrate_ambiguity_causes is some");
78+
// Heuristics: show the diagnostics when there are no candidates in crate.
79+
if let Ok(candidate_set) = self.assemble_candidates(stack) {
80+
let mut no_candidates_apply = true;
81+
82+
for c in candidate_set.vec.iter() {
83+
if self.evaluate_candidate(stack, &c)?.may_apply() {
84+
no_candidates_apply = false;
85+
break;
86+
}
87+
}
88+
89+
if !candidate_set.ambiguous && no_candidates_apply {
90+
let trait_ref = stack.obligation.predicate.skip_binder().trait_ref;
91+
let self_ty = trait_ref.self_ty();
92+
let (trait_desc, self_desc) = with_no_trimmed_paths(|| {
93+
let trait_desc = trait_ref.print_only_trait_path().to_string();
94+
let self_desc = if self_ty.has_concrete_skeleton() {
95+
Some(self_ty.to_string())
96+
} else {
97+
None
98+
};
99+
(trait_desc, self_desc)
100+
});
101+
let cause = if let Conflict::Upstream = conflict {
102+
IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc }
103+
} else {
104+
IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc }
105+
};
106+
debug!("evaluate_stack: pushing cause = {:?}", cause);
107+
self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause);
108+
}
109+
}
110+
}
111+
return Ok(None);
112+
}
113+
114+
let candidate_set = self.assemble_candidates(stack)?;
115+
116+
if candidate_set.ambiguous {
117+
debug!("candidate set contains ambig");
118+
return Ok(None);
119+
}
120+
121+
let mut candidates = candidate_set.vec;
122+
123+
debug!("assembled {} candidates for {:?}: {:?}", candidates.len(), stack, candidates);
124+
125+
// At this point, we know that each of the entries in the
126+
// candidate set is *individually* applicable. Now we have to
127+
// figure out if they contain mutual incompatibilities. This
128+
// frequently arises if we have an unconstrained input type --
129+
// for example, we are looking for `$0: Eq` where `$0` is some
130+
// unconstrained type variable. In that case, we'll get a
131+
// candidate which assumes $0 == int, one that assumes `$0 ==
132+
// usize`, etc. This spells an ambiguity.
133+
134+
// If there is more than one candidate, first winnow them down
135+
// by considering extra conditions (nested obligations and so
136+
// forth). We don't winnow if there is exactly one
137+
// candidate. This is a relatively minor distinction but it
138+
// can lead to better inference and error-reporting. An
139+
// example would be if there was an impl:
140+
//
141+
// impl<T:Clone> Vec<T> { fn push_clone(...) { ... } }
142+
//
143+
// and we were to see some code `foo.push_clone()` where `boo`
144+
// is a `Vec<Bar>` and `Bar` does not implement `Clone`. If
145+
// we were to winnow, we'd wind up with zero candidates.
146+
// Instead, we select the right impl now but report "`Bar` does
147+
// not implement `Clone`".
148+
if candidates.len() == 1 {
149+
return self.filter_negative_and_reservation_impls(candidates.pop().unwrap());
150+
}
151+
152+
// Winnow, but record the exact outcome of evaluation, which
153+
// is needed for specialization. Propagate overflow if it occurs.
154+
let mut candidates = candidates
155+
.into_iter()
156+
.map(|c| match self.evaluate_candidate(stack, &c) {
157+
Ok(eval) if eval.may_apply() => {
158+
Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval }))
159+
}
160+
Ok(_) => Ok(None),
161+
Err(OverflowError) => Err(Overflow),
162+
})
163+
.flat_map(Result::transpose)
164+
.collect::<Result<Vec<_>, _>>()?;
165+
166+
debug!("winnowed to {} candidates for {:?}: {:?}", candidates.len(), stack, candidates);
167+
168+
let needs_infer = stack.obligation.predicate.needs_infer();
169+
170+
// If there are STILL multiple candidates, we can further
171+
// reduce the list by dropping duplicates -- including
172+
// resolving specializations.
173+
if candidates.len() > 1 {
174+
let mut i = 0;
175+
while i < candidates.len() {
176+
let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| {
177+
self.candidate_should_be_dropped_in_favor_of(
178+
&candidates[i],
179+
&candidates[j],
180+
needs_infer,
181+
)
182+
});
183+
if is_dup {
184+
debug!("Dropping candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]);
185+
candidates.swap_remove(i);
186+
} else {
187+
debug!("Retaining candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]);
188+
i += 1;
189+
190+
// If there are *STILL* multiple candidates, give up
191+
// and report ambiguity.
192+
if i > 1 {
193+
debug!("multiple matches, ambig");
194+
return Ok(None);
195+
}
196+
}
197+
}
198+
}
199+
200+
// If there are *NO* candidates, then there are no impls --
201+
// that we know of, anyway. Note that in the case where there
202+
// are unbound type variables within the obligation, it might
203+
// be the case that you could still satisfy the obligation
204+
// from another crate by instantiating the type variables with
205+
// a type from another crate that does have an impl. This case
206+
// is checked for in `evaluate_stack` (and hence users
207+
// who might care about this case, like coherence, should use
208+
// that function).
209+
if candidates.is_empty() {
210+
// If there's an error type, 'downgrade' our result from
211+
// `Err(Unimplemented)` to `Ok(None)`. This helps us avoid
212+
// emitting additional spurious errors, since we're guaranteed
213+
// to have emitted at least one.
214+
if stack.obligation.references_error() {
215+
debug!("no results for error type, treating as ambiguous");
216+
return Ok(None);
217+
}
218+
return Err(Unimplemented);
219+
}
220+
221+
// Just one candidate left.
222+
self.filter_negative_and_reservation_impls(candidates.pop().unwrap().candidate)
223+
}
224+
65225
pub(super) fn assemble_candidates<'o>(
66226
&mut self,
67227
stack: &TraitObligationStack<'o, 'tcx>,

compiler/rustc_trait_selection/src/traits/select/mod.rs

-155
Original file line numberDiff line numberDiff line change
@@ -1029,161 +1029,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
10291029
Ok(Some(candidate))
10301030
}
10311031

1032-
fn candidate_from_obligation_no_cache<'o>(
1033-
&mut self,
1034-
stack: &TraitObligationStack<'o, 'tcx>,
1035-
) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
1036-
if let Some(conflict) = self.is_knowable(stack) {
1037-
debug!("coherence stage: not knowable");
1038-
if self.intercrate_ambiguity_causes.is_some() {
1039-
debug!("evaluate_stack: intercrate_ambiguity_causes is some");
1040-
// Heuristics: show the diagnostics when there are no candidates in crate.
1041-
if let Ok(candidate_set) = self.assemble_candidates(stack) {
1042-
let mut no_candidates_apply = true;
1043-
1044-
for c in candidate_set.vec.iter() {
1045-
if self.evaluate_candidate(stack, &c)?.may_apply() {
1046-
no_candidates_apply = false;
1047-
break;
1048-
}
1049-
}
1050-
1051-
if !candidate_set.ambiguous && no_candidates_apply {
1052-
let trait_ref = stack.obligation.predicate.skip_binder().trait_ref;
1053-
let self_ty = trait_ref.self_ty();
1054-
let (trait_desc, self_desc) = with_no_trimmed_paths(|| {
1055-
let trait_desc = trait_ref.print_only_trait_path().to_string();
1056-
let self_desc = if self_ty.has_concrete_skeleton() {
1057-
Some(self_ty.to_string())
1058-
} else {
1059-
None
1060-
};
1061-
(trait_desc, self_desc)
1062-
});
1063-
let cause = if let Conflict::Upstream = conflict {
1064-
IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc }
1065-
} else {
1066-
IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc }
1067-
};
1068-
debug!("evaluate_stack: pushing cause = {:?}", cause);
1069-
self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause);
1070-
}
1071-
}
1072-
}
1073-
return Ok(None);
1074-
}
1075-
1076-
let candidate_set = self.assemble_candidates(stack)?;
1077-
1078-
if candidate_set.ambiguous {
1079-
debug!("candidate set contains ambig");
1080-
return Ok(None);
1081-
}
1082-
1083-
let mut candidates = candidate_set.vec;
1084-
1085-
debug!("assembled {} candidates for {:?}: {:?}", candidates.len(), stack, candidates);
1086-
1087-
// At this point, we know that each of the entries in the
1088-
// candidate set is *individually* applicable. Now we have to
1089-
// figure out if they contain mutual incompatibilities. This
1090-
// frequently arises if we have an unconstrained input type --
1091-
// for example, we are looking for `$0: Eq` where `$0` is some
1092-
// unconstrained type variable. In that case, we'll get a
1093-
// candidate which assumes $0 == int, one that assumes `$0 ==
1094-
// usize`, etc. This spells an ambiguity.
1095-
1096-
// If there is more than one candidate, first winnow them down
1097-
// by considering extra conditions (nested obligations and so
1098-
// forth). We don't winnow if there is exactly one
1099-
// candidate. This is a relatively minor distinction but it
1100-
// can lead to better inference and error-reporting. An
1101-
// example would be if there was an impl:
1102-
//
1103-
// impl<T:Clone> Vec<T> { fn push_clone(...) { ... } }
1104-
//
1105-
// and we were to see some code `foo.push_clone()` where `boo`
1106-
// is a `Vec<Bar>` and `Bar` does not implement `Clone`. If
1107-
// we were to winnow, we'd wind up with zero candidates.
1108-
// Instead, we select the right impl now but report "`Bar` does
1109-
// not implement `Clone`".
1110-
if candidates.len() == 1 {
1111-
return self.filter_negative_and_reservation_impls(candidates.pop().unwrap());
1112-
}
1113-
1114-
// Winnow, but record the exact outcome of evaluation, which
1115-
// is needed for specialization. Propagate overflow if it occurs.
1116-
let mut candidates = candidates
1117-
.into_iter()
1118-
.map(|c| match self.evaluate_candidate(stack, &c) {
1119-
Ok(eval) if eval.may_apply() => {
1120-
Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval }))
1121-
}
1122-
Ok(_) => Ok(None),
1123-
Err(OverflowError) => Err(Overflow),
1124-
})
1125-
.flat_map(Result::transpose)
1126-
.collect::<Result<Vec<_>, _>>()?;
1127-
1128-
debug!("winnowed to {} candidates for {:?}: {:?}", candidates.len(), stack, candidates);
1129-
1130-
let needs_infer = stack.obligation.predicate.needs_infer();
1131-
1132-
// If there are STILL multiple candidates, we can further
1133-
// reduce the list by dropping duplicates -- including
1134-
// resolving specializations.
1135-
if candidates.len() > 1 {
1136-
let mut i = 0;
1137-
while i < candidates.len() {
1138-
let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| {
1139-
self.candidate_should_be_dropped_in_favor_of(
1140-
&candidates[i],
1141-
&candidates[j],
1142-
needs_infer,
1143-
)
1144-
});
1145-
if is_dup {
1146-
debug!("Dropping candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]);
1147-
candidates.swap_remove(i);
1148-
} else {
1149-
debug!("Retaining candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]);
1150-
i += 1;
1151-
1152-
// If there are *STILL* multiple candidates, give up
1153-
// and report ambiguity.
1154-
if i > 1 {
1155-
debug!("multiple matches, ambig");
1156-
return Ok(None);
1157-
}
1158-
}
1159-
}
1160-
}
1161-
1162-
// If there are *NO* candidates, then there are no impls --
1163-
// that we know of, anyway. Note that in the case where there
1164-
// are unbound type variables within the obligation, it might
1165-
// be the case that you could still satisfy the obligation
1166-
// from another crate by instantiating the type variables with
1167-
// a type from another crate that does have an impl. This case
1168-
// is checked for in `evaluate_stack` (and hence users
1169-
// who might care about this case, like coherence, should use
1170-
// that function).
1171-
if candidates.is_empty() {
1172-
// If there's an error type, 'downgrade' our result from
1173-
// `Err(Unimplemented)` to `Ok(None)`. This helps us avoid
1174-
// emitting additional spurious errors, since we're guaranteed
1175-
// to have emitted at least one.
1176-
if stack.obligation.references_error() {
1177-
debug!("no results for error type, treating as ambiguous");
1178-
return Ok(None);
1179-
}
1180-
return Err(Unimplemented);
1181-
}
1182-
1183-
// Just one candidate left.
1184-
self.filter_negative_and_reservation_impls(candidates.pop().unwrap().candidate)
1185-
}
1186-
11871032
fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Option<Conflict> {
11881033
debug!("is_knowable(intercrate={:?})", self.intercrate);
11891034

0 commit comments

Comments
 (0)