Skip to content

Commit a994a99

Browse files
committed
Always prefer where-clauses over impls in trait selection. Fixes #22110.
1 parent 2939e48 commit a994a99

File tree

2 files changed

+48
-57
lines changed

2 files changed

+48
-57
lines changed

src/librustc/middle/traits/select.rs

+14-57
Original file line numberDiff line numberDiff line change
@@ -652,8 +652,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
652652
let is_dup =
653653
(0..candidates.len())
654654
.filter(|&j| i != j)
655-
.any(|j| self.candidate_should_be_dropped_in_favor_of(stack,
656-
&candidates[i],
655+
.any(|j| self.candidate_should_be_dropped_in_favor_of(&candidates[i],
657656
&candidates[j]));
658657
if is_dup {
659658
debug!("Dropping candidate #{}/{}: {}",
@@ -1235,31 +1234,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12351234
self.evaluate_predicates_recursively(stack, selection.iter_nested())
12361235
}
12371236

1238-
/// Returns true if `candidate_i` should be dropped in favor of `candidate_j`.
1239-
///
1240-
/// This is generally true if either:
1241-
/// - candidate i and candidate j are equivalent; or,
1242-
/// - candidate i is a concrete impl and candidate j is a where clause bound,
1243-
/// and the concrete impl is applicable to the types in the where clause bound.
1244-
///
1245-
/// The last case refers to cases where there are blanket impls (often conditional
1246-
/// blanket impls) as well as a where clause. This can come down to one of two cases:
1247-
///
1248-
/// - The impl is truly unconditional (it has no where clauses
1249-
/// of its own), in which case the where clause is
1250-
/// unnecessary, because coherence requires that we would
1251-
/// pick that particular impl anyhow (at least so long as we
1252-
/// don't have specialization).
1253-
///
1254-
/// - The impl is conditional, in which case we may not have winnowed it out
1255-
/// because we don't know if the conditions apply, but the where clause is basically
1256-
/// telling us that there is some impl, though not necessarily the one we see.
1257-
///
1258-
/// In both cases we prefer to take the where clause, which is
1259-
/// essentially harmless. See issue #18453 for more details of
1260-
/// a case where doing the opposite caused us harm.
1237+
/// Returns true if `candidate_i` should be dropped in favor of
1238+
/// `candidate_j`. Generally speaking we will drop duplicate
1239+
/// candidates and prefer where-clause candidates.
12611240
fn candidate_should_be_dropped_in_favor_of<'o>(&mut self,
1262-
stack: &TraitObligationStack<'o, 'tcx>,
12631241
candidate_i: &SelectionCandidate<'tcx>,
12641242
candidate_j: &SelectionCandidate<'tcx>)
12651243
-> bool
@@ -1269,37 +1247,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12691247
}
12701248

12711249
match (candidate_i, candidate_j) {
1272-
(&ImplCandidate(impl_def_id), &ParamCandidate(ref bound)) => {
1273-
debug!("Considering whether to drop param {} in favor of impl {}",
1274-
candidate_i.repr(self.tcx()),
1275-
candidate_j.repr(self.tcx()));
1276-
1277-
self.infcx.probe(|snapshot| {
1278-
let (skol_obligation_trait_ref, skol_map) =
1279-
self.infcx().skolemize_late_bound_regions(
1280-
&stack.obligation.predicate, snapshot);
1281-
let impl_substs =
1282-
self.rematch_impl(impl_def_id, stack.obligation, snapshot,
1283-
&skol_map, skol_obligation_trait_ref.trait_ref.clone());
1284-
let impl_trait_ref =
1285-
ty::impl_trait_ref(self.tcx(), impl_def_id).unwrap();
1286-
let impl_trait_ref =
1287-
impl_trait_ref.subst(self.tcx(), &impl_substs.value);
1288-
let poly_impl_trait_ref =
1289-
ty::Binder(impl_trait_ref);
1290-
let origin =
1291-
infer::RelateOutputImplTypes(stack.obligation.cause.span);
1292-
self.infcx
1293-
.sub_poly_trait_refs(false, origin, poly_impl_trait_ref, bound.clone())
1294-
.is_ok()
1295-
})
1296-
}
1297-
(&BuiltinCandidate(_), &ParamCandidate(_)) => {
1298-
// If we have a where-clause like `Option<K> : Send`,
1299-
// then we wind up in a situation where there is a
1300-
// default rule (`Option<K>:Send if K:Send) and the
1301-
// where-clause that both seem applicable. Just take
1302-
// the where-clause in that case.
1250+
(&ImplCandidate(..), &ParamCandidate(..)) |
1251+
(&ClosureCandidate(..), &ParamCandidate(..)) |
1252+
(&FnPointerCandidate(..), &ParamCandidate(..)) |
1253+
(&BuiltinCandidate(..), &ParamCandidate(..)) => {
1254+
// We basically prefer always prefer to use a
1255+
// where-clause over another option. Where clauses
1256+
// impose the burden of finding the exact match onto
1257+
// the caller. Using an impl in preference of a where
1258+
// clause can also lead us to "overspecialize", as in
1259+
// #18453.
13031260
true
13041261
}
13051262
(&ProjectionCandidate, &ParamCandidate(_)) => {
+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test an issue where we reported ambiguity between the where-clause
12+
// and the blanket impl. The only important thing is that compilation
13+
// succeeds here. Issue #22110.
14+
15+
#![allow(dead_code)]
16+
17+
trait Foo<A> {
18+
fn foo(&self, a: A);
19+
}
20+
21+
impl<A,F:Fn(A)> Foo<A> for F {
22+
fn foo(&self, _: A) { }
23+
}
24+
25+
fn baz<A,F:for<'a> Foo<(&'a A,)>>(_: F) { }
26+
27+
fn components<T,A>(t: fn(&A))
28+
where fn(&A) : for<'a> Foo<(&'a A,)>,
29+
{
30+
baz(t)
31+
}
32+
33+
fn main() {
34+
}

0 commit comments

Comments
 (0)