Skip to content

Commit 4840cd8

Browse files
committed
Don't discard marker trait impls when inference variables are present
Fixes #61651 Previously, we would unconditionally discard impl candidates for marker traits during trait selection. However, if the predicate had inference variables, this could have the effect of constrainting inference variables (due to a successful trait selection) when we would have otherwise failed due to mutliple applicable impls, This commit prevents marker trait impls from being discarded while the obligation predicate has any inference variables, ensuring that discarding impls will never cause us to incorrectly constraint inference variables.
1 parent adc6572 commit 4840cd8

File tree

4 files changed

+84
-10
lines changed

4 files changed

+84
-10
lines changed

src/librustc/traits/select.rs

+57-5
Original file line numberDiff line numberDiff line change
@@ -1412,14 +1412,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
14121412

14131413
debug!("winnowed to {} candidates for {:?}: {:?}", candidates.len(), stack, candidates);
14141414

1415+
let needs_infer = stack.obligation.predicate.needs_infer();
1416+
14151417
// If there are STILL multiple candidates, we can further
14161418
// reduce the list by dropping duplicates -- including
14171419
// resolving specializations.
14181420
if candidates.len() > 1 {
14191421
let mut i = 0;
14201422
while i < candidates.len() {
14211423
let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| {
1422-
self.candidate_should_be_dropped_in_favor_of(&candidates[i], &candidates[j])
1424+
self.candidate_should_be_dropped_in_favor_of(
1425+
&candidates[i],
1426+
&candidates[j],
1427+
needs_infer,
1428+
)
14231429
});
14241430
if is_dup {
14251431
debug!("Dropping candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]);
@@ -2253,6 +2259,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
22532259
&mut self,
22542260
victim: &EvaluatedCandidate<'tcx>,
22552261
other: &EvaluatedCandidate<'tcx>,
2262+
needs_infer: bool,
22562263
) -> bool {
22572264
if victim.candidate == other.candidate {
22582265
return true;
@@ -2334,10 +2341,55 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
23342341
match victim.candidate {
23352342
ImplCandidate(victim_def) => {
23362343
let tcx = self.tcx();
2337-
return tcx.specializes((other_def, victim_def))
2338-
|| tcx
2339-
.impls_are_allowed_to_overlap(other_def, victim_def)
2340-
.is_some();
2344+
if tcx.specializes((other_def, victim_def)) {
2345+
return true;
2346+
}
2347+
return match tcx.impls_are_allowed_to_overlap(other_def, victim_def) {
2348+
Some(ty::ImplOverlapKind::Permitted { marker: true }) => {
2349+
// Subtle: If the predicate we are evaluating has inference
2350+
// variables, do *not* allow discarding candidates due to
2351+
// marker trait impls.
2352+
//
2353+
// Without this restriction, we could end up accidentally
2354+
// constrainting inference variables based on an arbitrarily
2355+
// chosen trait impl.
2356+
//
2357+
// Imagine we have the following code:
2358+
//
2359+
// ```rust
2360+
// #[marker] trait MyTrait {}
2361+
// impl MyTrait for u8 {}
2362+
// impl MyTrait for bool {}
2363+
// ```
2364+
//
2365+
// And we are evaluating the predicate `<_#0t as MyTrait>`.
2366+
//
2367+
// During selection, we will end up with one candidate for each
2368+
// impl of `MyTrait`. If we were to discard one impl in favor
2369+
// of the other, we would be left with one candidate, causing
2370+
// us to "successfully" select the predicate, unifying
2371+
// _#0t with (for example) `u8`.
2372+
//
2373+
// However, we have no reason to believe that this unification
2374+
// is correct - we've essentially just picked an arbitrary
2375+
// *possibility* for _#0t, and required that this be the *only*
2376+
// possibility.
2377+
//
2378+
// Eventually, we will either:
2379+
// 1) Unify all inference variables in the predicate through
2380+
// some other means (e.g. type-checking of a function). We will
2381+
// then be in a position to drop marker trait candidates
2382+
// without constraining inference variables (since there are
2383+
// none left to constrin)
2384+
// 2) Be left with some unconstrained inference variables. We
2385+
// will then correctly report an inference error, since the
2386+
// existence of multiple marker trait impls tells us nothing
2387+
// about which one should actually apply.
2388+
!needs_infer
2389+
}
2390+
Some(_) => true,
2391+
None => false,
2392+
};
23412393
}
23422394
ParamCandidate(ref cand) => {
23432395
// Prefer the impl to a global where clause candidate.

src/librustc/traits/specialize/specialization_graph.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ impl<'tcx> Children {
163163
tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling)
164164
{
165165
match overlap_kind {
166-
ty::ImplOverlapKind::Permitted => {}
166+
ty::ImplOverlapKind::Permitted { marker: _ } => {}
167167
ty::ImplOverlapKind::Issue33140 => {
168168
last_lint = Some(FutureCompatOverlapError {
169169
error: overlap_error(overlap),

src/librustc/ty/mod.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -2662,7 +2662,12 @@ impl<'tcx> ::std::ops::Deref for Attributes<'tcx> {
26622662
#[derive(Debug, PartialEq, Eq)]
26632663
pub enum ImplOverlapKind {
26642664
/// These impls are always allowed to overlap.
2665-
Permitted,
2665+
Permitted {
2666+
/// Whether or not the impl is permitted due to the trait being
2667+
/// a marker trait (a trait with #[marker], or a trait with
2668+
/// no associated items and #![feature(overlapping_marker_traits)] enabled)
2669+
marker: bool,
2670+
},
26662671
/// These impls are allowed to overlap, but that raises
26672672
/// an issue #33140 future-compatibility warning.
26682673
///
@@ -2833,7 +2838,7 @@ impl<'tcx> TyCtxt<'tcx> {
28332838
if self.impl_trait_ref(def_id1).map_or(false, |tr| tr.references_error())
28342839
|| self.impl_trait_ref(def_id2).map_or(false, |tr| tr.references_error())
28352840
{
2836-
return Some(ImplOverlapKind::Permitted);
2841+
return Some(ImplOverlapKind::Permitted { marker: false });
28372842
}
28382843

28392844
match (self.impl_polarity(def_id1), self.impl_polarity(def_id2)) {
@@ -2843,7 +2848,7 @@ impl<'tcx> TyCtxt<'tcx> {
28432848
"impls_are_allowed_to_overlap({:?}, {:?}) = Some(Permitted) (reservations)",
28442849
def_id1, def_id2
28452850
);
2846-
return Some(ImplOverlapKind::Permitted);
2851+
return Some(ImplOverlapKind::Permitted { marker: false });
28472852
}
28482853
(ImplPolarity::Positive, ImplPolarity::Negative)
28492854
| (ImplPolarity::Negative, ImplPolarity::Positive) => {
@@ -2879,7 +2884,7 @@ impl<'tcx> TyCtxt<'tcx> {
28792884
"impls_are_allowed_to_overlap({:?}, {:?}) = Some(Permitted) (marker overlap)",
28802885
def_id1, def_id2
28812886
);
2882-
Some(ImplOverlapKind::Permitted)
2887+
Some(ImplOverlapKind::Permitted { marker: true })
28832888
} else {
28842889
if let Some(self_ty1) = self.issue33140_self_ty(def_id1) {
28852890
if let Some(self_ty2) = self.issue33140_self_ty(def_id2) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// check-pass
2+
// Regression test for issue #61651
3+
// Verifies that we don't try to constrain inference
4+
// variables due to the presence of multiple applicable
5+
// marker trait impls
6+
7+
#![feature(marker_trait_attr)]
8+
9+
#[marker] // Remove this line and it works?!?
10+
trait Foo<T> {}
11+
impl Foo<u16> for u8 {}
12+
impl Foo<[u8; 1]> for u8 {}
13+
fn foo<T: Foo<U>, U>(_: T) -> U { unimplemented!() }
14+
15+
fn main() {
16+
let _: u16 = foo(0_u8);
17+
}

0 commit comments

Comments
 (0)