Skip to content

Commit f1551bf

Browse files
committed
selection failure: recompute applicable impls
1 parent ddfe1e8 commit f1551bf

21 files changed

+166
-73
lines changed

compiler/rustc_middle/src/traits/mod.rs

-3
Original file line numberDiff line numberDiff line change
@@ -576,9 +576,6 @@ pub enum SelectionError<'tcx> {
576576
/// Signaling that an error has already been emitted, to avoid
577577
/// multiple errors being shown.
578578
ErrorReporting,
579-
/// Multiple applicable `impl`s where found. The `DefId`s correspond to
580-
/// all the `impl`s' Items.
581-
Ambiguous(Vec<DefId>),
582579
}
583580

584581
/// When performing resolution, it is typically the case that there

compiler/rustc_middle/src/ty/mod.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -2550,11 +2550,11 @@ impl<'tcx> TyCtxt<'tcx> {
25502550

25512551
/// Looks up the span of `impl_did` if the impl is local; otherwise returns `Err`
25522552
/// with the name of the crate containing the impl.
2553-
pub fn span_of_impl(self, impl_did: DefId) -> Result<Span, Symbol> {
2554-
if let Some(impl_did) = impl_did.as_local() {
2555-
Ok(self.def_span(impl_did))
2553+
pub fn span_of_impl(self, impl_def_id: DefId) -> Result<Span, Symbol> {
2554+
if let Some(impl_def_id) = impl_def_id.as_local() {
2555+
Ok(self.def_span(impl_def_id))
25562556
} else {
2557-
Err(self.crate_name(impl_did.krate))
2557+
Err(self.crate_name(impl_def_id.krate))
25582558
}
25592559
}
25602560

compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,22 @@ pub struct FulfillmentContext<'tcx> {
1414
obligations: FxIndexSet<PredicateObligation<'tcx>>,
1515

1616
relationships: FxHashMap<ty::TyVid, ty::FoundRelationships>,
17+
18+
usable_in_snapshot: bool,
1719
}
1820

1921
impl FulfillmentContext<'_> {
2022
pub(crate) fn new() -> Self {
2123
FulfillmentContext {
2224
obligations: FxIndexSet::default(),
2325
relationships: FxHashMap::default(),
26+
usable_in_snapshot: false,
2427
}
2528
}
29+
30+
pub(crate) fn new_in_snapshot() -> Self {
31+
FulfillmentContext { usable_in_snapshot: true, ..Self::new() }
32+
}
2633
}
2734

2835
impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
@@ -41,7 +48,9 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
4148
infcx: &InferCtxt<'tcx>,
4249
obligation: PredicateObligation<'tcx>,
4350
) {
44-
assert!(!infcx.is_in_snapshot());
51+
if !self.usable_in_snapshot {
52+
assert!(!infcx.is_in_snapshot());
53+
}
4554
let obligation = infcx.resolve_vars_if_possible(obligation);
4655

4756
super::relationships::update(self, infcx, &obligation);
@@ -72,7 +81,9 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
7281
}
7382

7483
fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
75-
assert!(!infcx.is_in_snapshot());
84+
if !self.usable_in_snapshot {
85+
assert!(!infcx.is_in_snapshot());
86+
}
7687

7788
let mut errors = Vec::new();
7889
let mut next_round = FxIndexSet::default();

compiler/rustc_trait_selection/src/traits/engine.rs

+9-8
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> {
3838

3939
fn new_in_snapshot(tcx: TyCtxt<'tcx>) -> Box<Self> {
4040
if tcx.sess.opts.unstable_opts.chalk {
41-
Box::new(ChalkFulfillmentContext::new())
41+
Box::new(ChalkFulfillmentContext::new_in_snapshot())
4242
} else {
4343
Box::new(FulfillmentContext::new_in_snapshot())
4444
}
@@ -119,13 +119,10 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
119119
expected: T,
120120
actual: T,
121121
) -> Result<(), TypeError<'tcx>> {
122-
match self.infcx.at(cause, param_env).eq(expected, actual) {
123-
Ok(InferOk { obligations, value: () }) => {
124-
self.register_obligations(obligations);
125-
Ok(())
126-
}
127-
Err(e) => Err(e),
128-
}
122+
self.infcx
123+
.at(cause, param_env)
124+
.eq(expected, actual)
125+
.map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
129126
}
130127

131128
pub fn sup<T: ToTrace<'tcx>>(
@@ -144,6 +141,10 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
144141
}
145142
}
146143

144+
pub fn select_where_possible(&self) -> Vec<FulfillmentError<'tcx>> {
145+
self.engine.borrow_mut().select_where_possible(self.infcx)
146+
}
147+
147148
pub fn select_all_or_error(&self) -> Vec<FulfillmentError<'tcx>> {
148149
self.engine.borrow_mut().select_all_or_error(self.infcx)
149150
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use rustc_hir::def_id::DefId;
2+
use rustc_infer::infer::InferCtxt;
3+
use rustc_infer::traits::{Obligation, ObligationCause, TraitObligation};
4+
use rustc_span::DUMMY_SP;
5+
6+
use crate::traits::ObligationCtxt;
7+
8+
pub fn recompute_applicable_impls<'tcx>(
9+
infcx: &InferCtxt<'tcx>,
10+
obligation: &TraitObligation<'tcx>,
11+
) -> Vec<DefId> {
12+
let tcx = infcx.tcx;
13+
let param_env = obligation.param_env;
14+
let dummy_cause = ObligationCause::dummy();
15+
let impl_may_apply = |impl_def_id| {
16+
let ocx = ObligationCtxt::new_in_snapshot(infcx);
17+
let placeholder_obligation =
18+
infcx.replace_bound_vars_with_placeholders(obligation.predicate);
19+
let obligation_trait_ref =
20+
ocx.normalize(dummy_cause.clone(), param_env, placeholder_obligation.trait_ref);
21+
22+
let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
23+
let impl_trait_ref = tcx.bound_impl_trait_ref(impl_def_id).unwrap().subst(tcx, impl_substs);
24+
let impl_trait_ref = ocx.normalize(ObligationCause::dummy(), param_env, impl_trait_ref);
25+
26+
if let Err(_) = ocx.eq(&dummy_cause, param_env, obligation_trait_ref, impl_trait_ref) {
27+
return false;
28+
}
29+
30+
let impl_predicates = tcx.predicates_of(impl_def_id).instantiate(tcx, impl_substs);
31+
ocx.register_obligations(
32+
impl_predicates
33+
.predicates
34+
.iter()
35+
.map(|&predicate| Obligation::new(dummy_cause.clone(), param_env, predicate)),
36+
);
37+
38+
ocx.select_where_possible().is_empty()
39+
};
40+
41+
let mut impls = Vec::new();
42+
tcx.for_each_relevant_impl(
43+
obligation.predicate.def_id(),
44+
obligation.predicate.skip_binder().trait_ref.self_ty(),
45+
|impl_def_id| {
46+
if infcx.probe(move |_snapshot| impl_may_apply(impl_def_id)) {
47+
impls.push(impl_def_id)
48+
}
49+
},
50+
);
51+
impls
52+
}

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

+19-19
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod ambiguity;
12
pub mod on_unimplemented;
23
pub mod suggestions;
34

@@ -535,15 +536,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
535536
let mut span = obligation.cause.span;
536537

537538
let mut err = match *error {
538-
SelectionError::Ambiguous(ref impls) => {
539-
let mut err = self.tcx.sess.struct_span_err(
540-
obligation.cause.span,
541-
&format!("multiple applicable `impl`s for `{}`", obligation.predicate),
542-
);
543-
self.annotate_source_of_ambiguity(&mut err, impls, obligation.predicate);
544-
err.emit();
545-
return;
546-
}
547539
SelectionError::Unimplemented => {
548540
// If this obligation was generated as a result of well-formedness checking, see if we
549541
// can get a better error message by performing HIR-based well-formedness checking.
@@ -2144,8 +2136,21 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
21442136
crate::traits::TraitQueryMode::Standard,
21452137
);
21462138
match selcx.select_from_obligation(&obligation) {
2147-
Err(SelectionError::Ambiguous(impls)) if impls.len() > 1 => {
2148-
self.annotate_source_of_ambiguity(&mut err, &impls, predicate);
2139+
Ok(None) => {
2140+
let impls = ambiguity::recompute_applicable_impls(self.infcx, &obligation);
2141+
let has_non_region_infer =
2142+
trait_ref.skip_binder().substs.types().any(|t| !t.is_ty_infer());
2143+
// It doesn't make sense to talk about applicable impls if there are more
2144+
// than a handful of them.
2145+
if impls.len() > 1 && impls.len() < 5 && has_non_region_infer {
2146+
self.annotate_source_of_ambiguity(&mut err, &impls, predicate);
2147+
} else {
2148+
if self.is_tainted_by_errors() {
2149+
err.cancel();
2150+
return;
2151+
}
2152+
err.note(&format!("cannot satisfy `{}`", predicate));
2153+
}
21492154
}
21502155
_ => {
21512156
if self.is_tainted_by_errors() {
@@ -2441,7 +2446,6 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
24412446
}
24422447
}
24432448
}
2444-
let msg = format!("multiple `impl`s satisfying `{}` found", predicate);
24452449
let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{}`", n)).collect();
24462450
crate_names.sort();
24472451
crate_names.dedup();
@@ -2462,13 +2466,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
24622466
err.downgrade_to_delayed_bug();
24632467
return;
24642468
}
2465-
let post = if post.len() > 4 {
2466-
format!(
2467-
":\n{}\nand {} more",
2468-
post.iter().map(|p| format!("- {}", p)).take(4).collect::<Vec<_>>().join("\n"),
2469-
post.len() - 4,
2470-
)
2471-
} else if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) {
2469+
2470+
let msg = format!("multiple `impl`s satisfying `{}` found", predicate);
2471+
let post = if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) {
24722472
format!(":\n{}", post.iter().map(|p| format!("- {}", p)).collect::<Vec<_>>().join("\n"),)
24732473
} else if post.len() == 1 {
24742474
format!(": `{}`", post[0])

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

+2-10
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::traits;
2020
use crate::traits::coherence::Conflict;
2121
use crate::traits::query::evaluate_obligation::InferCtxtExt;
2222
use crate::traits::{util, SelectionResult};
23-
use crate::traits::{Ambiguous, ErrorReporting, Overflow, Unimplemented};
23+
use crate::traits::{ErrorReporting, Overflow, Unimplemented};
2424

2525
use super::BuiltinImplConditions;
2626
use super::IntercrateAmbiguityCause;
@@ -200,15 +200,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
200200
// and report ambiguity.
201201
if i > 1 {
202202
debug!("multiple matches, ambig");
203-
return Err(Ambiguous(
204-
candidates
205-
.into_iter()
206-
.filter_map(|c| match c.candidate {
207-
SelectionCandidate::ImplCandidate(def_id) => Some(def_id),
208-
_ => None,
209-
})
210-
.collect(),
211-
));
203+
return Ok(None);
212204
}
213205
}
214206
}

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

-4
Original file line numberDiff line numberDiff line change
@@ -294,9 +294,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
294294
assert!(self.query_mode == TraitQueryMode::Canonical);
295295
return Err(SelectionError::Overflow(OverflowError::Canonical));
296296
}
297-
Err(SelectionError::Ambiguous(_)) => {
298-
return Ok(None);
299-
}
300297
Err(e) => {
301298
return Err(e);
302299
}
@@ -931,7 +928,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
931928

932929
match self.candidate_from_obligation(stack) {
933930
Ok(Some(c)) => self.evaluate_candidate(stack, &c),
934-
Err(SelectionError::Ambiguous(_)) => Ok(EvaluatedToAmbig),
935931
Ok(None) => Ok(EvaluatedToAmbig),
936932
Err(Overflow(OverflowError::Canonical)) => Err(OverflowError::Canonical),
937933
Err(ErrorReporting) => Err(OverflowError::ErrorReporting),

src/test/ui/error-codes/E0282.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
fn main() {
2-
let x = "hello".chars().rev().collect(); //~ ERROR E0282
2+
let x = "hello".chars().rev().collect();
3+
//~^ ERROR E0282
34
}

src/test/ui/error-codes/E0401.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ fn foo<T>(x: T) {
88
W: Fn()>
99
(y: T) { //~ ERROR E0401
1010
}
11-
bfnr(x); //~ ERROR type annotations needed
11+
bfnr(x);
12+
//~^ ERROR type annotations needed
13+
//~| ERROR type annotations needed
1214
}
1315

1416

src/test/ui/error-codes/E0401.stderr

+24-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ LL | (y: T) {
2121
| ^ use of generic parameter from outer function
2222

2323
error[E0401]: can't use generic parameters from outer function
24-
--> $DIR/E0401.rs:22:25
24+
--> $DIR/E0401.rs:24:25
2525
|
2626
LL | impl<T> Iterator for A<T> {
2727
| ---- `Self` type implicitly declared here, by this `impl`
@@ -43,7 +43,28 @@ help: consider specifying the generic arguments
4343
LL | bfnr::<U, V, W>(x);
4444
| +++++++++++
4545

46-
error: aborting due to 4 previous errors
46+
error[E0283]: type annotations needed
47+
--> $DIR/E0401.rs:11:5
48+
|
49+
LL | bfnr(x);
50+
| ^^^^ cannot infer type of the type parameter `W` declared on the function `bfnr`
51+
|
52+
= note: multiple `impl`s satisfying `_: Fn<()>` found in the following crates: `alloc`, `core`:
53+
- impl<A, F> Fn<A> for &F
54+
where A: Tuple, F: Fn<A>, F: ?Sized;
55+
- impl<Args, F, A> Fn<Args> for Box<F, A>
56+
where Args: Tuple, F: Fn<Args>, A: Allocator, F: ?Sized;
57+
note: required by a bound in `bfnr`
58+
--> $DIR/E0401.rs:4:30
59+
|
60+
LL | fn bfnr<U, V: Baz<U>, W: Fn()>(y: T) {
61+
| ^^^^ required by this bound in `bfnr`
62+
help: consider specifying the type arguments in the function call
63+
|
64+
LL | bfnr::<U, V, W>(x);
65+
| +++++++++++
66+
67+
error: aborting due to 5 previous errors
4768

48-
Some errors have detailed explanations: E0282, E0401.
69+
Some errors have detailed explanations: E0282, E0283, E0401.
4970
For more information about an error, try `rustc --explain E0282`.

src/test/ui/impl-trait/cross-return-site-inference.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,19 @@ fn baa(b: bool) -> impl std::fmt::Debug {
3030

3131
fn muh() -> Result<(), impl std::fmt::Debug> {
3232
Err("whoops")?;
33-
Ok(()) //~ ERROR type annotations needed
33+
Ok(())
34+
//~^ ERROR type annotations needed
3435
}
3536

3637
fn muh2() -> Result<(), impl std::fmt::Debug> {
37-
return Err(From::from("foo")); //~ ERROR type annotations needed
38+
return Err(From::from("foo"));
39+
//~^ ERROR type annotations needed
3840
Ok(())
3941
}
4042

4143
fn muh3() -> Result<(), impl std::fmt::Debug> {
42-
Err(From::from("foo")) //~ ERROR type annotations needed
44+
Err(From::from("foo"))
45+
//~^ ERROR type annotations needed
4346
}
4447

4548
fn main() {}

src/test/ui/impl-trait/cross-return-site-inference.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ LL | Ok::<(), E>(())
1010
| +++++++++
1111

1212
error[E0282]: type annotations needed
13-
--> $DIR/cross-return-site-inference.rs:37:12
13+
--> $DIR/cross-return-site-inference.rs:38:12
1414
|
1515
LL | return Err(From::from("foo"));
1616
| ^^^ cannot infer type of the type parameter `E` declared on the enum `Result`
@@ -21,7 +21,7 @@ LL | return Err::<(), E>(From::from("foo"));
2121
| +++++++++
2222

2323
error[E0282]: type annotations needed
24-
--> $DIR/cross-return-site-inference.rs:42:5
24+
--> $DIR/cross-return-site-inference.rs:44:5
2525
|
2626
LL | Err(From::from("foo"))
2727
| ^^^ cannot infer type of the type parameter `E` declared on the enum `Result`

src/test/ui/inference/cannot-infer-async.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ fn main() {
1010
let fut = async {
1111
make_unit()?;
1212

13-
Ok(()) //~ ERROR type annotations needed
13+
Ok(())
14+
//~^ ERROR type annotations needed
1415
};
1516
}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
fn main() {
22
let x = |a: (), b: ()| {
33
Err(a)?;
4-
Ok(b) //~ ERROR type annotations needed
4+
Ok(b)
5+
//~^ ERROR type annotations needed
56
};
67
}

0 commit comments

Comments
 (0)