Skip to content

Commit 29b854f

Browse files
committed
Auto merge of #68057 - Aaron1011:fix/marker-trait-selection, r=matthewjasper
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.
2 parents 7da653f + 4840cd8 commit 29b854f

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
@@ -1417,14 +1417,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
14171417

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

1420+
let needs_infer = stack.obligation.predicate.needs_infer();
1421+
14201422
// If there are STILL multiple candidates, we can further
14211423
// reduce the list by dropping duplicates -- including
14221424
// resolving specializations.
14231425
if candidates.len() > 1 {
14241426
let mut i = 0;
14251427
while i < candidates.len() {
14261428
let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| {
1427-
self.candidate_should_be_dropped_in_favor_of(&candidates[i], &candidates[j])
1429+
self.candidate_should_be_dropped_in_favor_of(
1430+
&candidates[i],
1431+
&candidates[j],
1432+
needs_infer,
1433+
)
14281434
});
14291435
if is_dup {
14301436
debug!("Dropping candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]);
@@ -2258,6 +2264,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
22582264
&mut self,
22592265
victim: &EvaluatedCandidate<'tcx>,
22602266
other: &EvaluatedCandidate<'tcx>,
2267+
needs_infer: bool,
22612268
) -> bool {
22622269
if victim.candidate == other.candidate {
22632270
return true;
@@ -2339,10 +2346,55 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
23392346
match victim.candidate {
23402347
ImplCandidate(victim_def) => {
23412348
let tcx = self.tcx();
2342-
return tcx.specializes((other_def, victim_def))
2343-
|| tcx
2344-
.impls_are_allowed_to_overlap(other_def, victim_def)
2345-
.is_some();
2349+
if tcx.specializes((other_def, victim_def)) {
2350+
return true;
2351+
}
2352+
return match tcx.impls_are_allowed_to_overlap(other_def, victim_def) {
2353+
Some(ty::ImplOverlapKind::Permitted { marker: true }) => {
2354+
// Subtle: If the predicate we are evaluating has inference
2355+
// variables, do *not* allow discarding candidates due to
2356+
// marker trait impls.
2357+
//
2358+
// Without this restriction, we could end up accidentally
2359+
// constrainting inference variables based on an arbitrarily
2360+
// chosen trait impl.
2361+
//
2362+
// Imagine we have the following code:
2363+
//
2364+
// ```rust
2365+
// #[marker] trait MyTrait {}
2366+
// impl MyTrait for u8 {}
2367+
// impl MyTrait for bool {}
2368+
// ```
2369+
//
2370+
// And we are evaluating the predicate `<_#0t as MyTrait>`.
2371+
//
2372+
// During selection, we will end up with one candidate for each
2373+
// impl of `MyTrait`. If we were to discard one impl in favor
2374+
// of the other, we would be left with one candidate, causing
2375+
// us to "successfully" select the predicate, unifying
2376+
// _#0t with (for example) `u8`.
2377+
//
2378+
// However, we have no reason to believe that this unification
2379+
// is correct - we've essentially just picked an arbitrary
2380+
// *possibility* for _#0t, and required that this be the *only*
2381+
// possibility.
2382+
//
2383+
// Eventually, we will either:
2384+
// 1) Unify all inference variables in the predicate through
2385+
// some other means (e.g. type-checking of a function). We will
2386+
// then be in a position to drop marker trait candidates
2387+
// without constraining inference variables (since there are
2388+
// none left to constrin)
2389+
// 2) Be left with some unconstrained inference variables. We
2390+
// will then correctly report an inference error, since the
2391+
// existence of multiple marker trait impls tells us nothing
2392+
// about which one should actually apply.
2393+
!needs_infer
2394+
}
2395+
Some(_) => true,
2396+
None => false,
2397+
};
23462398
}
23472399
ParamCandidate(ref cand) => {
23482400
// 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
@@ -2591,7 +2591,12 @@ impl<'tcx> ::std::ops::Deref for Attributes<'tcx> {
25912591
#[derive(Debug, PartialEq, Eq)]
25922592
pub enum ImplOverlapKind {
25932593
/// These impls are always allowed to overlap.
2594-
Permitted,
2594+
Permitted {
2595+
/// Whether or not the impl is permitted due to the trait being
2596+
/// a marker trait (a trait with #[marker], or a trait with
2597+
/// no associated items and #![feature(overlapping_marker_traits)] enabled)
2598+
marker: bool,
2599+
},
25952600
/// These impls are allowed to overlap, but that raises
25962601
/// an issue #33140 future-compatibility warning.
25972602
///
@@ -2711,7 +2716,7 @@ impl<'tcx> TyCtxt<'tcx> {
27112716
if self.impl_trait_ref(def_id1).map_or(false, |tr| tr.references_error())
27122717
|| self.impl_trait_ref(def_id2).map_or(false, |tr| tr.references_error())
27132718
{
2714-
return Some(ImplOverlapKind::Permitted);
2719+
return Some(ImplOverlapKind::Permitted { marker: false });
27152720
}
27162721

27172722
match (self.impl_polarity(def_id1), self.impl_polarity(def_id2)) {
@@ -2721,7 +2726,7 @@ impl<'tcx> TyCtxt<'tcx> {
27212726
"impls_are_allowed_to_overlap({:?}, {:?}) = Some(Permitted) (reservations)",
27222727
def_id1, def_id2
27232728
);
2724-
return Some(ImplOverlapKind::Permitted);
2729+
return Some(ImplOverlapKind::Permitted { marker: false });
27252730
}
27262731
(ImplPolarity::Positive, ImplPolarity::Negative)
27272732
| (ImplPolarity::Negative, ImplPolarity::Positive) => {
@@ -2757,7 +2762,7 @@ impl<'tcx> TyCtxt<'tcx> {
27572762
"impls_are_allowed_to_overlap({:?}, {:?}) = Some(Permitted) (marker overlap)",
27582763
def_id1, def_id2
27592764
);
2760-
Some(ImplOverlapKind::Permitted)
2765+
Some(ImplOverlapKind::Permitted { marker: true })
27612766
} else {
27622767
if let Some(self_ty1) = self.issue33140_self_ty(def_id1) {
27632768
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)