Skip to content

Commit 85f4f41

Browse files
committed
Auto merge of rust-lang#103252 - lcnr:recompute_applicable_impls, r=jackh726
selection failure: recompute applicable impls The way we currently skip errors for ambiguous trait obligations seems pretty fragile so we get some duplicate errors because of this. Removing this info from selection errors changes this system to be closer to my image of our new trait solver and is also making it far easier to change overflow errors to be non-fatal ✨ r? types cc `@estebank`
2 parents c5842b0 + 91d5a32 commit 85f4f41

21 files changed

+170
-75
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

+20-20
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,12 +2136,25 @@ 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.delay_as_bug();
2150+
return;
2151+
}
2152+
err.note(&format!("cannot satisfy `{}`", predicate));
2153+
}
21492154
}
21502155
_ => {
21512156
if self.is_tainted_by_errors() {
2152-
err.cancel();
2157+
err.delay_as_bug();
21532158
return;
21542159
}
21552160
err.note(&format!("cannot satisfy `{}`", predicate));
@@ -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)