From 397973b601f6b9802a01542ff3d171785437d1e1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 30 Aug 2017 16:06:05 -0400 Subject: [PATCH] make LUB/GLB of higher-ranked things actually do EQ --- src/librustc/infer/glb.rs | 25 ++++++++++++++++++- src/librustc/infer/lub.rs | 25 ++++++++++++++++++- src/librustc/ty/error.rs | 5 ++++ src/librustc/ty/structural_impls.rs | 5 +++- .../lub-glb-with-unbound-infer-var.rs | 24 ++++++++++++++++++ 5 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 src/test/run-pass/lub-glb-with-unbound-infer-var.rs diff --git a/src/librustc/infer/glb.rs b/src/librustc/infer/glb.rs index 8b42314ed97..982784f8c40 100644 --- a/src/librustc/infer/glb.rs +++ b/src/librustc/infer/glb.rs @@ -15,6 +15,7 @@ use super::Subtype; use traits::ObligationCause; use ty::{self, Ty, TyCtxt}; +use ty::error::TypeError; use ty::relate::{Relate, RelateResult, TypeRelation}; /// "Greatest lower bound" (common subtype) @@ -74,7 +75,29 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> -> RelateResult<'tcx, ty::Binder> 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) + } + } + } } } diff --git a/src/librustc/infer/lub.rs b/src/librustc/infer/lub.rs index 4a2a7a6bdfe..bb2df94edd4 100644 --- a/src/librustc/infer/lub.rs +++ b/src/librustc/infer/lub.rs @@ -15,6 +15,7 @@ use super::Subtype; use traits::ObligationCause; use ty::{self, Ty, TyCtxt}; +use ty::error::TypeError; use ty::relate::{Relate, RelateResult, TypeRelation}; /// "Least upper bound" (common supertype) @@ -74,7 +75,29 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> -> RelateResult<'tcx, ty::Binder> 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) + } + } + } } } diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs index 5cfa72c0712..afd1d04a870 100644 --- a/src/librustc/ty/error.rs +++ b/src/librustc/ty/error.rs @@ -54,6 +54,8 @@ pub enum TypeError<'tcx> { ProjectionBoundsLength(ExpectedFound), TyParamDefaultMismatch(ExpectedFound>), ExistentialMismatch(ExpectedFound<&'tcx ty::Slice>>), + + OldStyleLUB(Box>), } #[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), format!("trait `{}`", values.found)) } + OldStyleLUB(ref err) => { + write!(f, "{}", err) + } } } } diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 5f1448cd1f1..e5c24b4fcf9 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -428,7 +428,8 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> { TyParamDefaultMismatch(ref x) => { 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)), TyParamDefaultMismatch(ref x) => TyParamDefaultMismatch(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) }, Sorts(x) => x.visit_with(visitor), + OldStyleLUB(ref x) => x.visit_with(visitor), TyParamDefaultMismatch(ref x) => x.visit_with(visitor), ExistentialMismatch(x) => x.visit_with(visitor), Mismatch | diff --git a/src/test/run-pass/lub-glb-with-unbound-infer-var.rs b/src/test/run-pass/lub-glb-with-unbound-infer-var.rs new file mode 100644 index 00000000000..6b9bd67f9a5 --- /dev/null +++ b/src/test/run-pass/lub-glb-with-unbound-infer-var.rs @@ -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 or the MIT license +// , 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); +}