Skip to content

replace the leak check with universes, take 2 #65232

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Feb 8, 2020
Merged
5 changes: 4 additions & 1 deletion src/librustc/ich/impls_ty.rs
Original file line number Diff line number Diff line change
@@ -63,9 +63,12 @@ impl<'a> HashStable<StableHashingContext<'a>> for ty::RegionKind {
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
mem::discriminant(self).hash_stable(hcx, hasher);
match *self {
ty::ReErased | ty::ReStatic | ty::ReEmpty => {
ty::ReErased | ty::ReStatic => {
// No variant fields to hash for these ...
}
ty::ReEmpty(universe) => {
universe.hash_stable(hcx, hasher);
}
ty::ReLateBound(db, ty::BrAnon(i)) => {
db.hash_stable(hcx, hasher);
i.hash_stable(hcx, hasher);
17 changes: 14 additions & 3 deletions src/librustc/infer/canonical/canonicalizer.rs
Original file line number Diff line number Diff line change
@@ -167,18 +167,29 @@ impl CanonicalizeRegionMode for CanonicalizeQueryResponse {
r: ty::Region<'tcx>,
) -> ty::Region<'tcx> {
match r {
ty::ReFree(_) | ty::ReEmpty | ty::ReErased | ty::ReStatic | ty::ReEarlyBound(..) => r,
ty::ReFree(_)
| ty::ReErased
| ty::ReStatic
| ty::ReEmpty(ty::UniverseIndex::ROOT)
| ty::ReEarlyBound(..) => r,

ty::RePlaceholder(placeholder) => canonicalizer.canonical_var_for_region(
CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderRegion(*placeholder) },
r,
),

ty::ReVar(vid) => {
let universe = canonicalizer.region_var_universe(*vid);
canonicalizer.canonical_var_for_region(
CanonicalVarInfo { kind: CanonicalVarKind::Region(universe) },
r,
)
}

ty::ReEmpty(ui) => {
bug!("canonicalizing 'empty in universe {:?}", ui) // FIXME
}

_ => {
// Other than `'static` or `'empty`, the query
// response should be executing in a fully
@@ -213,7 +224,7 @@ impl CanonicalizeRegionMode for CanonicalizeUserTypeAnnotation {
r: ty::Region<'tcx>,
) -> ty::Region<'tcx> {
match r {
ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReErased | ty::ReEmpty | ty::ReStatic => r,
ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReErased | ty::ReStatic => r,
ty::ReVar(_) => canonicalizer.canonical_var_for_region_in_root_universe(r),
_ => {
// We only expect region names that the user can type.
@@ -320,8 +331,8 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
| ty::ReEarlyBound(..)
| ty::ReFree(_)
| ty::ReScope(_)
| ty::ReEmpty(_)
| ty::RePlaceholder(..)
| ty::ReEmpty
| ty::ReErased => self.canonicalize_region_mode.canonicalize_free_region(self, r),

ty::ReClosureBound(..) => {
2 changes: 1 addition & 1 deletion src/librustc/infer/combine.rs
Original file line number Diff line number Diff line change
@@ -577,7 +577,7 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {

ty::RePlaceholder(..)
| ty::ReVar(..)
| ty::ReEmpty
| ty::ReEmpty(_)
| ty::ReStatic
| ty::ReScope(..)
| ty::ReEarlyBound(..)
35 changes: 33 additions & 2 deletions src/librustc/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
@@ -138,7 +138,10 @@ pub(super) fn note_and_explain_region(
msg_span_from_free_region(tcx, region)
}

ty::ReEmpty => ("the empty lifetime".to_owned(), None),
ty::ReEmpty(ty::UniverseIndex::ROOT) => ("the empty lifetime".to_owned(), None),

// uh oh, hope no user ever sees THIS
ty::ReEmpty(ui) => (format!("the empty lifetime in universe {:?}", ui), None),

ty::RePlaceholder(_) => (format!("any other region"), None),

@@ -181,7 +184,8 @@ fn msg_span_from_free_region(
msg_span_from_early_bound_and_free_regions(tcx, region)
}
ty::ReStatic => ("the static lifetime".to_owned(), None),
ty::ReEmpty => ("an empty lifetime".to_owned(), None),
ty::ReEmpty(ty::UniverseIndex::ROOT) => ("an empty lifetime".to_owned(), None),
ty::ReEmpty(ui) => (format!("an empty lifetime in universe {:?}", ui), None),
_ => bug!("{:?}", region),
}
}
@@ -375,6 +379,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
}
}

RegionResolutionError::UpperBoundUniverseConflict(
_,
_,
var_universe,
sup_origin,
sup_r,
) => {
assert!(sup_r.is_placeholder());

// Make a dummy value for the "sub region" --
// this is the initial value of the
// placeholder. In practice, we expect more
// tailored errors that don't really use this
// value.
let sub_r = self.tcx.mk_region(ty::ReEmpty(var_universe));

self.report_placeholder_failure(
region_scope_tree,
sup_origin,
sub_r,
sup_r,
)
.emit();
}

RegionResolutionError::MemberConstraintFailure {
opaque_type_def_id,
hidden_ty,
@@ -429,6 +458,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
RegionResolutionError::GenericBoundFailure(..) => true,
RegionResolutionError::ConcreteFailure(..)
| RegionResolutionError::SubSupConflict(..)
| RegionResolutionError::UpperBoundUniverseConflict(..)
| RegionResolutionError::MemberConstraintFailure { .. } => false,
};

@@ -443,6 +473,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(),
RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(),
RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _) => rvo.span(),
RegionResolutionError::UpperBoundUniverseConflict(_, ref rvo, _, _, _) => rvo.span(),
RegionResolutionError::MemberConstraintFailure { span, .. } => span,
});
errors
Original file line number Diff line number Diff line change
@@ -45,7 +45,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
///
/// It will later be extended to trait objects.
pub(super) fn try_report_anon_anon_conflict(&self) -> Option<ErrorReported> {
let (span, sub, sup) = self.regions();
let (span, sub, sup) = self.regions()?;

// Determine whether the sub and sup consist of both anonymous (elided) regions.
let anon_reg_sup = self.tcx().is_suitable_region(sup)?;
18 changes: 7 additions & 11 deletions src/librustc/infer/error_reporting/nice_region_error/mod.rs
Original file line number Diff line number Diff line change
@@ -17,11 +17,6 @@ mod util;

impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
pub fn try_report_nice_region_error(&self, error: &RegionResolutionError<'tcx>) -> bool {
match *error {
ConcreteFailure(..) | SubSupConflict(..) => {}
_ => return false, // inapplicable
}

if let Some(tables) = self.in_progress_tables {
let tables = tables.borrow();
NiceRegionError::new(self, error.clone(), Some(&tables)).try_report().is_some()
@@ -79,13 +74,14 @@ impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> {
.or_else(|| self.try_report_impl_not_conforming_to_trait())
}

pub fn regions(&self) -> (Span, ty::Region<'tcx>, ty::Region<'tcx>) {
pub fn regions(&self) -> Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)> {
match (&self.error, self.regions) {
(Some(ConcreteFailure(origin, sub, sup)), None) => (origin.span(), sub, sup),
(Some(SubSupConflict(_, _, origin, sub, _, sup)), None) => (origin.span(), sub, sup),
(None, Some((span, sub, sup))) => (span, sub, sup),
(Some(_), Some(_)) => panic!("incorrectly built NiceRegionError"),
_ => panic!("trying to report on an incorrect lifetime failure"),
(Some(ConcreteFailure(origin, sub, sup)), None) => Some((origin.span(), sub, sup)),
(Some(SubSupConflict(_, _, origin, sub, _, sup)), None) => {
Some((origin.span(), sub, sup))
}
(None, Some((span, sub, sup))) => Some((span, sub, sup)),
_ => None,
}
}
}
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
/// When given a `ConcreteFailure` for a function with parameters containing a named region and
/// an anonymous region, emit an descriptive diagnostic error.
pub(super) fn try_report_named_anon_conflict(&self) -> Option<DiagnosticBuilder<'a>> {
let (span, sub, sup) = self.regions();
let (span, sub, sup) = self.regions()?;

debug!(
"try_report_named_anon_conflict(sub={:?}, sup={:?}, error={:?})",
Original file line number Diff line number Diff line change
@@ -107,6 +107,25 @@ impl NiceRegionError<'me, 'tcx> {
found.substs,
)),

Some(RegionResolutionError::UpperBoundUniverseConflict(
vid,
_,
_,
SubregionOrigin::Subtype(box TypeTrace {
cause,
values: ValuePairs::TraitRefs(ExpectedFound { expected, found }),
}),
sup_placeholder @ ty::RePlaceholder(_),
)) if expected.def_id == found.def_id => Some(self.try_report_placeholders_trait(
Some(self.tcx().mk_region(ty::ReVar(*vid))),
cause,
None,
Some(*sup_placeholder),
expected.def_id,
expected.substs,
found.substs,
)),

Some(RegionResolutionError::ConcreteFailure(
SubregionOrigin::Subtype(box TypeTrace {
cause,
2 changes: 1 addition & 1 deletion src/librustc/infer/freshen.rs
Original file line number Diff line number Diff line change
@@ -130,7 +130,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> {
| ty::ReScope(_)
| ty::ReVar(_)
| ty::RePlaceholder(..)
| ty::ReEmpty
| ty::ReEmpty(_)
| ty::ReErased => {
// replace all free regions with 'erased
self.tcx().lifetimes.re_erased
10 changes: 10 additions & 0 deletions src/librustc/infer/higher_ranked/mod.rs
Original file line number Diff line number Diff line change
@@ -128,6 +128,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
placeholder_map: &PlaceholderMap<'tcx>,
snapshot: &CombinedSnapshot<'_, 'tcx>,
) -> RelateResult<'tcx, ()> {
// If the user gave `-Zno-leak-check`, or we have been
// configured to skip the leak check, then skip the leak check
// completely. The leak check is deprecated. Any legitimate
// subtyping errors that it would have caught will now be
// caught later on, during region checking. However, we
// continue to use it for a transition period.
if self.tcx.sess.opts.debugging_opts.no_leak_check || self.skip_leak_check.get() {
return Ok(());
}

self.borrow_region_constraints().leak_check(
self.tcx,
overly_polymorphic,
148 changes: 123 additions & 25 deletions src/librustc/infer/lexical_region_resolve/mod.rs
Original file line number Diff line number Diff line change
@@ -82,6 +82,16 @@ pub enum RegionResolutionError<'tcx> {
Region<'tcx>,
),

/// Indicates a `'b: 'a` constraint where `'a` is in a universe that
/// cannot name the placeholder `'b`.
UpperBoundUniverseConflict(
RegionVid,
RegionVariableOrigin,
ty::UniverseIndex, // the universe index of the region variable
SubregionOrigin<'tcx>, // cause of the constraint
Region<'tcx>, // the placeholder `'b`
),

/// Indicates a failure of a `MemberConstraint`. These arise during
/// impl trait processing explicitly -- basically, the impl trait's hidden type
/// included some region that it was not supposed to.
@@ -149,7 +159,14 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
fn construct_var_data(&self, tcx: TyCtxt<'tcx>) -> LexicalRegionResolutions<'tcx> {
LexicalRegionResolutions {
error_region: tcx.lifetimes.re_static,
values: IndexVec::from_elem_n(VarValue::Value(tcx.lifetimes.re_empty), self.num_vars()),
values: IndexVec::from_fn_n(
|vid| {
let vid_universe = self.var_infos[vid].universe;
let re_empty = tcx.mk_region(ty::ReEmpty(vid_universe));
VarValue::Value(re_empty)
},
self.num_vars(),
),
}
}

@@ -381,8 +398,11 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
// This is a specialized version of the `lub_concrete_regions`
// check below for a common case, here purely as an
// optimization.
if let ReEmpty = a_region {
return false;
let b_universe = self.var_infos[b_vid].universe;
if let ReEmpty(a_universe) = a_region {
if *a_universe == b_universe {
return false;
}
}

let mut lub = self.lub_concrete_regions(a_region, cur_region);
@@ -399,7 +419,6 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
// tighter bound than `'static`.
//
// (This might e.g. arise from being asked to prove `for<'a> { 'b: 'a }`.)
let b_universe = self.var_infos[b_vid].universe;
if let ty::RePlaceholder(p) = lub {
if b_universe.cannot_name(p.universe) {
lub = self.tcx().lifetimes.re_static;
@@ -420,12 +439,38 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {

/// True if `a <= b`, but not defined over inference variables.
fn sub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> bool {
let tcx = self.tcx();
let sub_free_regions = |r1, r2| self.region_rels.free_regions.sub_free_regions(tcx, r1, r2);

// Check for the case where we know that `'b: 'static` -- in that case,
// `a <= b` for all `a`.
let b_free_or_static = self.region_rels.free_regions.is_free_or_static(b);
if b_free_or_static && sub_free_regions(tcx.lifetimes.re_static, b) {
return true;
}

// If both `a` and `b` are free, consult the declared
// relationships. Note that this can be more precise than the
// `lub` relationship defined below, since sometimes the "lub"
// is actually the `postdom_upper_bound` (see
// `TransitiveRelation` for more details).
let a_free_or_static = self.region_rels.free_regions.is_free_or_static(a);
if a_free_or_static && b_free_or_static {
return sub_free_regions(a, b);
}

// For other cases, leverage the LUB code to find the LUB and
// check if it is equal to `b`.
self.lub_concrete_regions(a, b) == b
}

/// Returns the smallest region `c` such that `a <= c` and `b <= c`.
/// Returns the least-upper-bound of `a` and `b`; i.e., the
/// smallest region `c` such that `a <= c` and `b <= c`.
///
/// Neither `a` nor `b` may be an inference variable (hence the
/// term "concrete regions").
fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> {
match (a, b) {
let r = match (a, b) {
(&ty::ReClosureBound(..), _)
| (_, &ty::ReClosureBound(..))
| (&ReLateBound(..), _)
@@ -435,14 +480,6 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
bug!("cannot relate region: LUB({:?}, {:?})", a, b);
}

(r @ &ReStatic, _) | (_, r @ &ReStatic) => {
r // nothing lives longer than static
}

(&ReEmpty, r) | (r, &ReEmpty) => {
r // everything lives longer than empty
}

(&ReVar(v_id), _) | (_, &ReVar(v_id)) => {
span_bug!(
self.var_infos[v_id].origin.span(),
@@ -453,6 +490,41 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
);
}

(&ReStatic, _) | (_, &ReStatic) => {
// nothing lives longer than `'static`
self.tcx().lifetimes.re_static
}

(&ReEmpty(_), r @ ReEarlyBound(_))
| (r @ ReEarlyBound(_), &ReEmpty(_))
| (&ReEmpty(_), r @ ReFree(_))
| (r @ ReFree(_), &ReEmpty(_))
| (&ReEmpty(_), r @ ReScope(_))
| (r @ ReScope(_), &ReEmpty(_)) => {
// All empty regions are less than early-bound, free,
// and scope regions.
r
}

(&ReEmpty(a_ui), &ReEmpty(b_ui)) => {
// Empty regions are ordered according to the universe
// they are associated with.
let ui = a_ui.min(b_ui);
self.tcx().mk_region(ReEmpty(ui))
}

(&ReEmpty(empty_ui), &RePlaceholder(placeholder))
| (&RePlaceholder(placeholder), &ReEmpty(empty_ui)) => {
// If this empty region is from a universe that can
// name the placeholder, then the placeholder is
// larger; otherwise, the only ancestor is `'static`.
if empty_ui.can_name(placeholder.universe) {
self.tcx().mk_region(RePlaceholder(placeholder))
} else {
self.tcx().lifetimes.re_static
}
}

(&ReEarlyBound(_), &ReScope(s_id))
| (&ReScope(s_id), &ReEarlyBound(_))
| (&ReFree(_), &ReScope(s_id))
@@ -509,7 +581,11 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
self.tcx().lifetimes.re_static
}
}
}
};

debug!("lub_concrete_regions({:?}, {:?}) = {:?}", a, b, r);

r
}

/// After expansion is complete, go and check upper bounds (i.e.,
@@ -528,7 +604,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
}

Constraint::RegSubReg(sub, sup) => {
if self.region_rels.is_subregion_of(sub, sup) {
if self.sub_concrete_regions(sub, sup) {
continue;
}

@@ -557,7 +633,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
// Do not report these errors immediately:
// instead, set the variable value to error and
// collect them later.
if !self.region_rels.is_subregion_of(a_region, b_region) {
if !self.sub_concrete_regions(a_region, b_region) {
debug!(
"collect_errors: region error at {:?}: \
cannot verify that {:?}={:?} <= {:?}",
@@ -592,12 +668,6 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
debug!("collect_errors: verify={:?}", verify);
let sub = var_data.normalize(self.tcx(), verify.region);

// This was an inference variable which didn't get
// constrained, therefore it can be assume to hold.
if let ty::ReEmpty = *sub {
continue;
}

let verify_kind_ty = verify.kind.to_ty(self.tcx());
if self.bound_is_met(&verify.bound, var_data, verify_kind_ty, sub) {
continue;
@@ -760,7 +830,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
};

for upper_bound in &upper_bounds {
if !self.region_rels.is_subregion_of(effective_lower_bound, upper_bound.region) {
if !self.sub_concrete_regions(effective_lower_bound, upper_bound.region) {
let origin = self.var_infos[node_idx].origin;
debug!(
"region inference error at {:?} for {:?}: SubSupConflict sub: {:?} \
@@ -780,6 +850,26 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
}
}

// If we have a scenario like `exists<'a> { forall<'b> { 'b:
// 'a } }`, we wind up without any lower-bound -- all we have
// are placeholders as upper bounds, but the universe of the
// variable `'a` doesn't permit those placeholders.
for upper_bound in &upper_bounds {
if let ty::RePlaceholder(p) = upper_bound.region {
if node_universe.cannot_name(p.universe) {
let origin = self.var_infos[node_idx].origin.clone();
errors.push(RegionResolutionError::UpperBoundUniverseConflict(
node_idx,
origin,
node_universe,
upper_bound.origin.clone(),
upper_bound.region,
));
return;
}
}
}

// Errors in earlier passes can yield error variables without
// resolution errors here; delay ICE in favor of those errors.
self.tcx().sess.delay_span_bug(
@@ -890,7 +980,15 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
}

VerifyBound::OutlivedBy(r) => {
self.region_rels.is_subregion_of(min, var_values.normalize(self.tcx(), r))
self.sub_concrete_regions(min, var_values.normalize(self.tcx(), r))
}

VerifyBound::IsEmpty => {
if let ty::ReEmpty(_) = min {
true
} else {
false
}
}

VerifyBound::AnyBound(bs) => {
36 changes: 29 additions & 7 deletions src/librustc/infer/mod.rs
Original file line number Diff line number Diff line change
@@ -125,6 +125,13 @@ pub struct InferCtxt<'a, 'tcx> {
/// order, represented by its upper and lower bounds.
pub type_variables: RefCell<type_variable::TypeVariableTable<'tcx>>,

/// If set, this flag causes us to skip the 'leak check' during
/// higher-ranked subtyping operations. This flag is a temporary one used
/// to manage the removal of the leak-check: for the time being, we still run the
/// leak-check, but we issue warnings. This flag can only be set to true
/// when entering a snapshot.
skip_leak_check: Cell<bool>,

/// Map from const parameter variable to the kind of const it represents.
const_unification_table: RefCell<ut::UnificationTable<ut::InPlace<ty::ConstVid<'tcx>>>>,

@@ -246,7 +253,7 @@ pub enum ValuePairs<'tcx> {
/// encounter an error or subtyping constraint.
///
/// See the `error_reporting` module for more details.
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct TypeTrace<'tcx> {
cause: ObligationCause<'tcx>,
values: ValuePairs<'tcx>,
@@ -550,6 +557,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
tainted_by_errors_flag: Cell::new(false),
err_count_on_creation: tcx.sess.err_count(),
in_snapshot: Cell::new(false),
skip_leak_check: Cell::new(false),
region_obligations: RefCell::new(vec![]),
universe: Cell::new(ty::UniverseIndex::ROOT),
})
@@ -593,6 +601,7 @@ pub struct CombinedSnapshot<'a, 'tcx> {
region_obligations_snapshot: usize,
universe: ty::UniverseIndex,
was_in_snapshot: bool,
was_skip_leak_check: bool,
_in_progress_tables: Option<Ref<'a, ty::TypeckTables<'tcx>>>,
}

@@ -720,6 +729,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
region_obligations_snapshot: self.region_obligations.borrow().len(),
universe: self.universe(),
was_in_snapshot: in_snapshot,
was_skip_leak_check: self.skip_leak_check.get(),
// Borrow tables "in progress" (i.e., during typeck)
// to ban writes from within a snapshot to them.
_in_progress_tables: self.in_progress_tables.map(|tables| tables.borrow()),
@@ -738,11 +748,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
region_obligations_snapshot,
universe,
was_in_snapshot,
was_skip_leak_check,
_in_progress_tables,
} = snapshot;

self.in_snapshot.set(was_in_snapshot);
self.universe.set(universe);
self.skip_leak_check.set(was_skip_leak_check);

self.projection_cache.borrow_mut().rollback_to(projection_cache_snapshot);
self.type_variables.borrow_mut().rollback_to(type_snapshot);
@@ -765,10 +777,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
region_obligations_snapshot: _,
universe: _,
was_in_snapshot,
was_skip_leak_check,
_in_progress_tables,
} = snapshot;

self.in_snapshot.set(was_in_snapshot);
self.skip_leak_check.set(was_skip_leak_check);

self.projection_cache.borrow_mut().commit(projection_cache_snapshot);
self.type_variables.borrow_mut().commit(type_snapshot);
@@ -822,6 +836,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
r
}

/// If `should_skip` is true, then execute `f` then unroll any bindings it creates.
pub fn probe_maybe_skip_leak_check<R, F>(&self, should_skip: bool, f: F) -> R
where
F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> R,
{
debug!("probe()");
let snapshot = self.start_snapshot();
let skip_leak_check = should_skip || self.skip_leak_check.get();
self.skip_leak_check.set(skip_leak_check);
let r = f(&snapshot);
self.rollback_to("probe", snapshot);
r
}

/// Scan the constraints produced since `snapshot` began and returns:
///
/// - `None` -- if none of them involve "region outlives" constraints
@@ -1647,12 +1675,6 @@ impl<'tcx> TypeTrace<'tcx> {
}
}

impl<'tcx> fmt::Debug for TypeTrace<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "TypeTrace({:?})", self.cause)
}
}

impl<'tcx> SubregionOrigin<'tcx> {
pub fn span(&self) -> Span {
match *self {
8 changes: 4 additions & 4 deletions src/librustc/infer/opaque_types/mod.rs
Original file line number Diff line number Diff line change
@@ -384,9 +384,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
match least_region {
None => least_region = Some(subst_arg),
Some(lr) => {
if free_region_relations.sub_free_regions(lr, subst_arg) {
if free_region_relations.sub_free_regions(self.tcx, lr, subst_arg) {
// keep the current least region
} else if free_region_relations.sub_free_regions(subst_arg, lr) {
} else if free_region_relations.sub_free_regions(self.tcx, subst_arg, lr) {
// switch to `subst_arg`
least_region = Some(subst_arg);
} else {
@@ -611,7 +611,7 @@ pub fn unexpected_hidden_region_diagnostic(
);

// Explain the region we are capturing.
if let ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic | ty::ReEmpty = hidden_region {
if let ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic | ty::ReEmpty(_) = hidden_region {
// Assuming regionck succeeded (*), we ought to always be
// capturing *some* region from the fn header, and hence it
// ought to be free. So under normal circumstances, we will go
@@ -843,7 +843,7 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> {
.emit();
}
}
self.tcx.lifetimes.re_empty
self.tcx.lifetimes.re_root_empty
}
None => {
self.tcx
13 changes: 12 additions & 1 deletion src/librustc/infer/outlives/verify.rs
Original file line number Diff line number Diff line change
@@ -60,7 +60,18 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
// scope type parameters:
let param_bounds = param_bounds.chain(self.implicit_region_bound);

VerifyBound::AnyBound(param_bounds.map(|r| VerifyBound::OutlivedBy(r)).collect())
let any_bounds: Vec<_> = param_bounds.map(|r| VerifyBound::OutlivedBy(r)).collect();

if any_bounds.is_empty() {
// We know that all types `T` outlive `'empty`, so if we
// can find no other bound, then check that the region
// being tested is `'empty`.
VerifyBound::IsEmpty
} else {
// If we can find any other bound `R` such that `T: R`, then
// we don't need to check for `'empty`, because `R: 'empty`.
VerifyBound::AnyBound(any_bounds)
}
}

/// Given a projection like `T::Item`, searches the environment
12 changes: 0 additions & 12 deletions src/librustc/infer/region_constraints/leak_check.rs
Original file line number Diff line number Diff line change
@@ -33,18 +33,6 @@ impl<'tcx> RegionConstraintCollector<'tcx> {

assert!(self.in_snapshot());

// If the user gave `-Zno-leak-check`, then skip the leak
// check completely. This is wildly unsound and also not
// unlikely to cause an ICE or two. It is intended for use
// only during a transition period, in which the MIR typeck
// uses the "universe-style" check, and the rest of typeck
// uses the more conservative leak check. Since the leak
// check is more conservative, we can't test the
// universe-style check without disabling it.
if tcx.sess.opts.debugging_opts.no_leak_check {
return Ok(());
}

// Go through each placeholder that we created.
for (_, &placeholder_region) in placeholder_map {
// Find the universe this placeholder inhabits.
8 changes: 6 additions & 2 deletions src/librustc/infer/region_constraints/mod.rs
Original file line number Diff line number Diff line change
@@ -233,6 +233,9 @@ pub enum VerifyBound<'tcx> {
/// if `R: min`, then by transitivity `G: min`.
OutlivedBy(Region<'tcx>),

/// Given a region `R`, true if it is `'empty`.
IsEmpty,

/// Given a set of bounds `B`, expands to the function:
///
/// ```rust
@@ -792,10 +795,10 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
match *region {
ty::ReScope(..)
| ty::ReStatic
| ty::ReEmpty
| ty::ReErased
| ty::ReFree(..)
| ty::ReEarlyBound(..) => ty::UniverseIndex::ROOT,
ty::ReEmpty(ui) => ui,
ty::RePlaceholder(placeholder) => placeholder.universe,
ty::ReClosureBound(vid) | ty::ReVar(vid) => self.var_universe(vid),
ty::ReLateBound(..) => bug!("universe(): encountered bound region {:?}", region),
@@ -867,6 +870,7 @@ impl<'tcx> VerifyBound<'tcx> {
VerifyBound::IfEq(..) => false,
VerifyBound::OutlivedBy(ty::ReStatic) => true,
VerifyBound::OutlivedBy(_) => false,
VerifyBound::IsEmpty => false,
VerifyBound::AnyBound(bs) => bs.iter().any(|b| b.must_hold()),
VerifyBound::AllBounds(bs) => bs.iter().all(|b| b.must_hold()),
}
@@ -875,7 +879,7 @@ impl<'tcx> VerifyBound<'tcx> {
pub fn cannot_hold(&self) -> bool {
match self {
VerifyBound::IfEq(_, b) => b.cannot_hold(),
VerifyBound::OutlivedBy(ty::ReEmpty) => true,
VerifyBound::IsEmpty => false,
VerifyBound::OutlivedBy(_) => false,
VerifyBound::AnyBound(bs) => bs.iter().all(|b| b.cannot_hold()),
VerifyBound::AllBounds(bs) => bs.iter().any(|b| b.cannot_hold()),
60 changes: 2 additions & 58 deletions src/librustc/middle/free_region.rs
Original file line number Diff line number Diff line change
@@ -4,8 +4,8 @@
//! and use that to decide when one free region outlives another, and so forth.
use crate::middle::region;
use crate::ty::free_region_map::{FreeRegionMap, FreeRegionRelations};
use crate::ty::{self, Region, TyCtxt};
use crate::ty::free_region_map::FreeRegionMap;
use crate::ty::{Region, TyCtxt};
use rustc_hir::def_id::DefId;

/// Combines a `region::ScopeTree` (which governs relationships between
@@ -38,62 +38,6 @@ impl<'a, 'tcx> RegionRelations<'a, 'tcx> {
Self { tcx, context, region_scope_tree, free_regions }
}

/// Determines whether one region is a subregion of another. This is intended to run *after
/// inference* and sadly the logic is somewhat duplicated with the code in infer.rs.
pub fn is_subregion_of(
&self,
sub_region: ty::Region<'tcx>,
super_region: ty::Region<'tcx>,
) -> bool {
let result = sub_region == super_region || {
match (sub_region, super_region) {
(ty::ReEmpty, _) | (_, ty::ReStatic) => true,

(ty::ReScope(sub_scope), ty::ReScope(super_scope)) => {
self.region_scope_tree.is_subscope_of(*sub_scope, *super_scope)
}

(ty::ReScope(sub_scope), ty::ReEarlyBound(ref br)) => {
let fr_scope = self.region_scope_tree.early_free_scope(self.tcx, br);
self.region_scope_tree.is_subscope_of(*sub_scope, fr_scope)
}

(ty::ReScope(sub_scope), ty::ReFree(fr)) => {
let fr_scope = self.region_scope_tree.free_scope(self.tcx, fr);
self.region_scope_tree.is_subscope_of(*sub_scope, fr_scope)
}

(ty::ReEarlyBound(_), ty::ReEarlyBound(_))
| (ty::ReFree(_), ty::ReEarlyBound(_))
| (ty::ReEarlyBound(_), ty::ReFree(_))
| (ty::ReFree(_), ty::ReFree(_)) => {
self.free_regions.sub_free_regions(sub_region, super_region)
}

_ => false,
}
};
let result = result || self.is_static(super_region);
debug!(
"is_subregion_of(sub_region={:?}, super_region={:?}) = {:?}",
sub_region, super_region, result
);
result
}

/// Determines whether this free region is required to be `'static`.
fn is_static(&self, super_region: ty::Region<'tcx>) -> bool {
debug!("is_static(super_region={:?})", super_region);
match *super_region {
ty::ReStatic => true,
ty::ReEarlyBound(_) | ty::ReFree(_) => {
let re_static = self.tcx.mk_region(ty::ReStatic);
self.free_regions.sub_free_regions(&re_static, &super_region)
}
_ => false,
}
}

pub fn lub_free_regions(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> Region<'tcx> {
self.free_regions.lub_free_regions(self.tcx, r_a, r_b)
}
15 changes: 11 additions & 4 deletions src/librustc/traits/coherence.rs
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@
use crate::infer::{CombinedSnapshot, InferOk};
use crate::traits::select::IntercrateAmbiguityCause;
use crate::traits::IntercrateMode;
use crate::traits::SkipLeakCheck;
use crate::traits::{self, Normalized, Obligation, ObligationCause, SelectionContext};
use crate::ty::fold::TypeFoldable;
use crate::ty::subst::Subst;
@@ -53,6 +54,7 @@ pub fn overlapping_impls<F1, F2, R>(
impl1_def_id: DefId,
impl2_def_id: DefId,
intercrate_mode: IntercrateMode,
skip_leak_check: SkipLeakCheck,
on_overlap: F1,
no_overlap: F2,
) -> R
@@ -70,7 +72,7 @@ where

let overlaps = tcx.infer_ctxt().enter(|infcx| {
let selcx = &mut SelectionContext::intercrate(&infcx, intercrate_mode);
overlap(selcx, impl1_def_id, impl2_def_id).is_some()
overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id).is_some()
});

if !overlaps {
@@ -83,7 +85,7 @@ where
tcx.infer_ctxt().enter(|infcx| {
let selcx = &mut SelectionContext::intercrate(&infcx, intercrate_mode);
selcx.enable_tracking_intercrate_ambiguity_causes();
on_overlap(overlap(selcx, impl1_def_id, impl2_def_id).unwrap())
on_overlap(overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id).unwrap())
})
}

@@ -113,12 +115,15 @@ fn with_fresh_ty_vars<'cx, 'tcx>(
/// where-clauses)? If so, returns an `ImplHeader` that unifies the two impls.
fn overlap<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
skip_leak_check: SkipLeakCheck,
a_def_id: DefId,
b_def_id: DefId,
) -> Option<OverlapResult<'tcx>> {
debug!("overlap(a_def_id={:?}, b_def_id={:?})", a_def_id, b_def_id);

selcx.infcx().probe(|snapshot| overlap_within_probe(selcx, a_def_id, b_def_id, snapshot))
selcx.infcx().probe_maybe_skip_leak_check(skip_leak_check.is_yes(), |snapshot| {
overlap_within_probe(selcx, a_def_id, b_def_id, snapshot)
})
}

fn overlap_within_probe(
@@ -146,7 +151,9 @@ fn overlap_within_probe(
.eq_impl_headers(&a_impl_header, &b_impl_header)
{
Ok(InferOk { obligations, value: () }) => obligations,
Err(_) => return None,
Err(_) => {
return None;
}
};

debug!("overlap: unification check succeeded");
22 changes: 22 additions & 0 deletions src/librustc/traits/mod.rs
Original file line number Diff line number Diff line change
@@ -83,6 +83,28 @@ pub enum IntercrateMode {
Fixed,
}

/// Whether to skip the leak check, as part of a future compatibility warning step.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum SkipLeakCheck {
Yes,
No,
}

impl SkipLeakCheck {
fn is_yes(self) -> bool {
self == SkipLeakCheck::Yes
}
}

/// The "default" for skip-leak-check corresponds to the current
/// behavior (do not skip the leak check) -- not the behavior we are
/// transitioning into.
impl Default for SkipLeakCheck {
fn default() -> Self {
SkipLeakCheck::No
}
}

/// The mode that trait queries run in.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum TraitQueryMode {
6 changes: 4 additions & 2 deletions src/librustc/traits/specialize/mod.rs
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ use crate::ty::{self, TyCtxt, TypeFoldable};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::struct_span_err;
use rustc_hir::def_id::DefId;
use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK;
use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS;
use rustc_span::DUMMY_SP;

@@ -97,7 +98,7 @@ pub fn translate_substs<'a, 'tcx>(
|_| {
bug!(
"When translating substitutions for specialization, the expected \
specialization failed to hold"
specialization failed to hold"
)
},
)
@@ -268,7 +269,7 @@ fn fulfill_implication<'a, 'tcx>(
// no dice!
debug!(
"fulfill_implication: for impls on {:?} and {:?}, \
could not fulfill: {:?} given {:?}",
could not fulfill: {:?} given {:?}",
source_trait_ref, target_trait_ref, errors, param_env.caller_bounds
);
Err(())
@@ -342,6 +343,7 @@ pub(super) fn specialization_graph_provider(
FutureCompatOverlapErrorKind::Issue33140 => {
ORDER_DEPENDENT_TRAIT_OBJECTS
}
FutureCompatOverlapErrorKind::LeakCheck => COHERENCE_LEAK_CHECK,
};
tcx.struct_span_lint_hir(
lint,
24 changes: 22 additions & 2 deletions src/librustc/traits/specialize/specialization_graph.rs
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@ pub use rustc::traits::types::specialization_graph::*;
pub enum FutureCompatOverlapErrorKind {
Issue43355,
Issue33140,
LeakCheck,
}

#[derive(Debug)]
@@ -111,6 +112,7 @@ impl<'tcx> Children {
possible_sibling,
impl_def_id,
traits::IntercrateMode::Issue43355,
traits::SkipLeakCheck::default(),
|overlap| {
if let Some(overlap_kind) =
tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling)
@@ -161,6 +163,7 @@ impl<'tcx> Children {
possible_sibling,
impl_def_id,
traits::IntercrateMode::Fixed,
traits::SkipLeakCheck::default(),
|overlap| {
last_lint = Some(FutureCompatOverlapError {
error: overlap_error(overlap),
@@ -169,6 +172,23 @@ impl<'tcx> Children {
},
|| (),
);

if last_lint.is_none() {
traits::overlapping_impls(
tcx,
possible_sibling,
impl_def_id,
traits::IntercrateMode::Fixed,
traits::SkipLeakCheck::Yes,
|overlap| {
last_lint = Some(FutureCompatOverlapError {
error: overlap_error(overlap),
kind: FutureCompatOverlapErrorKind::LeakCheck,
});
},
|| (),
);
}
}

// no overlap (error bailed already via ?)
@@ -247,7 +267,7 @@ impl<'tcx> Graph {
if trait_ref.references_error() {
debug!(
"insert: inserting dummy node for erroneous TraitRef {:?}, \
impl_def_id={:?}, trait_def_id={:?}",
impl_def_id={:?}, trait_def_id={:?}",
trait_ref, impl_def_id, trait_def_id
);

@@ -326,7 +346,7 @@ impl<'tcx> Graph {
if self.parent.insert(child, parent).is_some() {
bug!(
"When recording an impl from the crate store, information about its parent \
was already present."
was already present."
);
}

9 changes: 7 additions & 2 deletions src/librustc/ty/context.rs
Original file line number Diff line number Diff line change
@@ -173,8 +173,13 @@ pub struct CommonTypes<'tcx> {
}

pub struct CommonLifetimes<'tcx> {
pub re_empty: Region<'tcx>,
/// `ReEmpty` in the root universe.
pub re_root_empty: Region<'tcx>,

/// `ReStatic`
pub re_static: Region<'tcx>,

/// Erased region, used after type-checking
pub re_erased: Region<'tcx>,
}

@@ -876,7 +881,7 @@ impl<'tcx> CommonLifetimes<'tcx> {
let mk = |r| interners.region.intern(r, |r| Interned(interners.arena.alloc(r))).0;

CommonLifetimes {
re_empty: mk(RegionKind::ReEmpty),
re_root_empty: mk(RegionKind::ReEmpty(ty::UniverseIndex::ROOT)),
re_static: mk(RegionKind::ReStatic),
re_erased: mk(RegionKind::ReErased),
}
89 changes: 63 additions & 26 deletions src/librustc/ty/free_region_map.rs
Original file line number Diff line number Diff line change
@@ -23,11 +23,61 @@ impl<'tcx> FreeRegionMap<'tcx> {
// (with the exception that `'static: 'x` is not notable)
pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) {
debug!("relate_regions(sub={:?}, sup={:?})", sub, sup);
if is_free_or_static(sub) && is_free(sup) {
if self.is_free_or_static(sub) && self.is_free(sup) {
self.relation.add(sub, sup)
}
}

/// Tests whether `r_a <= r_b`.
///
/// Both regions must meet `is_free_or_static`.
///
/// Subtle: one tricky case that this code gets correct is as
/// follows. If we know that `r_b: 'static`, then this function
/// will return true, even though we don't know anything that
/// directly relates `r_a` and `r_b`.
///
/// Also available through the `FreeRegionRelations` trait below.
pub fn sub_free_regions(
&self,
tcx: TyCtxt<'tcx>,
r_a: Region<'tcx>,
r_b: Region<'tcx>,
) -> bool {
assert!(self.is_free_or_static(r_a) && self.is_free_or_static(r_b));
let re_static = tcx.lifetimes.re_static;
if self.check_relation(re_static, r_b) {
// `'a <= 'static` is always true, and not stored in the
// relation explicitly, so check if `'b` is `'static` (or
// equivalent to it)
true
} else {
self.check_relation(r_a, r_b)
}
}

/// Check whether `r_a <= r_b` is found in the relation.
fn check_relation(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool {
r_a == r_b || self.relation.contains(&r_a, &r_b)
}

/// True for free regions other than `'static`.
pub fn is_free(&self, r: Region<'_>) -> bool {
match *r {
ty::ReEarlyBound(_) | ty::ReFree(_) => true,
_ => false,
}
}

/// True if `r` is a free region or static of the sort that this
/// free region map can be used with.
pub fn is_free_or_static(&self, r: Region<'_>) -> bool {
match *r {
ty::ReStatic => true,
_ => self.is_free(r),
}
}

/// Computes the least-upper-bound of two free regions. In some
/// cases, this is more conservative than necessary, in order to
/// avoid making arbitrary choices. See
@@ -39,13 +89,13 @@ impl<'tcx> FreeRegionMap<'tcx> {
r_b: Region<'tcx>,
) -> Region<'tcx> {
debug!("lub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b);
assert!(is_free(r_a));
assert!(is_free(r_b));
assert!(self.is_free(r_a));
assert!(self.is_free(r_b));
let result = if r_a == r_b {
r_a
} else {
match self.relation.postdom_upper_bound(&r_a, &r_b) {
None => tcx.mk_region(ty::ReStatic),
None => tcx.lifetimes.re_static,
Some(r) => *r,
}
};
@@ -60,31 +110,18 @@ impl<'tcx> FreeRegionMap<'tcx> {
pub trait FreeRegionRelations<'tcx> {
/// Tests whether `r_a <= r_b`. Both must be free regions or
/// `'static`.
fn sub_free_regions(&self, shorter: ty::Region<'tcx>, longer: ty::Region<'tcx>) -> bool;
fn sub_free_regions(
&self,
tcx: TyCtxt<'tcx>,
shorter: ty::Region<'tcx>,
longer: ty::Region<'tcx>,
) -> bool;
}

impl<'tcx> FreeRegionRelations<'tcx> for FreeRegionMap<'tcx> {
fn sub_free_regions(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool {
assert!(is_free_or_static(r_a) && is_free_or_static(r_b));
if let ty::ReStatic = r_b {
true // `'a <= 'static` is just always true, and not stored in the relation explicitly
} else {
r_a == r_b || self.relation.contains(&r_a, &r_b)
}
}
}

fn is_free(r: Region<'_>) -> bool {
match *r {
ty::ReEarlyBound(_) | ty::ReFree(_) => true,
_ => false,
}
}

fn is_free_or_static(r: Region<'_>) -> bool {
match *r {
ty::ReStatic => true,
_ => is_free(r),
fn sub_free_regions(&self, tcx: TyCtxt<'tcx>, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool {
// invoke the "inherent method"
self.sub_free_regions(tcx, r_a, r_b)
}
}

8 changes: 6 additions & 2 deletions src/librustc/ty/print/pretty.rs
Original file line number Diff line number Diff line change
@@ -1382,7 +1382,7 @@ impl<F: fmt::Write> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx, F> {

ty::ReVar(_) | ty::ReScope(_) | ty::ReErased => false,

ty::ReStatic | ty::ReEmpty | ty::ReClosureBound(_) => true,
ty::ReStatic | ty::ReEmpty(_) | ty::ReClosureBound(_) => true,
}
}
}
@@ -1464,10 +1464,14 @@ impl<F: fmt::Write> FmtPrinter<'_, '_, F> {
p!(write("'static"));
return Ok(self);
}
ty::ReEmpty => {
ty::ReEmpty(ty::UniverseIndex::ROOT) => {
p!(write("'<empty>"));
return Ok(self);
}
ty::ReEmpty(ui) => {
p!(write("'<empty:{:?}>", ui));
return Ok(self);
}

// The user should never encounter these in unsubstituted form.
ty::ReClosureBound(vid) => {
2 changes: 1 addition & 1 deletion src/librustc/ty/structural_impls.rs
Original file line number Diff line number Diff line change
@@ -108,7 +108,7 @@ impl fmt::Debug for ty::RegionKind {

ty::RePlaceholder(placeholder) => write!(f, "RePlaceholder({:?})", placeholder),

ty::ReEmpty => write!(f, "ReEmpty"),
ty::ReEmpty(ui) => write!(f, "ReEmpty({:?})", ui),

ty::ReErased => write!(f, "ReErased"),
}
85 changes: 70 additions & 15 deletions src/librustc/ty/sty.rs
Original file line number Diff line number Diff line change
@@ -1269,11 +1269,67 @@ rustc_index::newtype_index! {

pub type Region<'tcx> = &'tcx RegionKind;

/// Representation of regions.
/// Representation of (lexical) regions. Note that the NLL checker
/// uses a distinct representation of regions. For this reason, it
/// internally replaces all the regions with inference variables --
/// the index of the variable is then used to index into internal NLL
/// data structures. See `rustc_mir::borrow_check` module for more
/// information.
///
/// Unlike types, most region variants are "fictitious", not concrete,
/// regions. Among these, `ReStatic`, `ReEmpty` and `ReScope` are the only
/// ones representing concrete regions.
/// ## The Region lattice within a given function
///
/// In general, the (lexical, and hence deprecated) region lattice
/// looks like
///
/// ```
/// static ----------+-----...------+ (greatest)
/// | | |
/// early-bound and | |
/// free regions | |
/// | | |
/// scope regions | |
/// | | |
/// empty(root) placeholder(U1) |
/// | / |
/// | / placeholder(Un)
/// empty(U1) -- /
/// | /
/// ... /
/// | /
/// empty(Un) -------- (smallest)
/// ```
///
/// Early-bound/free regions are the named lifetimes in scope from the
/// function declaration. They have relationships to one another
/// determined based on the declared relationships from the
/// function. They all collectively outlive the scope regions. (See
/// `RegionRelations` type, and particularly
/// `crate::infer::outlives::free_region_map::FreeRegionMap`.)
///
/// The scope regions are related to one another based on the AST
/// structure. (See `RegionRelations` type, and particularly the
/// `rustc::middle::region::ScopeTree`.)
///
/// Note that inference variables and bound regions are not included
/// in this diagram. In the case of inference variables, they should
/// be inferred to some other region from the diagram. In the case of
/// bound regions, they are excluded because they don't make sense to
/// include -- the diagram indicates the relationship between free
/// regions.
///
/// ## Inference variables
///
/// During region inference, we sometimes create inference variables,
/// represented as `ReVar`. These will be inferred by the code in
/// `infer::lexical_region_resolve` to some free region from the
/// lattice above (the minimal region that meets the
/// constraints).
///
/// During NLL checking, where regions are defined differently, we
/// also use `ReVar` -- in that case, the index is used to index into
/// the NLL region checker's data structures. The variable may in fact
/// represent either a free region or an inference variable, in that
/// case.
///
/// ## Bound Regions
///
@@ -1356,14 +1412,13 @@ pub enum RegionKind {
/// Should not exist after typeck.
RePlaceholder(ty::PlaceholderRegion),

/// Empty lifetime is for data that is never accessed.
/// Bottom in the region lattice. We treat ReEmpty somewhat
/// specially; at least right now, we do not generate instances of
/// it during the GLB computations, but rather
/// generate an error instead. This is to improve error messages.
/// The only way to get an instance of ReEmpty is to have a region
/// variable with no constraints.
ReEmpty,
/// Empty lifetime is for data that is never accessed. We tag the
/// empty lifetime with a universe -- the idea is that we don't
/// want `exists<'a> { forall<'b> { 'b: 'a } }` to be satisfiable.
/// Therefore, the `'empty` in a universe `U` is less than all
/// regions visible from `U`, but not less than regions not visible
/// from `U`.
ReEmpty(ty::UniverseIndex),

/// Erased region, used by trait selection, in MIR and during codegen.
ReErased,
@@ -1612,7 +1667,7 @@ impl RegionKind {
RegionKind::ReStatic => true,
RegionKind::ReVar(..) => false,
RegionKind::RePlaceholder(placeholder) => placeholder.name.is_named(),
RegionKind::ReEmpty => false,
RegionKind::ReEmpty(_) => false,
RegionKind::ReErased => false,
RegionKind::ReClosureBound(..) => false,
}
@@ -1695,7 +1750,7 @@ impl RegionKind {
flags = flags | TypeFlags::HAS_FREE_REGIONS;
flags = flags | TypeFlags::HAS_RE_EARLY_BOUND;
}
ty::ReEmpty | ty::ReStatic | ty::ReFree { .. } | ty::ReScope { .. } => {
ty::ReEmpty(_) | ty::ReStatic | ty::ReFree { .. } | ty::ReScope { .. } => {
flags = flags | TypeFlags::HAS_FREE_REGIONS;
}
ty::ReErased => {}
@@ -1705,7 +1760,7 @@ impl RegionKind {
}

match *self {
ty::ReStatic | ty::ReEmpty | ty::ReErased | ty::ReLateBound(..) => (),
ty::ReStatic | ty::ReEmpty(_) | ty::ReErased | ty::ReLateBound(..) => (),
_ => flags = flags | TypeFlags::HAS_FREE_LOCAL_NAMES,
}

8 changes: 8 additions & 0 deletions src/librustc_index/vec.rs
Original file line number Diff line number Diff line change
@@ -574,6 +574,14 @@ impl<I: Idx, T> IndexVec<I, T> {
IndexVec { raw: vec![elem; n], _marker: PhantomData }
}

/// Create an `IndexVec` with `n` elements, where the value of each
/// element is the result of `func(i)`
#[inline]
pub fn from_fn_n(func: impl FnMut(I) -> T, n: usize) -> Self {
let indices = (0..n).map(I::new);
Self::from_raw(indices.map(func).collect())
}

#[inline]
pub fn push(&mut self, d: T) -> I {
let idx = I::new(self.len());
2 changes: 1 addition & 1 deletion src/librustc_mir/borrow_check/diagnostics/region_name.rs
Original file line number Diff line number Diff line change
@@ -291,7 +291,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
| ty::ReScope(..)
| ty::ReVar(..)
| ty::RePlaceholder(..)
| ty::ReEmpty
| ty::ReEmpty(_)
| ty::ReErased
| ty::ReClosureBound(..) => None,
}
5 changes: 5 additions & 0 deletions src/librustc_mir/borrow_check/region_infer/mod.rs
Original file line number Diff line number Diff line change
@@ -1108,6 +1108,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
self.eval_if_eq(tcx, body, generic_ty, lower_bound, test_ty, verify_bound1)
}

VerifyBound::IsEmpty => {
let lower_bound_scc = self.constraint_sccs.scc(lower_bound);
self.scc_values.elements_contained_in(lower_bound_scc).next().is_none()
}

VerifyBound::OutlivedBy(r) => {
let r_vid = self.to_region_vid(r);
self.eval_outlives(r_vid, lower_bound)
Original file line number Diff line number Diff line change
@@ -160,7 +160,8 @@ impl<'a, 'b, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<'
a: ty::Region<'tcx>,
b: ty::Region<'tcx>,
) {
if let ty::ReEmpty = a {
// FIXME -- this is not the fix I would prefer
if let ty::ReEmpty(ty::UniverseIndex::ROOT) = a {
return;
}
let b = self.to_region_vid(b);
@@ -175,7 +176,8 @@ impl<'a, 'b, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<'
a: ty::Region<'tcx>,
bound: VerifyBound<'tcx>,
) {
if let ty::ReEmpty = a {
// FIXME: I'd prefer if NLL had a notion of empty
if let ty::ReEmpty(ty::UniverseIndex::ROOT) = a {
return;
}
let type_test = self.verify_to_type_test(kind, a, bound);
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ use rustc::mir::ConstraintCategory;
use rustc::traits::query::outlives_bounds::{self, OutlivesBound};
use rustc::traits::query::type_op::{self, TypeOp};
use rustc::ty::free_region_map::FreeRegionRelations;
use rustc::ty::{self, RegionVid, Ty};
use rustc::ty::{self, RegionVid, Ty, TyCtxt};
use rustc_data_structures::transitive_relation::TransitiveRelation;
use rustc_span::DUMMY_SP;
use std::rc::Rc;
@@ -333,7 +333,7 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> {
// `where Type:` is lowered to `where Type: 'empty` so that
// we check `Type` is well formed, but there's no use for
// this bound here.
if let ty::ReEmpty = r1 {
if let ty::ReEmpty(_) = r1 {
return;
}

@@ -359,7 +359,12 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> {
/// over the `FreeRegionMap` from lexical regions and
/// `UniversalRegions` (from NLL)`.
impl<'tcx> FreeRegionRelations<'tcx> for UniversalRegionRelations<'tcx> {
fn sub_free_regions(&self, shorter: ty::Region<'tcx>, longer: ty::Region<'tcx>) -> bool {
fn sub_free_regions(
&self,
_tcx: TyCtxt<'tcx>,
shorter: ty::Region<'tcx>,
longer: ty::Region<'tcx>,
) -> bool {
let shorter = shorter.to_region_vid();
assert!(self.universal_regions.is_universal_region(shorter));
let longer = longer.to_region_vid();
11 changes: 11 additions & 0 deletions src/librustc_session/lint/builtin.rs
Original file line number Diff line number Diff line change
@@ -260,6 +260,16 @@ declare_lint! {
};
}

declare_lint! {
pub COHERENCE_LEAK_CHECK,
Warn,
"distinct impls distinguished only by the leak-check code",
@future_incompatible = FutureIncompatibleInfo {
reference: "issue #56105 <https://github.com/rust-lang/rust/issues/56105>",
edition: None,
};
}

declare_lint! {
pub DEPRECATED,
Warn,
@@ -509,6 +519,7 @@ declare_lint_pass! {
MISSING_FRAGMENT_SPECIFIER,
LATE_BOUND_LIFETIME_ARGUMENTS,
ORDER_DEPENDENT_TRAIT_OBJECTS,
COHERENCE_LEAK_CHECK,
DEPRECATED,
UNUSED_UNSAFE,
UNUSED_MUT,
8 changes: 5 additions & 3 deletions src/librustc_traits/chalk_context/resolvent_ops.rs
Original file line number Diff line number Diff line change
@@ -246,9 +246,11 @@ impl TypeRelation<'tcx> for AnswerSubstitutor<'cx, 'tcx> {
assert_eq!(a_bound.assert_bound_var(), b_bound.assert_bound_var());
}

(ty::ReStatic, ty::ReStatic)
| (ty::ReErased, ty::ReErased)
| (ty::ReEmpty, ty::ReEmpty) => (),
(ty::ReStatic, ty::ReStatic) | (ty::ReErased, ty::ReErased) => (),

(ty::ReEmpty(a_ui), ty::ReEmpty(b_ui)) => {
assert_eq!(a_ui, b_ui);
}

(&ty::ReFree(a_free), &ty::ReFree(b_free)) => {
assert_eq!(a_free, b_free);
5 changes: 4 additions & 1 deletion src/librustc_typeck/coherence/inherent_impls_overlap.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::namespace::Namespace;
use rustc::traits::{self, IntercrateMode};
use rustc::traits::{self, IntercrateMode, SkipLeakCheck};
use rustc::ty::TyCtxt;
use rustc_errors::struct_span_err;
use rustc_hir as hir;
@@ -76,6 +76,9 @@ impl InherentOverlapChecker<'tcx> {
impl1_def_id,
impl2_def_id,
IntercrateMode::Issue43355,
// We go ahead and just skip the leak check for
// inherent impls without warning.
SkipLeakCheck::Yes,
|overlap| {
self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap);
false
3 changes: 2 additions & 1 deletion src/librustc_typeck/collect.rs
Original file line number Diff line number Diff line change
@@ -2322,7 +2322,8 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
// compiler/tooling bugs from not handling WF predicates.
} else {
let span = bound_pred.bounded_ty.span;
let predicate = ty::OutlivesPredicate(ty, tcx.mk_region(ty::ReEmpty));
let re_root_empty = tcx.lifetimes.re_root_empty;
let predicate = ty::OutlivesPredicate(ty, re_root_empty);
predicates.push((
ty::Predicate::TypeOutlives(ty::Binder::dummy(predicate)),
span,
2 changes: 1 addition & 1 deletion src/librustc_typeck/outlives/utils.rs
Original file line number Diff line number Diff line change
@@ -166,7 +166,7 @@ fn is_free_region(tcx: TyCtxt<'_>, region: Region<'_>) -> bool {
//
// struct Bar<T>(<Self as Foo>::Type) where Self: ;
// struct Baz<'a>(&'a Self) where Self: ;
RegionKind::ReEmpty => false,
RegionKind::ReEmpty(_) => false,

// These regions don't appear in types from type declarations:
RegionKind::ReErased
2 changes: 1 addition & 1 deletion src/librustc_typeck/variance/constraints.rs
Original file line number Diff line number Diff line change
@@ -453,7 +453,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
| ty::ReScope(..)
| ty::ReVar(..)
| ty::RePlaceholder(..)
| ty::ReEmpty
| ty::ReEmpty(_)
| ty::ReErased => {
// We don't expect to see anything but 'static or bound
// regions when visiting member types or method types.
6 changes: 3 additions & 3 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
@@ -447,7 +447,7 @@ impl Clean<Option<Lifetime>> for ty::RegionKind {
| ty::ReScope(..)
| ty::ReVar(..)
| ty::RePlaceholder(..)
| ty::ReEmpty
| ty::ReEmpty(_)
| ty::ReClosureBound(_)
| ty::ReErased => {
debug!("cannot clean region {:?}", self);
@@ -521,7 +521,7 @@ impl<'tcx> Clean<Option<WherePredicate>>
let ty::OutlivesPredicate(ref a, ref b) = *self;

match (a, b) {
(ty::ReEmpty, ty::ReEmpty) => {
(ty::ReEmpty(_), ty::ReEmpty(_)) => {
return None;
}
_ => {}
@@ -539,7 +539,7 @@ impl<'tcx> Clean<Option<WherePredicate>> for ty::OutlivesPredicate<Ty<'tcx>, ty:
let ty::OutlivesPredicate(ref ty, ref lt) = *self;

match lt {
ty::ReEmpty => return None,
ty::ReEmpty(_) => return None,
_ => {}
}

14 changes: 14 additions & 0 deletions src/test/ui/coherence/coherence-inherited-subtyping.old.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error[E0592]: duplicate definitions with name `method1`
--> $DIR/coherence-inherited-subtyping.rs:14:5
|
LL | fn method1(&self) {}
| ^^^^^^^^^^^^^^^^^^^^ duplicate definitions for `method1`
...
LL | fn method1(&self) {}
| -------------------- other definition for `method1`
|
= note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details

error: aborting due to previous error

For more information about this error, try `rustc --explain E0592`.
14 changes: 14 additions & 0 deletions src/test/ui/coherence/coherence-inherited-subtyping.re.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error[E0592]: duplicate definitions with name `method1`
--> $DIR/coherence-inherited-subtyping.rs:14:5
|
LL | fn method1(&self) {}
| ^^^^^^^^^^^^^^^^^^^^ duplicate definitions for `method1`
...
LL | fn method1(&self) {}
| -------------------- other definition for `method1`
|
= note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details

error: aborting due to previous error

For more information about this error, try `rustc --explain E0592`.
21 changes: 21 additions & 0 deletions src/test/ui/coherence/coherence-inherited-subtyping.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Test that two distinct impls which match subtypes of one another
// yield coherence errors (or not) depending on the variance.
//
// Note: This scenario is currently accepted, but as part of the
// universe transition (#56105) may eventually become an error.

// revisions: old re

struct Foo<T> {
t: T,
}

impl Foo<for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8> {
fn method1(&self) {} //~ ERROR duplicate definitions with name `method1`
}

impl Foo<for<'a> fn(&'a u8, &'a u8) -> &'a u8> {
fn method1(&self) {}
}

fn main() {}
14 changes: 14 additions & 0 deletions src/test/ui/coherence/coherence-subtyping.old.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
warning: conflicting implementations of trait `TheTrait` for type `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`:
--> $DIR/coherence-subtyping.rs:16:1
|
LL | impl TheTrait for for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8 {}
| ---------------------------------------------------------- first implementation here
LL |
LL | impl TheTrait for for<'a> fn(&'a u8, &'a u8) -> &'a u8 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`
|
= note: `#[warn(coherence_leak_check)]` on by default
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #56105 <https://github.com/rust-lang/rust/issues/56105>
= note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details

14 changes: 14 additions & 0 deletions src/test/ui/coherence/coherence-subtyping.re.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
warning: conflicting implementations of trait `TheTrait` for type `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`:
--> $DIR/coherence-subtyping.rs:16:1
|
LL | impl TheTrait for for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8 {}
| ---------------------------------------------------------- first implementation here
LL |
LL | impl TheTrait for for<'a> fn(&'a u8, &'a u8) -> &'a u8 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`
|
= note: `#[warn(coherence_leak_check)]` on by default
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #56105 <https://github.com/rust-lang/rust/issues/56105>
= note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details

13 changes: 8 additions & 5 deletions src/test/ui/coherence/coherence-subtyping.rs
Original file line number Diff line number Diff line change
@@ -5,16 +5,19 @@
// universe transition (#56105) may eventually become an error.

// revisions: old re
// build-pass (FIXME(62277): could be check-pass?)
// check-pass

trait TheTrait {
fn foo(&self) { }
fn foo(&self) {}
}

impl TheTrait for for<'a,'b> fn(&'a u8, &'b u8) -> &'a u8 {
}
impl TheTrait for for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8 {}

impl TheTrait for for<'a> fn(&'a u8, &'a u8) -> &'a u8 {
//[re]~^ WARNING conflicting implementation
//[re]~^^ WARNING this was previously accepted by the compiler but is being phased out
//[old]~^^^ WARNING conflicting implementation
//[old]~^^^^ WARNING this was previously accepted by the compiler but is being phased out
}

fn main() { }
fn main() {}
Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ where
T: Anything<'b, 'c>,
{
with_signature(cell, t, |cell, t| require(cell, t));
//~^ ERROR associated type `<T as Anything<'_#5r, '_#6r>>::AssocType` may not live long enough
//~^ ERROR may not live long enough
}

#[rustc_regions]
@@ -46,7 +46,7 @@ where
'a: 'a,
{
with_signature(cell, t, |cell, t| require(cell, t));
//~^ ERROR associated type `<T as Anything<'_#6r, '_#7r>>::AssocType` may not live long enough
//~^ ERROR may not live long enough
}

#[rustc_regions]