Skip to content

Commit 397973b

Browse files
committed
make LUB/GLB of higher-ranked things actually do EQ
1 parent 02eed2e commit 397973b

File tree

5 files changed

+81
-3
lines changed

5 files changed

+81
-3
lines changed

src/librustc/infer/glb.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use super::Subtype;
1515

1616
use traits::ObligationCause;
1717
use ty::{self, Ty, TyCtxt};
18+
use ty::error::TypeError;
1819
use ty::relate::{Relate, RelateResult, TypeRelation};
1920

2021
/// "Greatest lower bound" (common subtype)
@@ -74,7 +75,29 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx>
7475
-> RelateResult<'tcx, ty::Binder<T>>
7576
where T: Relate<'tcx>
7677
{
77-
self.fields.higher_ranked_glb(a, b, self.a_is_expected)
78+
let was_error = self.infcx().probe(|_snapshot| {
79+
// Subtle: use a fresh combine-fields here because we recover
80+
// from Err. Doing otherwise could propagate obligations out
81+
// through our `self.obligations` field.
82+
self.infcx()
83+
.combine_fields(self.fields.trace.clone(), self.fields.param_env)
84+
.higher_ranked_glb(a, b, self.a_is_expected)
85+
.is_err()
86+
});
87+
88+
// When higher-ranked types are involved, computing the LUB is
89+
// very challenging, switch to invariance. This is obviously
90+
// overly conservative but works ok in practice.
91+
match self.relate_with_variance(ty::Variance::Invariant, a, b) {
92+
Ok(_) => Ok(a.clone()),
93+
Err(err) => {
94+
if !was_error {
95+
Err(TypeError::OldStyleLUB(Box::new(err)))
96+
} else {
97+
Err(err)
98+
}
99+
}
100+
}
78101
}
79102
}
80103

src/librustc/infer/lub.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use super::Subtype;
1515

1616
use traits::ObligationCause;
1717
use ty::{self, Ty, TyCtxt};
18+
use ty::error::TypeError;
1819
use ty::relate::{Relate, RelateResult, TypeRelation};
1920

2021
/// "Least upper bound" (common supertype)
@@ -74,7 +75,29 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx>
7475
-> RelateResult<'tcx, ty::Binder<T>>
7576
where T: Relate<'tcx>
7677
{
77-
self.fields.higher_ranked_lub(a, b, self.a_is_expected)
78+
let was_error = self.infcx().probe(|_snapshot| {
79+
// Subtle: use a fresh combine-fields here because we recover
80+
// from Err. Doing otherwise could propagate obligations out
81+
// through our `self.obligations` field.
82+
self.infcx()
83+
.combine_fields(self.fields.trace.clone(), self.fields.param_env)
84+
.higher_ranked_lub(a, b, self.a_is_expected)
85+
.is_err()
86+
});
87+
88+
// When higher-ranked types are involved, computing the LUB is
89+
// very challenging, switch to invariance. This is obviously
90+
// overly conservative but works ok in practice.
91+
match self.relate_with_variance(ty::Variance::Invariant, a, b) {
92+
Ok(_) => Ok(a.clone()),
93+
Err(err) => {
94+
if !was_error {
95+
Err(TypeError::OldStyleLUB(Box::new(err)))
96+
} else {
97+
Err(err)
98+
}
99+
}
100+
}
78101
}
79102
}
80103

src/librustc/ty/error.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ pub enum TypeError<'tcx> {
5454
ProjectionBoundsLength(ExpectedFound<usize>),
5555
TyParamDefaultMismatch(ExpectedFound<type_variable::Default<'tcx>>),
5656
ExistentialMismatch(ExpectedFound<&'tcx ty::Slice<ty::ExistentialPredicate<'tcx>>>),
57+
58+
OldStyleLUB(Box<TypeError<'tcx>>),
5759
}
5860

5961
#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)]
@@ -170,6 +172,9 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
170172
report_maybe_different(f, format!("trait `{}`", values.expected),
171173
format!("trait `{}`", values.found))
172174
}
175+
OldStyleLUB(ref err) => {
176+
write!(f, "{}", err)
177+
}
173178
}
174179
}
175180
}

src/librustc/ty/structural_impls.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,8 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> {
428428
TyParamDefaultMismatch(ref x) => {
429429
return tcx.lift(x).map(TyParamDefaultMismatch)
430430
}
431-
ExistentialMismatch(ref x) => return tcx.lift(x).map(ExistentialMismatch)
431+
ExistentialMismatch(ref x) => return tcx.lift(x).map(ExistentialMismatch),
432+
OldStyleLUB(ref x) => return tcx.lift(x).map(OldStyleLUB),
432433
})
433434
}
434435
}
@@ -1174,6 +1175,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> {
11741175
Sorts(x) => Sorts(x.fold_with(folder)),
11751176
TyParamDefaultMismatch(ref x) => TyParamDefaultMismatch(x.fold_with(folder)),
11761177
ExistentialMismatch(x) => ExistentialMismatch(x.fold_with(folder)),
1178+
OldStyleLUB(ref x) => OldStyleLUB(x.fold_with(folder)),
11771179
}
11781180
}
11791181

@@ -1191,6 +1193,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> {
11911193
b.visit_with(visitor)
11921194
},
11931195
Sorts(x) => x.visit_with(visitor),
1196+
OldStyleLUB(ref x) => x.visit_with(visitor),
11941197
TyParamDefaultMismatch(ref x) => x.visit_with(visitor),
11951198
ExistentialMismatch(x) => x.visit_with(visitor),
11961199
Mismatch |
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2016 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 for a specific corner case: when we compute the LUB of two fn
12+
// types and their parameters have unbound variables. In that case, we
13+
// wind up relating those two variables. This was causing an ICE in an
14+
// in-progress PR.
15+
16+
fn main() {
17+
let a_f: fn(_) = |_| ();
18+
let b_f: fn(_) = |_| ();
19+
let c_f = match 22 {
20+
0 => a_f,
21+
_ => b_f,
22+
};
23+
c_f(4);
24+
}

0 commit comments

Comments
 (0)