make LUB/GLB of higher-ranked things actually do EQ

This commit is contained in:
Niko Matsakis 2017-08-30 16:06:05 -04:00
parent 02eed2e9a5
commit 397973b601
5 changed files with 81 additions and 3 deletions

View File

@ -15,6 +15,7 @@ use super::Subtype;
use traits::ObligationCause; use traits::ObligationCause;
use ty::{self, Ty, TyCtxt}; use ty::{self, Ty, TyCtxt};
use ty::error::TypeError;
use ty::relate::{Relate, RelateResult, TypeRelation}; use ty::relate::{Relate, RelateResult, TypeRelation};
/// "Greatest lower bound" (common subtype) /// "Greatest lower bound" (common subtype)
@ -74,7 +75,29 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx>
-> RelateResult<'tcx, ty::Binder<T>> -> RelateResult<'tcx, ty::Binder<T>>
where T: Relate<'tcx> where T: Relate<'tcx>
{ {
self.fields.higher_ranked_glb(a, b, self.a_is_expected) let was_error = self.infcx().probe(|_snapshot| {
// Subtle: use a fresh combine-fields here because we recover
// from Err. Doing otherwise could propagate obligations out
// through our `self.obligations` field.
self.infcx()
.combine_fields(self.fields.trace.clone(), self.fields.param_env)
.higher_ranked_glb(a, b, self.a_is_expected)
.is_err()
});
// When higher-ranked types are involved, computing the LUB is
// very challenging, switch to invariance. This is obviously
// overly conservative but works ok in practice.
match self.relate_with_variance(ty::Variance::Invariant, a, b) {
Ok(_) => Ok(a.clone()),
Err(err) => {
if !was_error {
Err(TypeError::OldStyleLUB(Box::new(err)))
} else {
Err(err)
}
}
}
} }
} }

View File

@ -15,6 +15,7 @@ use super::Subtype;
use traits::ObligationCause; use traits::ObligationCause;
use ty::{self, Ty, TyCtxt}; use ty::{self, Ty, TyCtxt};
use ty::error::TypeError;
use ty::relate::{Relate, RelateResult, TypeRelation}; use ty::relate::{Relate, RelateResult, TypeRelation};
/// "Least upper bound" (common supertype) /// "Least upper bound" (common supertype)
@ -74,7 +75,29 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx>
-> RelateResult<'tcx, ty::Binder<T>> -> RelateResult<'tcx, ty::Binder<T>>
where T: Relate<'tcx> where T: Relate<'tcx>
{ {
self.fields.higher_ranked_lub(a, b, self.a_is_expected) let was_error = self.infcx().probe(|_snapshot| {
// Subtle: use a fresh combine-fields here because we recover
// from Err. Doing otherwise could propagate obligations out
// through our `self.obligations` field.
self.infcx()
.combine_fields(self.fields.trace.clone(), self.fields.param_env)
.higher_ranked_lub(a, b, self.a_is_expected)
.is_err()
});
// When higher-ranked types are involved, computing the LUB is
// very challenging, switch to invariance. This is obviously
// overly conservative but works ok in practice.
match self.relate_with_variance(ty::Variance::Invariant, a, b) {
Ok(_) => Ok(a.clone()),
Err(err) => {
if !was_error {
Err(TypeError::OldStyleLUB(Box::new(err)))
} else {
Err(err)
}
}
}
} }
} }

View File

@ -54,6 +54,8 @@ pub enum TypeError<'tcx> {
ProjectionBoundsLength(ExpectedFound<usize>), ProjectionBoundsLength(ExpectedFound<usize>),
TyParamDefaultMismatch(ExpectedFound<type_variable::Default<'tcx>>), TyParamDefaultMismatch(ExpectedFound<type_variable::Default<'tcx>>),
ExistentialMismatch(ExpectedFound<&'tcx ty::Slice<ty::ExistentialPredicate<'tcx>>>), ExistentialMismatch(ExpectedFound<&'tcx ty::Slice<ty::ExistentialPredicate<'tcx>>>),
OldStyleLUB(Box<TypeError<'tcx>>),
} }
#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)] #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)]
@ -170,6 +172,9 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
report_maybe_different(f, format!("trait `{}`", values.expected), report_maybe_different(f, format!("trait `{}`", values.expected),
format!("trait `{}`", values.found)) format!("trait `{}`", values.found))
} }
OldStyleLUB(ref err) => {
write!(f, "{}", err)
}
} }
} }
} }

View File

@ -428,7 +428,8 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> {
TyParamDefaultMismatch(ref x) => { TyParamDefaultMismatch(ref x) => {
return tcx.lift(x).map(TyParamDefaultMismatch) return tcx.lift(x).map(TyParamDefaultMismatch)
} }
ExistentialMismatch(ref x) => return tcx.lift(x).map(ExistentialMismatch) ExistentialMismatch(ref x) => return tcx.lift(x).map(ExistentialMismatch),
OldStyleLUB(ref x) => return tcx.lift(x).map(OldStyleLUB),
}) })
} }
} }
@ -1174,6 +1175,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> {
Sorts(x) => Sorts(x.fold_with(folder)), Sorts(x) => Sorts(x.fold_with(folder)),
TyParamDefaultMismatch(ref x) => TyParamDefaultMismatch(x.fold_with(folder)), TyParamDefaultMismatch(ref x) => TyParamDefaultMismatch(x.fold_with(folder)),
ExistentialMismatch(x) => ExistentialMismatch(x.fold_with(folder)), ExistentialMismatch(x) => ExistentialMismatch(x.fold_with(folder)),
OldStyleLUB(ref x) => OldStyleLUB(x.fold_with(folder)),
} }
} }
@ -1191,6 +1193,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> {
b.visit_with(visitor) b.visit_with(visitor)
}, },
Sorts(x) => x.visit_with(visitor), Sorts(x) => x.visit_with(visitor),
OldStyleLUB(ref x) => x.visit_with(visitor),
TyParamDefaultMismatch(ref x) => x.visit_with(visitor), TyParamDefaultMismatch(ref x) => x.visit_with(visitor),
ExistentialMismatch(x) => x.visit_with(visitor), ExistentialMismatch(x) => x.visit_with(visitor),
Mismatch | Mismatch |

View File

@ -0,0 +1,24 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test for a specific corner case: when we compute the LUB of two fn
// types and their parameters have unbound variables. In that case, we
// wind up relating those two variables. This was causing an ICE in an
// in-progress PR.
fn main() {
let a_f: fn(_) = |_| ();
let b_f: fn(_) = |_| ();
let c_f = match 22 {
0 => a_f,
_ => b_f,
};
c_f(4);
}